switchroom 0.19.11 → 0.19.13

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.
@@ -29063,6 +29063,9 @@ function isSubstantiveFinalReply(input) {
29063
29063
  return true;
29064
29064
  return false;
29065
29065
  }
29066
+ function shouldJournalReplySiteDelivery(input) {
29067
+ return isSubstantiveFinalReply(input);
29068
+ }
29066
29069
  var FINAL_ANSWER_MIN_CHARS2 = 200;
29067
29070
 
29068
29071
  // history.ts
@@ -29088,8 +29091,8 @@ __export(exports_history, {
29088
29091
  _resetForTests: () => _resetForTests,
29089
29092
  MIN_PREFIX_MATCH_CHARS: () => MIN_PREFIX_MATCH_CHARS2
29090
29093
  });
29091
- import { chmodSync as chmodSync9, existsSync as existsSync25, mkdirSync as mkdirSync26 } from "fs";
29092
- import { join as join29 } from "path";
29094
+ import { chmodSync as chmodSync9, existsSync as existsSync26, mkdirSync as mkdirSync27 } from "fs";
29095
+ import { join as join30 } from "path";
29093
29096
  function loadDatabaseClass2() {
29094
29097
  if (DatabaseClass2 != null)
29095
29098
  return DatabaseClass2;
@@ -29120,8 +29123,8 @@ function initHistory2(stateDir, retentionDays = 30) {
29120
29123
  if (db2 != null)
29121
29124
  return;
29122
29125
  const Database = loadDatabaseClass2();
29123
- mkdirSync26(stateDir, { recursive: true, mode: 448 });
29124
- const path2 = join29(stateDir, "history.db");
29126
+ mkdirSync27(stateDir, { recursive: true, mode: 448 });
29127
+ const path2 = join30(stateDir, "history.db");
29125
29128
  dbPath2 = path2;
29126
29129
  db2 = new Database(path2, { create: true });
29127
29130
  db2.exec("PRAGMA journal_mode = WAL");
@@ -29187,7 +29190,7 @@ function initHistory2(stateDir, retentionDays = 30) {
29187
29190
  }
29188
29191
  for (const suffix of ["", "-shm", "-wal"]) {
29189
29192
  const f = path2 + suffix;
29190
- if (existsSync25(f)) {
29193
+ if (existsSync26(f)) {
29191
29194
  try {
29192
29195
  chmodSync9(f, 420);
29193
29196
  } catch {}
@@ -29239,7 +29242,7 @@ function checkpointWal2() {
29239
29242
  if (dbPath2) {
29240
29243
  for (const suffix of ["-shm", "-wal"]) {
29241
29244
  const f = dbPath2 + suffix;
29242
- if (existsSync25(f)) {
29245
+ if (existsSync26(f)) {
29243
29246
  try {
29244
29247
  chmodSync9(f, 420);
29245
29248
  } catch {}
@@ -29486,7 +29489,7 @@ var FLUSH_SUPPRESSION_WINDOW_MS = 2000;
29486
29489
  var init_turn_flush_suppression = () => {};
29487
29490
 
29488
29491
  // ../src/util/atomic.ts
29489
- import { closeSync as closeSync6, constants as constants2, fchmodSync, fchownSync, fsyncSync as fsyncSync2, openSync as openSync6, renameSync as renameSync11, rmSync as rmSync5, writeSync as writeSync4 } from "node:fs";
29492
+ import { closeSync as closeSync6, constants as constants2, fchmodSync, fchownSync, fsyncSync as fsyncSync2, openSync as openSync6, renameSync as renameSync12, rmSync as rmSync5, writeSync as writeSync4 } from "node:fs";
29490
29493
  var TMP_OPEN_FLAGS;
29491
29494
  var init_atomic = __esm(() => {
29492
29495
  TMP_OPEN_FLAGS = constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0);
@@ -32448,7 +32451,7 @@ var require_util = __commonJS((exports2) => {
32448
32451
  return path2;
32449
32452
  }
32450
32453
  exports2.normalize = normalize;
32451
- function join37(aRoot, aPath) {
32454
+ function join38(aRoot, aPath) {
32452
32455
  if (aRoot === "") {
32453
32456
  aRoot = ".";
32454
32457
  }
@@ -32480,7 +32483,7 @@ var require_util = __commonJS((exports2) => {
32480
32483
  }
32481
32484
  return joined;
32482
32485
  }
32483
- exports2.join = join37;
32486
+ exports2.join = join38;
32484
32487
  exports2.isAbsolute = function(aPath) {
32485
32488
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
32486
32489
  };
@@ -32653,7 +32656,7 @@ var require_util = __commonJS((exports2) => {
32653
32656
  parsed.path = parsed.path.substring(0, index2 + 1);
32654
32657
  }
32655
32658
  }
32656
- sourceURL = join37(urlGenerate(parsed), sourceURL);
32659
+ sourceURL = join38(urlGenerate(parsed), sourceURL);
32657
32660
  }
32658
32661
  return normalize(sourceURL);
32659
32662
  }
@@ -35258,19 +35261,19 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
35258
35261
  }
35259
35262
 
35260
35263
  // gateway/quota-cache.ts
35261
- import { existsSync as existsSync41, readFileSync as readFileSync42, writeFileSync as writeFileSync35, mkdirSync as mkdirSync36 } from "fs";
35262
- import { join as join46, dirname as dirname15 } from "path";
35264
+ import { existsSync as existsSync42, readFileSync as readFileSync43, writeFileSync as writeFileSync36, mkdirSync as mkdirSync37 } from "fs";
35265
+ import { join as join47, dirname as dirname15 } from "path";
35263
35266
  function defaultCachePath() {
35264
- return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join46(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35267
+ return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join47(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35265
35268
  }
35266
35269
  function readQuotaCache(opts = {}) {
35267
35270
  const path2 = opts.path ?? defaultCachePath();
35268
35271
  const now = opts.now ?? Date.now();
35269
- if (!existsSync41(path2))
35272
+ if (!existsSync42(path2))
35270
35273
  return null;
35271
35274
  let entry;
35272
35275
  try {
35273
- entry = JSON.parse(readFileSync42(path2, "utf8"));
35276
+ entry = JSON.parse(readFileSync43(path2, "utf8"));
35274
35277
  } catch {
35275
35278
  return null;
35276
35279
  }
@@ -35296,8 +35299,8 @@ function writeQuotaCache(result, opts = {}) {
35296
35299
  result
35297
35300
  };
35298
35301
  try {
35299
- mkdirSync36(dirname15(path2), { recursive: true });
35300
- writeFileSync35(path2, JSON.stringify(entry, null, 2), { mode: 384 });
35302
+ mkdirSync37(dirname15(path2), { recursive: true });
35303
+ writeFileSync36(path2, JSON.stringify(entry, null, 2), { mode: 384 });
35301
35304
  } catch {}
35302
35305
  }
35303
35306
  var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
@@ -35307,8 +35310,8 @@ var init_quota_cache = __esm(() => {
35307
35310
  });
35308
35311
 
35309
35312
  // gateway/boot-probes.ts
35310
- import { readFileSync as readFileSync43, readdirSync as readdirSync9, existsSync as existsSync42 } from "fs";
35311
- import { join as join47 } from "path";
35313
+ import { readFileSync as readFileSync44, readdirSync as readdirSync10, existsSync as existsSync43 } from "fs";
35314
+ import { join as join48 } from "path";
35312
35315
  import { execFile as execFileCb } from "child_process";
35313
35316
  import { promisify } from "util";
35314
35317
  async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
@@ -35350,11 +35353,11 @@ function mapPlan(billingType, hasExtra) {
35350
35353
  }
35351
35354
  async function probeAccount(agentDir) {
35352
35355
  return withTimeout("Account", (async () => {
35353
- const claudeDir = join47(agentDir, ".claude");
35354
- const claudeJsonPath = join47(claudeDir, ".claude.json");
35356
+ const claudeDir = join48(agentDir, ".claude");
35357
+ const claudeJsonPath = join48(claudeDir, ".claude.json");
35355
35358
  let cfg = {};
35356
35359
  try {
35357
- const raw = readFileSync43(claudeJsonPath, "utf8");
35360
+ const raw = readFileSync44(claudeJsonPath, "utf8");
35358
35361
  cfg = JSON.parse(raw);
35359
35362
  } catch {
35360
35363
  return { status: "fail", label: "Account", detail: "no .claude.json" };
@@ -35372,12 +35375,12 @@ async function probeAccount(agentDir) {
35372
35375
  let tokenStr = "";
35373
35376
  let status = "ok";
35374
35377
  for (const candidate of [
35375
- join47(claudeDir, ".oauth-token.meta.json"),
35376
- join47(claudeDir, "accounts", "default", ".oauth-token.meta.json")
35378
+ join48(claudeDir, ".oauth-token.meta.json"),
35379
+ join48(claudeDir, "accounts", "default", ".oauth-token.meta.json")
35377
35380
  ]) {
35378
- if (existsSync42(candidate)) {
35381
+ if (existsSync43(candidate)) {
35379
35382
  try {
35380
- const meta = JSON.parse(readFileSync43(candidate, "utf8"));
35383
+ const meta = JSON.parse(readFileSync44(candidate, "utf8"));
35381
35384
  if (meta.expiresAt) {
35382
35385
  tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
35383
35386
  const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
@@ -35552,9 +35555,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
35552
35555
  if (!cgroup)
35553
35556
  return null;
35554
35557
  const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
35555
- if (!existsSync42(procsPath))
35558
+ if (!existsSync43(procsPath))
35556
35559
  return null;
35557
- const pidsRaw = readFileSync43(procsPath, "utf-8");
35560
+ const pidsRaw = readFileSync44(procsPath, "utf-8");
35558
35561
  const pids = pidsRaw.split(`
35559
35562
  `).map((s) => s.trim()).filter(Boolean);
35560
35563
  if (pids.length === 0)
@@ -35567,7 +35570,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
35567
35570
  let rss = 0;
35568
35571
  let comm = "";
35569
35572
  try {
35570
- const status = readFileSync43(`/proc/${pid}/status`, "utf-8");
35573
+ const status = readFileSync44(`/proc/${pid}/status`, "utf-8");
35571
35574
  const rssLine = status.split(`
35572
35575
  `).find((l) => l.startsWith("VmRSS:"));
35573
35576
  if (rssLine) {
@@ -35579,7 +35582,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
35579
35582
  continue;
35580
35583
  }
35581
35584
  try {
35582
- comm = readFileSync43(`/proc/${pid}/comm`, "utf-8").trim();
35585
+ comm = readFileSync44(`/proc/${pid}/comm`, "utf-8").trim();
35583
35586
  } catch {}
35584
35587
  candidates.push({ pid, rss, comm });
35585
35588
  }
@@ -35759,9 +35762,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
35759
35762
  let claudeDirForProbe = null;
35760
35763
  for (const candidate of [
35761
35764
  claudeConfigDir,
35762
- join47(claudeConfigDir, "accounts", "default")
35765
+ join48(claudeConfigDir, "accounts", "default")
35763
35766
  ]) {
35764
- if (existsSync42(join47(candidate, ".oauth-token"))) {
35767
+ if (existsSync43(join48(candidate, ".oauth-token"))) {
35765
35768
  claudeDirForProbe = candidate;
35766
35769
  break;
35767
35770
  }
@@ -35826,7 +35829,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
35826
35829
  }
35827
35830
  function readContainerBootTimeMsForProbe() {
35828
35831
  try {
35829
- const stat1 = readFileSync43("/proc/1/stat", "utf8");
35832
+ const stat1 = readFileSync44("/proc/1/stat", "utf8");
35830
35833
  const lastParen = stat1.lastIndexOf(")");
35831
35834
  if (lastParen < 0)
35832
35835
  return null;
@@ -35834,7 +35837,7 @@ function readContainerBootTimeMsForProbe() {
35834
35837
  const starttimeTicks = Number(after[19]);
35835
35838
  if (!Number.isFinite(starttimeTicks))
35836
35839
  return null;
35837
- const procStat = readFileSync43("/proc/stat", "utf8");
35840
+ const procStat = readFileSync44("/proc/stat", "utf8");
35838
35841
  const btimeLine = procStat.split(`
35839
35842
  `).find((l) => l.startsWith("btime "));
35840
35843
  if (!btimeLine)
@@ -35932,7 +35935,7 @@ async function probeUds(label, socketPath, opts = {}) {
35932
35935
  }
35933
35936
  return withTimeout(label, (async () => {
35934
35937
  if (!opts.connectImpl) {
35935
- if (!existsSync42(socketPath)) {
35938
+ if (!existsSync43(socketPath)) {
35936
35939
  return {
35937
35940
  status: "fail",
35938
35941
  label,
@@ -35996,7 +35999,7 @@ async function probeSkills(agentDir, opts = {}) {
35996
35999
  return withTimeout("Skills", (async () => {
35997
36000
  const fs2 = opts.fs ?? realSkillsFs;
35998
36001
  const max = opts.maxNamesShown ?? 3;
35999
- const skillsDir = join47(agentDir, ".claude", "skills");
36002
+ const skillsDir = join48(agentDir, ".claude", "skills");
36000
36003
  if (!fs2.exists(skillsDir)) {
36001
36004
  return { status: "ok", label: "Skills", detail: "no skills dir" };
36002
36005
  }
@@ -36011,17 +36014,17 @@ async function probeSkills(agentDir, opts = {}) {
36011
36014
  }
36012
36015
  const dangling = [];
36013
36016
  for (const name of entries) {
36014
- const skillPath = join47(skillsDir, name);
36017
+ const skillPath = join48(skillsDir, name);
36015
36018
  if (!fs2.exists(skillPath)) {
36016
36019
  dangling.push(name);
36017
36020
  continue;
36018
36021
  }
36019
- const skillMd = join47(skillPath, "SKILL.md");
36022
+ const skillMd = join48(skillPath, "SKILL.md");
36020
36023
  if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
36021
36024
  continue;
36022
36025
  }
36023
36026
  }
36024
- const overlayDir = opts.overlaySkillsDir ?? join47(agentDir, "skills.d");
36027
+ const overlayDir = opts.overlaySkillsDir ?? join48(agentDir, "skills.d");
36025
36028
  const overlaySlugs = new Set;
36026
36029
  if (fs2.exists(overlayDir)) {
36027
36030
  let overlayEntries = [];
@@ -36063,8 +36066,8 @@ function renderBucketedSkills(switchroom, agent) {
36063
36066
  }
36064
36067
  async function probeConnections(agentDir, opts = {}) {
36065
36068
  return withTimeout("Connections", (async () => {
36066
- const path2 = join47(agentDir, ".claude", "connection-health.json");
36067
- const read = opts.readFileImpl ?? ((p) => readFileSync43(p, "utf8"));
36069
+ const path2 = join48(agentDir, ".claude", "connection-health.json");
36070
+ const read = opts.readFileImpl ?? ((p) => readFileSync44(p, "utf8"));
36068
36071
  let issues = [];
36069
36072
  try {
36070
36073
  const parsed = JSON.parse(read(path2));
@@ -36094,25 +36097,25 @@ var init_boot_probes = __esm(() => {
36094
36097
  init_quota_check();
36095
36098
  execFile = promisify(execFileCb);
36096
36099
  realProcFs = {
36097
- readdir: (p) => readdirSync9(p),
36098
- readFile: (p) => readFileSync43(p, "utf-8")
36100
+ readdir: (p) => readdirSync10(p),
36101
+ readFile: (p) => readFileSync44(p, "utf-8")
36099
36102
  };
36100
36103
  realSchedulerFs = {
36101
- readFile: (p) => readFileSync43(p, "utf-8"),
36104
+ readFile: (p) => readFileSync44(p, "utf-8"),
36102
36105
  mtimeMs: (p) => {
36103
- const { statSync: statSync13 } = __require("fs");
36104
- return statSync13(p).mtimeMs;
36106
+ const { statSync: statSync14 } = __require("fs");
36107
+ return statSync14(p).mtimeMs;
36105
36108
  },
36106
- exists: (p) => existsSync42(p)
36109
+ exists: (p) => existsSync43(p)
36107
36110
  };
36108
36111
  realSkillsFs = {
36109
- readdir: (p) => readdirSync9(p),
36110
- exists: (p) => existsSync42(p)
36112
+ readdir: (p) => readdirSync10(p),
36113
+ exists: (p) => existsSync43(p)
36111
36114
  };
36112
36115
  });
36113
36116
 
36114
36117
  // gateway/boot-issue-cache.ts
36115
- import { existsSync as existsSync43, readFileSync as readFileSync44, writeFileSync as writeFileSync36, mkdirSync as mkdirSync37, renameSync as renameSync17 } from "fs";
36118
+ import { existsSync as existsSync44, readFileSync as readFileSync45, writeFileSync as writeFileSync37, mkdirSync as mkdirSync38, renameSync as renameSync18 } from "fs";
36116
36119
  import { dirname as dirname16 } from "path";
36117
36120
  function fingerprintProbe(key, r) {
36118
36121
  if (r.status === "ok")
@@ -36192,11 +36195,11 @@ function diffProbes(probes, cache, opts = {}) {
36192
36195
  return out;
36193
36196
  }
36194
36197
  function loadCache(path2, now = Date.now) {
36195
- if (!existsSync43(path2))
36198
+ if (!existsSync44(path2))
36196
36199
  return { ...EMPTY_CACHE, probes: {} };
36197
36200
  let raw;
36198
36201
  try {
36199
- raw = readFileSync44(path2, "utf-8");
36202
+ raw = readFileSync45(path2, "utf-8");
36200
36203
  } catch {
36201
36204
  return { ...EMPTY_CACHE, probes: {} };
36202
36205
  }
@@ -36205,7 +36208,7 @@ function loadCache(path2, now = Date.now) {
36205
36208
  parsed = JSON.parse(raw);
36206
36209
  } catch {
36207
36210
  try {
36208
- renameSync17(path2, `${path2}.corrupt-${now()}`);
36211
+ renameSync18(path2, `${path2}.corrupt-${now()}`);
36209
36212
  } catch {}
36210
36213
  return { ...EMPTY_CACHE, probes: {} };
36211
36214
  }
@@ -36239,10 +36242,10 @@ function applyAndSave(path2, cache, diff) {
36239
36242
  }
36240
36243
  }
36241
36244
  try {
36242
- mkdirSync37(dirname16(path2), { recursive: true });
36245
+ mkdirSync38(dirname16(path2), { recursive: true });
36243
36246
  const tmp = `${path2}.tmp`;
36244
- writeFileSync36(tmp, JSON.stringify(next), { mode: 384 });
36245
- renameSync17(tmp, path2);
36247
+ writeFileSync37(tmp, JSON.stringify(next), { mode: 384 });
36248
+ renameSync18(tmp, path2);
36246
36249
  } catch {}
36247
36250
  return next;
36248
36251
  }
@@ -36254,14 +36257,14 @@ var init_boot_issue_cache = __esm(() => {
36254
36257
  });
36255
36258
 
36256
36259
  // gateway/config-snapshot.ts
36257
- import { createHash as createHash3 } from "crypto";
36258
- import { existsSync as existsSync44, readFileSync as readFileSync45, writeFileSync as writeFileSync37, mkdirSync as mkdirSync38, renameSync as renameSync18 } from "fs";
36260
+ import { createHash as createHash4 } from "crypto";
36261
+ import { existsSync as existsSync45, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync39, renameSync as renameSync19 } from "fs";
36259
36262
  import { dirname as dirname17 } from "path";
36260
36263
  function hashStringArray(items) {
36261
36264
  if (!items || items.length === 0)
36262
36265
  return null;
36263
36266
  const sorted = [...items].sort();
36264
- const raw = createHash3("sha256").update(sorted.join("\x00")).digest("hex");
36267
+ const raw = createHash4("sha256").update(sorted.join("\x00")).digest("hex");
36265
36268
  return raw.slice(0, 12);
36266
36269
  }
36267
36270
  function normalizeModel(model) {
@@ -36320,11 +36323,11 @@ function renderConfigChangeDim(dim) {
36320
36323
  }
36321
36324
  }
36322
36325
  function loadSnapshot(path2, now = Date.now) {
36323
- if (!existsSync44(path2))
36326
+ if (!existsSync45(path2))
36324
36327
  return null;
36325
36328
  let raw;
36326
36329
  try {
36327
- raw = readFileSync45(path2, "utf-8");
36330
+ raw = readFileSync46(path2, "utf-8");
36328
36331
  } catch {
36329
36332
  return null;
36330
36333
  }
@@ -36333,7 +36336,7 @@ function loadSnapshot(path2, now = Date.now) {
36333
36336
  parsed = JSON.parse(raw);
36334
36337
  } catch {
36335
36338
  try {
36336
- renameSync18(path2, `${path2}.corrupt-${now()}`);
36339
+ renameSync19(path2, `${path2}.corrupt-${now()}`);
36337
36340
  } catch {}
36338
36341
  return null;
36339
36342
  }
@@ -36354,10 +36357,10 @@ function loadSnapshot(path2, now = Date.now) {
36354
36357
  }
36355
36358
  function persistSnapshot(path2, snapshot) {
36356
36359
  try {
36357
- mkdirSync38(dirname17(path2), { recursive: true });
36360
+ mkdirSync39(dirname17(path2), { recursive: true });
36358
36361
  const tmp = `${path2}.tmp`;
36359
- writeFileSync37(tmp, JSON.stringify(snapshot), { mode: 384 });
36360
- renameSync18(tmp, path2);
36362
+ writeFileSync38(tmp, JSON.stringify(snapshot), { mode: 384 });
36363
+ renameSync19(tmp, path2);
36361
36364
  } catch {}
36362
36365
  }
36363
36366
  var init_config_snapshot = __esm(() => {
@@ -36365,13 +36368,13 @@ var init_config_snapshot = __esm(() => {
36365
36368
  });
36366
36369
 
36367
36370
  // gateway/boot-card-msgid.ts
36368
- import { readFileSync as readFileSync46, writeFileSync as writeFileSync38 } from "node:fs";
36371
+ import { readFileSync as readFileSync47, writeFileSync as writeFileSync39 } from "node:fs";
36369
36372
  function bootCardChatKey(chatId, threadId) {
36370
36373
  return `${chatId}:${threadId ?? ""}`;
36371
36374
  }
36372
36375
  function readStore(path2) {
36373
36376
  try {
36374
- const parsed = JSON.parse(readFileSync46(path2, "utf8"));
36377
+ const parsed = JSON.parse(readFileSync47(path2, "utf8"));
36375
36378
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
36376
36379
  return parsed;
36377
36380
  }
@@ -36390,7 +36393,7 @@ function saveBootCardMsgId(path2, chatKey3, messageId) {
36390
36393
  if (store2[chatKey3] === messageId)
36391
36394
  return;
36392
36395
  store2[chatKey3] = messageId;
36393
- writeFileSync38(path2, JSON.stringify(store2), "utf8");
36396
+ writeFileSync39(path2, JSON.stringify(store2), "utf8");
36394
36397
  } catch {}
36395
36398
  }
36396
36399
  var init_boot_card_msgid = () => {};
@@ -36408,8 +36411,8 @@ __export(exports_boot_card, {
36408
36411
  readRoutingMode: () => readRoutingMode,
36409
36412
  containerBootStartMs: () => containerBootStartMs
36410
36413
  });
36411
- import { join as join48 } from "path";
36412
- import { readFileSync as readFileSync47, statSync as statSync13 } from "fs";
36414
+ import { join as join49 } from "path";
36415
+ import { readFileSync as readFileSync48, statSync as statSync14 } from "fs";
36413
36416
  function resolvePersonaName(slug, loadConfig3) {
36414
36417
  try {
36415
36418
  const config = loadConfig3 ? loadConfig3() : loadConfig();
@@ -36431,7 +36434,7 @@ function shouldSkipDuplicateBootCard(gate, site) {
36431
36434
  }
36432
36435
  return { skip: false };
36433
36436
  }
36434
- function containerBootStartMs(fsImpl = { readFileSync: readFileSync47 }) {
36437
+ function containerBootStartMs(fsImpl = { readFileSync: readFileSync48 }) {
36435
36438
  try {
36436
36439
  const btimeLine = fsImpl.readFileSync("/proc/stat", "utf-8").split(`
36437
36440
  `).find((l) => l.startsWith("btime "));
@@ -36452,8 +36455,8 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync47 }) {
36452
36455
  return null;
36453
36456
  }
36454
36457
  }
36455
- function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync47, statSync: statSync13 }) {
36456
- const path2 = join48(agentDir, ".routing-mode");
36458
+ function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync48, statSync: statSync14 }) {
36459
+ const path2 = join49(agentDir, ".routing-mode");
36457
36460
  try {
36458
36461
  const raw = fsImpl.readFileSync(path2, "utf-8");
36459
36462
  const line = raw.split(`
@@ -36560,7 +36563,7 @@ function renderBootCard(opts) {
36560
36563
  return stackCardLines(flatLines);
36561
36564
  }
36562
36565
  async function runAllProbes(opts) {
36563
- const claudeDir = join48(opts.agentDir, ".claude");
36566
+ const claudeDir = join49(opts.agentDir, ".claude");
36564
36567
  const probes = {};
36565
36568
  const slug = opts.agentSlug ?? opts.agentName;
36566
36569
  await Promise.allSettled([
@@ -37414,7 +37417,7 @@ __export(exports_tmux2, {
37414
37417
  captureAgentPane: () => captureAgentPane2
37415
37418
  });
37416
37419
  import { execFileSync as execFileSync8 } from "node:child_process";
37417
- import { chmodSync as chmodSync13, mkdirSync as mkdirSync46, readdirSync as readdirSync12, statSync as statSync18, unlinkSync as unlinkSync25, writeFileSync as writeFileSync47 } from "node:fs";
37420
+ import { chmodSync as chmodSync13, mkdirSync as mkdirSync47, readdirSync as readdirSync13, statSync as statSync19, unlinkSync as unlinkSync26, writeFileSync as writeFileSync48 } from "node:fs";
37418
37421
  import { resolve as resolve12 } from "node:path";
37419
37422
  function captureAgentPane2(opts) {
37420
37423
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -37426,7 +37429,7 @@ function captureAgentPane2(opts) {
37426
37429
  const reasonSlug = sanitizeReason2(reason);
37427
37430
  const outPath = resolve12(outDir, `${ts}-${reasonSlug}.txt`);
37428
37431
  try {
37429
- mkdirSync46(outDir, { recursive: true, mode: 448 });
37432
+ mkdirSync47(outDir, { recursive: true, mode: 448 });
37430
37433
  } catch (err) {
37431
37434
  const msg = `mkdir crash-reports failed: ${err.message}`;
37432
37435
  console.error(`[tmux-capture] ${agentName3}: ${msg}`);
@@ -37465,7 +37468,7 @@ function captureAgentPane2(opts) {
37465
37468
  ` + `
37466
37469
  `;
37467
37470
  try {
37468
- writeFileSync47(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
37471
+ writeFileSync48(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
37469
37472
  mode: 384
37470
37473
  });
37471
37474
  } catch (err) {
@@ -37535,7 +37538,7 @@ function sleepSync3(ms) {
37535
37538
  function pruneOldReports2(dir, retain) {
37536
37539
  let entries;
37537
37540
  try {
37538
- entries = readdirSync12(dir);
37541
+ entries = readdirSync13(dir);
37539
37542
  } catch {
37540
37543
  return;
37541
37544
  }
@@ -37543,13 +37546,13 @@ function pruneOldReports2(dir, retain) {
37543
37546
  const full = resolve12(dir, n);
37544
37547
  let mtimeMs = 0;
37545
37548
  try {
37546
- mtimeMs = statSync18(full).mtimeMs;
37549
+ mtimeMs = statSync19(full).mtimeMs;
37547
37550
  } catch {}
37548
37551
  return { full, mtimeMs };
37549
37552
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
37550
37553
  for (const stale of files.slice(retain)) {
37551
37554
  try {
37552
- unlinkSync25(stale.full);
37555
+ unlinkSync26(stale.full);
37553
37556
  } catch {}
37554
37557
  }
37555
37558
  }
@@ -38286,7 +38289,7 @@ function buildGrantedKeyboard(scope) {
38286
38289
  const btn = scopeToOpenInDriveButton(scope);
38287
38290
  if (btn === null)
38288
38291
  return;
38289
- return new import_grammy14.InlineKeyboard().url(btn.text, btn.url);
38292
+ return new import_grammy15.InlineKeyboard().url(btn.text, btn.url);
38290
38293
  }
38291
38294
  async function handleApprovalCallback(ctx, data) {
38292
38295
  const parsed = parseApprovalCallback(data);
@@ -38332,11 +38335,11 @@ async function handleApprovalCallback(ctx, data) {
38332
38335
  } catch {}
38333
38336
  await ctx.answerCallbackQuery({ text: granted ? "Approved" : "Denied" });
38334
38337
  }
38335
- var import_grammy14;
38338
+ var import_grammy15;
38336
38339
  var init_approval_callback = __esm(() => {
38337
38340
  init_approval_card();
38338
38341
  init_client3();
38339
- import_grammy14 = __toESM(require_mod2(), 1);
38342
+ import_grammy15 = __toESM(require_mod2(), 1);
38340
38343
  });
38341
38344
 
38342
38345
  // ../src/telegram/materialize-bot-token.ts
@@ -38345,7 +38348,7 @@ __export(exports_materialize_bot_token, {
38345
38348
  materializeBotToken: () => materializeBotToken,
38346
38349
  BotTokenMaterializeError: () => BotTokenMaterializeError
38347
38350
  });
38348
- import { existsSync as existsSync53 } from "node:fs";
38351
+ import { existsSync as existsSync54 } from "node:fs";
38349
38352
  function pickConfiguredToken(config, agentName3) {
38350
38353
  if (agentName3) {
38351
38354
  const agent = config.agents?.[agentName3];
@@ -38359,7 +38362,7 @@ function tryDirectVaultRead4(ref, config, passphrase) {
38359
38362
  if (!passphrase)
38360
38363
  return null;
38361
38364
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
38362
- if (!existsSync53(vaultPath))
38365
+ if (!existsSync54(vaultPath))
38363
38366
  return null;
38364
38367
  try {
38365
38368
  const secrets = openVault(passphrase, vaultPath);
@@ -38509,28 +38512,28 @@ var init_approvals_commands = __esm(() => {
38509
38512
  });
38510
38513
 
38511
38514
  // gateway/gateway.ts
38512
- var import_grammy15 = __toESM(require_mod(), 1);
38515
+ var import_grammy16 = __toESM(require_mod(), 1);
38513
38516
  var import_runner3 = __toESM(require_mod3(), 1);
38514
- import { randomBytes as randomBytes10, createHash as createHash4 } from "crypto";
38517
+ import { randomBytes as randomBytes10, createHash as createHash5 } from "crypto";
38515
38518
  import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
38516
38519
  import {
38517
- readFileSync as readFileSync58,
38518
- writeFileSync as writeFileSync48,
38519
- mkdirSync as mkdirSync47,
38520
- readdirSync as readdirSync13,
38520
+ readFileSync as readFileSync59,
38521
+ writeFileSync as writeFileSync49,
38522
+ mkdirSync as mkdirSync48,
38523
+ readdirSync as readdirSync14,
38521
38524
  rmSync as rmSync6,
38522
- statSync as statSync19,
38523
- renameSync as renameSync21,
38525
+ statSync as statSync20,
38526
+ renameSync as renameSync22,
38524
38527
  realpathSync as realpathSync4,
38525
38528
  chmodSync as chmodSync14,
38526
38529
  openSync as openSync12,
38527
38530
  closeSync as closeSync12,
38528
- existsSync as existsSync54,
38529
- unlinkSync as unlinkSync26,
38530
- appendFileSync as appendFileSync7
38531
+ existsSync as existsSync55,
38532
+ unlinkSync as unlinkSync27,
38533
+ appendFileSync as appendFileSync8
38531
38534
  } from "fs";
38532
- import { homedir as homedir19 } from "os";
38533
- import { join as join60, sep as sep4, basename as basename15 } from "path";
38535
+ import { homedir as homedir20 } from "os";
38536
+ import { join as join61, sep as sep4, basename as basename15 } from "path";
38534
38537
 
38535
38538
  // plugin-logger.ts
38536
38539
  import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
@@ -41573,7 +41576,16 @@ import { AsyncLocalStorage } from "async_hooks";
41573
41576
 
41574
41577
  // retry-api-call.ts
41575
41578
  var import_grammy2 = __toESM(require_mod2(), 1);
41579
+ function isLocalResourceError(err) {
41580
+ const code = err?.code;
41581
+ if (typeof code === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code)) {
41582
+ return true;
41583
+ }
41584
+ const msg = err instanceof Error ? err.message : String(err ?? "");
41585
+ return /\b(ENOSPC|EDQUOT|EIO|ENOMEM)\b/.test(msg) || /no space left on device/i.test(msg) || /disk quota exceeded/i.test(msg);
41586
+ }
41576
41587
  var LOCAL_RESOURCE_EXHAUSTED = "LOCAL_RESOURCE_EXHAUSTED";
41588
+ var GIVE_UP_MESSAGE = "retryApiCall: max retries exceeded";
41577
41589
  var FLOOD_WAIT_ACTIVE = "FLOOD_WAIT_ACTIVE";
41578
41590
  function makeFloodWaitActiveError(retryAfterSec, untilTs, original) {
41579
41591
  return Object.assign(new Error(FLOOD_WAIT_ACTIVE), {
@@ -41587,6 +41599,99 @@ function makeFloodWaitActiveError(retryAfterSec, untilTs, original) {
41587
41599
  function isFloodWaitActiveError(err) {
41588
41600
  return err instanceof Error && err.message === FLOOD_WAIT_ACTIVE;
41589
41601
  }
41602
+ var DEFAULT_MAX_FLOOD_SLEEP_MS = 120000;
41603
+ var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
41604
+ function createRetryApiCall(config = {}) {
41605
+ const maxRetries = config.maxRetries ?? 3;
41606
+ const maxFloodSleepMs = config.maxFloodSleepMs ?? DEFAULT_MAX_FLOOD_SLEEP_MS;
41607
+ const sleep = config.sleep ?? DEFAULT_SLEEP;
41608
+ const observer = config.observer;
41609
+ const log = config.log;
41610
+ const onFloodWait = config.onFloodWait;
41611
+ const floodWaitRemainingMs = config.floodWaitRemainingMs;
41612
+ function openWindowMs() {
41613
+ if (!floodWaitRemainingMs)
41614
+ return 0;
41615
+ try {
41616
+ const ms = floodWaitRemainingMs();
41617
+ return Number.isFinite(ms) && ms > 0 ? ms : 0;
41618
+ } catch {
41619
+ return 0;
41620
+ }
41621
+ }
41622
+ return async function retryApiCall(fn, opts) {
41623
+ for (let attempt = 0;attempt < maxRetries; attempt++) {
41624
+ const remaining = openWindowMs();
41625
+ if (remaining > maxFloodSleepMs) {
41626
+ const retryAfterSec = Math.ceil(remaining / 1000);
41627
+ log?.(`telegram gateway: flood window still open for ${retryAfterSec}s \u2014 ` + `not issuing the ${opts?.verb ?? "api-call"} (would feed the ban)
41628
+ `);
41629
+ const gated = makeFloodWaitActiveError(retryAfterSec, Date.now() + remaining, null);
41630
+ observer?.onGiveUp?.({ attempts: attempt + 1, error: gated });
41631
+ throw gated;
41632
+ }
41633
+ try {
41634
+ return await fn();
41635
+ } catch (err) {
41636
+ const isGrammyErr = err instanceof import_grammy2.GrammyError;
41637
+ const msg = err instanceof Error ? err.message : String(err);
41638
+ const desc = isGrammyErr ? err.description : msg;
41639
+ if (isLocalResourceError(err)) {
41640
+ log?.(`telegram gateway: LOCAL resource exhaustion (${err.code ?? "disk/mem"}) \u2014 ` + `not retrying the send (would feed a flood ban); surfacing degraded state
41641
+ `);
41642
+ observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
41643
+ throw Object.assign(new Error(LOCAL_RESOURCE_EXHAUSTED), { original: err });
41644
+ }
41645
+ if (isGrammyErr && err.error_code === 429) {
41646
+ const retryAfter = Number(err.parameters?.retry_after ?? 5);
41647
+ const delayMs = retryAfter * 1000;
41648
+ try {
41649
+ onFloodWait?.(retryAfter, opts);
41650
+ } catch {}
41651
+ if (delayMs > maxFloodSleepMs) {
41652
+ log?.(`telegram gateway: 429 flood ban of ${retryAfter}s exceeds the ` + `${Math.round(maxFloodSleepMs / 1000)}s in-process sleep ceiling \u2014 ` + `not sleeping it; surfacing degraded state
41653
+ `);
41654
+ observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
41655
+ throw makeFloodWaitActiveError(retryAfter, Date.now() + delayMs, err);
41656
+ }
41657
+ log?.(`telegram gateway: 429 rate limited, waiting ${retryAfter}s
41658
+ `);
41659
+ observer?.onRetry?.({ attempt, reason: "flood_wait", delayMs });
41660
+ await sleep(delayMs);
41661
+ continue;
41662
+ }
41663
+ if (isGrammyErr && err.error_code === 400 && desc.includes("not modified")) {
41664
+ observer?.onBenign?.({ kind: "not_modified" });
41665
+ return;
41666
+ }
41667
+ if (isGrammyErr && err.error_code === 400 && (desc.includes("message to edit not found") || desc.includes("message to delete not found"))) {
41668
+ observer?.onBenign?.({
41669
+ kind: desc.includes("edit") ? "message_not_found" : "delete_not_found"
41670
+ });
41671
+ return;
41672
+ }
41673
+ if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
41674
+ throw Object.assign(new Error("THREAD_NOT_FOUND"), { original: err });
41675
+ }
41676
+ if (!isGrammyErr && (msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("fetch failed") || msg.includes("ENOTFOUND"))) {
41677
+ if (attempt < maxRetries - 1) {
41678
+ const delayMs = Math.pow(2, attempt) * 1000;
41679
+ log?.(`telegram gateway: network error, retrying in ${delayMs / 1000}s: ${msg}
41680
+ `);
41681
+ observer?.onRetry?.({ attempt, reason: "network", delayMs });
41682
+ await sleep(delayMs);
41683
+ continue;
41684
+ }
41685
+ }
41686
+ observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
41687
+ throw err;
41688
+ }
41689
+ }
41690
+ const giveUpErr = new Error(GIVE_UP_MESSAGE);
41691
+ observer?.onGiveUp?.({ attempts: maxRetries, error: giveUpErr });
41692
+ throw giveUpErr;
41693
+ };
41694
+ }
41590
41695
  async function retryWithThreadFallback(retry, send, opts) {
41591
41696
  try {
41592
41697
  return await retry(() => send(opts.threadId), {
@@ -61646,7 +61751,7 @@ function createChatLock() {
61646
61751
 
61647
61752
  // retry-api-call.ts
61648
61753
  var import_grammy6 = __toESM(require_mod2(), 1);
61649
- function isLocalResourceError(err) {
61754
+ function isLocalResourceError2(err) {
61650
61755
  const code2 = err?.code;
61651
61756
  if (typeof code2 === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code2)) {
61652
61757
  return true;
@@ -61655,7 +61760,7 @@ function isLocalResourceError(err) {
61655
61760
  return /\b(ENOSPC|EDQUOT|EIO|ENOMEM)\b/.test(msg) || /no space left on device/i.test(msg) || /disk quota exceeded/i.test(msg);
61656
61761
  }
61657
61762
  var LOCAL_RESOURCE_EXHAUSTED2 = "LOCAL_RESOURCE_EXHAUSTED";
61658
- var GIVE_UP_MESSAGE = "retryApiCall: max retries exceeded";
61763
+ var GIVE_UP_MESSAGE2 = "retryApiCall: max retries exceeded";
61659
61764
  var FLOOD_WAIT_ACTIVE2 = "FLOOD_WAIT_ACTIVE";
61660
61765
  function makeFloodWaitActiveError2(retryAfterSec, untilTs, original) {
61661
61766
  return Object.assign(new Error(FLOOD_WAIT_ACTIVE2), {
@@ -61669,12 +61774,12 @@ function makeFloodWaitActiveError2(retryAfterSec, untilTs, original) {
61669
61774
  function isFloodWaitActiveError2(err) {
61670
61775
  return err instanceof Error && err.message === FLOOD_WAIT_ACTIVE2;
61671
61776
  }
61672
- var DEFAULT_MAX_FLOOD_SLEEP_MS = 120000;
61673
- var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
61777
+ var DEFAULT_MAX_FLOOD_SLEEP_MS2 = 120000;
61778
+ var DEFAULT_SLEEP2 = (ms) => new Promise((r) => setTimeout(r, ms));
61674
61779
  function createRetryApiCall2(config = {}) {
61675
61780
  const maxRetries = config.maxRetries ?? 3;
61676
- const maxFloodSleepMs = config.maxFloodSleepMs ?? DEFAULT_MAX_FLOOD_SLEEP_MS;
61677
- const sleep = config.sleep ?? DEFAULT_SLEEP;
61781
+ const maxFloodSleepMs = config.maxFloodSleepMs ?? DEFAULT_MAX_FLOOD_SLEEP_MS2;
61782
+ const sleep = config.sleep ?? DEFAULT_SLEEP2;
61678
61783
  const observer = config.observer;
61679
61784
  const log = config.log;
61680
61785
  const onFloodWait = config.onFloodWait;
@@ -61706,7 +61811,7 @@ function createRetryApiCall2(config = {}) {
61706
61811
  const isGrammyErr = err instanceof import_grammy6.GrammyError;
61707
61812
  const msg = err instanceof Error ? err.message : String(err);
61708
61813
  const desc = isGrammyErr ? err.description : msg;
61709
- if (isLocalResourceError(err)) {
61814
+ if (isLocalResourceError2(err)) {
61710
61815
  log?.(`telegram gateway: LOCAL resource exhaustion (${err.code ?? "disk/mem"}) \u2014 ` + `not retrying the send (would feed a flood ban); surfacing degraded state
61711
61816
  `);
61712
61817
  observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
@@ -61757,7 +61862,7 @@ function createRetryApiCall2(config = {}) {
61757
61862
  throw err;
61758
61863
  }
61759
61864
  }
61760
- const giveUpErr = new Error(GIVE_UP_MESSAGE);
61865
+ const giveUpErr = new Error(GIVE_UP_MESSAGE2);
61761
61866
  observer?.onGiveUp?.({ attempts: maxRetries, error: giveUpErr });
61762
61867
  throw giveUpErr;
61763
61868
  };
@@ -69023,7 +69128,7 @@ var STARTUP_RETRY_DELAYS_MS = [
69023
69128
  32000,
69024
69129
  64000
69025
69130
  ];
69026
- var DEFAULT_SLEEP2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
69131
+ var DEFAULT_SLEEP3 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
69027
69132
  function classifyStartupError(err) {
69028
69133
  if (!(err instanceof Error))
69029
69134
  return "other";
@@ -69043,7 +69148,7 @@ function classifyStartupError(err) {
69043
69148
  }
69044
69149
  async function gatewayStartupRetry(fn, opts = {}) {
69045
69150
  const delays = opts.delaysMs ?? STARTUP_RETRY_DELAYS_MS;
69046
- const sleep = opts.sleep ?? DEFAULT_SLEEP2;
69151
+ const sleep = opts.sleep ?? DEFAULT_SLEEP3;
69047
69152
  const onExhausted = opts.onExhausted ?? ((err) => {
69048
69153
  process.stderr.write(`telegram gateway: startup failed after ${delays.length + 1} attempts \u2014 exiting so systemd can restart: ${err}
69049
69154
  `);
@@ -73012,16 +73117,6 @@ function validateStringArray(field, value, tag = "gateway") {
73012
73117
 
73013
73118
  // format.ts
73014
73119
  var RICH_MESSAGE_MAX_CHARS2 = 32768;
73015
- function repairEscapedWhitespace2(text4) {
73016
- if (!/\\[nrt"\\]/.test(text4))
73017
- return text4;
73018
- const nonce = Math.random().toString(36).slice(2);
73019
- const BACKSLASH_PH = `\x00BK${nonce}_`;
73020
- const { masked, restore: restore2 } = maskCodeRegions2(text4, nonce);
73021
- const unescaped = masked.replace(/\\\\/g, BACKSLASH_PH).replace(/\\n/g, `
73022
- `).replace(/\\r/g, "\r").replace(/\\t/g, "\t").replace(/\\"/g, '"').replace(new RegExp(BACKSLASH_PH.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), "\\");
73023
- return restore2(unescaped);
73024
- }
73025
73120
  function maskCodeRegions2(text4, nonce) {
73026
73121
  const FENCE_MASK_PH = `\x00RMF${nonce}_`;
73027
73122
  const INLINE_MASK_PH = `\x00RMI${nonce}_`;
@@ -73041,55 +73136,6 @@ function maskCodeRegions2(text4, nonce) {
73041
73136
  const stripPlaceholders = (s) => s.replace(anyMaskRe, "");
73042
73137
  return { masked, restore: restore2, placeholder: FENCE_MASK_PH, stripPlaceholders };
73043
73138
  }
73044
- function splitCollapsedInlineBullets2(text4) {
73045
- if (!/[\u2022\u00b7]/.test(text4))
73046
- return text4;
73047
- const interiorSep = /[ \t]+([\u2022\u00b7])[ \t]+/g;
73048
- return text4.split(`
73049
- `).map((line) => {
73050
- if (!/^[ \t]*[\u2022\u00b7*-] /.test(line))
73051
- return line;
73052
- if (!/[ \t][\u2022\u00b7][ \t]/.test(line))
73053
- return line;
73054
- return line.replace(interiorSep, `
73055
- $1 `);
73056
- }).join(`
73057
- `);
73058
- }
73059
- function normalizeParagraphBreaks2(text4) {
73060
- if (!text4.includes(`
73061
- `) && !/[\u2022\u00b7]/.test(text4))
73062
- return text4;
73063
- const nonce = Math.random().toString(36).slice(2);
73064
- const { masked: maskedRaw, restore: restore2, placeholder } = maskCodeRegions2(text4, nonce);
73065
- const masked = splitCollapsedInlineBullets2(maskedRaw);
73066
- let out = masked.replace(/\n[ \t\r]+\n(?:[ \t\r]*\n)*/g, `
73067
-
73068
- `).replace(/\n{3,}/g, `
73069
-
73070
- `);
73071
- const lines = out.split(`
73072
- `);
73073
- const pieces = [];
73074
- for (let i = 0;i < lines.length; i++) {
73075
- let line = lines[i];
73076
- const isLast = i === lines.length - 1;
73077
- const next = isLast ? "" : lines[i + 1];
73078
- const promote = !isLast && line.trim() !== "" && next.trim() !== "" && shouldPromoteBreak2(line, next, placeholder);
73079
- if (promote) {
73080
- line = line.replace(/[ \t\r]+$/, "");
73081
- }
73082
- pieces.push(line);
73083
- if (isLast)
73084
- break;
73085
- pieces.push(promote ? `
73086
- ` : `
73087
- `);
73088
- }
73089
- out = pieces.join("");
73090
- out = ensureBlockBoundaries2(out, placeholder);
73091
- return restore2(out);
73092
- }
73093
73139
  function hardenCardBreaks2(text4) {
73094
73140
  if (!text4.includes(`
73095
73141
  `))
@@ -73200,85 +73246,6 @@ function addParagraphSpacers2(text4) {
73200
73246
  return restore2(out.join(`
73201
73247
  `));
73202
73248
  }
73203
- function normalizePunctuation2(text4) {
73204
- if (!/[\u2014\u2013\u2022\u00b7]/.test(text4))
73205
- return text4;
73206
- const nonce = Math.random().toString(36).slice(2);
73207
- const { masked, restore: restore2 } = maskCodeRegions2(text4, nonce);
73208
- const linkMasks = [];
73209
- const LINK_MASK_PH = `\x00RML${nonce}_`;
73210
- const maskedLinks = masked.replace(/(\]\()([^)\n]*)(\))/g, (_m, open, href, close) => {
73211
- const idx = linkMasks.length;
73212
- linkMasks.push(href);
73213
- return `${open}${LINK_MASK_PH}${idx}\x00${close}`;
73214
- });
73215
- const maskedAutolinks = maskedLinks.replace(/(<)([a-zA-Z][a-zA-Z0-9+.-]*:[^>\s]*)(>)/g, (_m, lt, uri, gt) => {
73216
- const idx = linkMasks.length;
73217
- linkMasks.push(uri);
73218
- return `${lt}${LINK_MASK_PH}${idx}\x00${gt}`;
73219
- });
73220
- const escNonce = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
73221
- const linkRestoreRe = new RegExp(`\x00RML${escNonce}_(\\d+)\x00`, "g");
73222
- const restoreLinks = (s) => s.replace(linkRestoreRe, (_m, idx) => linkMasks[Number(idx)] ?? _m);
73223
- let out = maskedAutolinks.replace(/(\S)[ \t][\u2014\u2013][ \t](?=(\S))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2014(?=(\w))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2013(?=\w)/g, "$1-");
73224
- out = restoreLinks(out);
73225
- out = out.split(`
73226
- `).map((line) => line.replace(/^([ \t]*)[\u2022\u00b7][ \t]+/, "$1- ")).join(`
73227
- `);
73228
- return restore2(out);
73229
- }
73230
- function listItemContent2(line) {
73231
- return line.trimStart().replace(/^(?:[-*+]|\d+[.)])\s+/, "");
73232
- }
73233
- function isFullyBolded2(fragment) {
73234
- return /^\*\*[^*]+\*\*[.,:;!?]?$/.test(fragment.trim());
73235
- }
73236
- function unbold2(fragment) {
73237
- return fragment.replace(/\*\*([^*]+)\*\*/g, "$1");
73238
- }
73239
- function stripExcessBold2(text4) {
73240
- if (!text4.includes("**"))
73241
- return text4;
73242
- const nonce = Math.random().toString(36).slice(2);
73243
- const { masked, restore: restore2, placeholder, stripPlaceholders } = maskCodeRegions2(text4, nonce);
73244
- const visible = stripPlaceholders(masked);
73245
- if (visible.length < 100)
73246
- return restore2(masked);
73247
- let boldChars = 0;
73248
- for (const m of visible.matchAll(/\*\*([^*]+)\*\*/g))
73249
- boldChars += m[1].length;
73250
- if (boldChars / visible.length > 0.3) {
73251
- return restore2(unbold2(masked));
73252
- }
73253
- const blocks = masked.split(/\n{2,}/);
73254
- const rebuilt = blocks.map((block) => {
73255
- const lines = block.split(`
73256
- `).filter((l) => l.trim() !== "");
73257
- if (lines.length === 0 || !block.includes("**"))
73258
- return block;
73259
- const listLines = lines.filter((l) => isListItemLine2(l));
73260
- if (listLines.length >= 2 && listLines.length === lines.length) {
73261
- if (lines.every((l) => isFullyBolded2(listItemContent2(l))))
73262
- return unbold2(block);
73263
- return block;
73264
- }
73265
- const isProseBlock = lines.every((l) => !isMarkerLine2(l, placeholder));
73266
- if (!isProseBlock)
73267
- return block;
73268
- if (!lines.every((l) => isFullyBolded2(l)))
73269
- return block;
73270
- if (lines.length === 1 && lines[0].trim().length <= 48)
73271
- return block;
73272
- return unbold2(block);
73273
- });
73274
- const seps = masked.match(/\n{2,}/g) ?? [];
73275
- let out = rebuilt[0] ?? "";
73276
- for (let i = 1;i < rebuilt.length; i++)
73277
- out += (seps[i - 1] ?? `
73278
-
73279
- `) + rebuilt[i];
73280
- return restore2(out);
73281
- }
73282
73249
  function isListItemLine2(line) {
73283
73250
  const t = line.trimStart();
73284
73251
  return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t);
@@ -73301,82 +73268,6 @@ function isBlockquoteLine2(line) {
73301
73268
  function isHeadingLine2(line) {
73302
73269
  return /^#{1,6}\s/.test(line.trimStart());
73303
73270
  }
73304
- function ensureBlockBoundaries2(text4, placeholder) {
73305
- if (!text4.includes(`
73306
- `))
73307
- return text4;
73308
- const lines = text4.split(`
73309
- `);
73310
- const result = [];
73311
- for (let i = 0;i < lines.length; i++) {
73312
- const line = lines[i];
73313
- const prev = result.length > 0 ? result[result.length - 1] : null;
73314
- const prevNonBlank = prev != null && prev.trim() !== "";
73315
- const curBlank = line.trim() === "";
73316
- if (prevNonBlank && !curBlank) {
73317
- const next = i + 1 < lines.length ? lines[i + 1] : "";
73318
- const prevIsTable = isTableRowLine2(prev);
73319
- const startsTableHere = !prevIsTable && (line.includes("|") && isTableDelimiterLine2(next) || isTableRowLine2(line) && isTableDelimiterLine2(next));
73320
- const startsFence = isFenceOpenLine2(line, placeholder) && !isFenceOpenLine2(prev, placeholder);
73321
- const startsQuote = isBlockquoteLine2(line) && !isBlockquoteLine2(prev);
73322
- const startsHeading = isHeadingLine2(line) && !isHeadingLine2(prev);
73323
- const startsList = isListItemLine2(line) && !isListItemLine2(prev);
73324
- if (startsTableHere || startsFence || startsQuote || startsHeading || startsList) {
73325
- result.push("");
73326
- }
73327
- }
73328
- if (prevNonBlank && !curBlank && isFenceOpenLine2(prev, placeholder)) {
73329
- const alreadySeparated = result.length > 0 && result[result.length - 1].trim() === "";
73330
- const curIsBlockStart = isFenceOpenLine2(line, placeholder) || isBlockquoteLine2(line) || isHeadingLine2(line) || isTableRowLine2(line) || isTableDelimiterLine2(line);
73331
- if (!alreadySeparated && !curIsBlockStart) {
73332
- result.push("");
73333
- }
73334
- }
73335
- if (prevNonBlank && !curBlank && isListItemLine2(prev) && !isListItemLine2(line)) {
73336
- const isIndentedContinuation = /^(\t| {4,})\S/.test(line);
73337
- const alreadySeparated = result.length > 0 && result[result.length - 1].trim() === "";
73338
- if (!isIndentedContinuation && !alreadySeparated) {
73339
- result.push("");
73340
- }
73341
- }
73342
- result.push(line);
73343
- }
73344
- return result.join(`
73345
- `);
73346
- }
73347
- function isMarkerLine2(line, placeholder) {
73348
- if (/^ {4,}\S/.test(line))
73349
- return true;
73350
- const t = line.trimStart();
73351
- if (placeholder != null && placeholder.length > 0 && t.startsWith(placeholder))
73352
- return true;
73353
- return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t) || t.startsWith(">") || /^#{1,6}\s/.test(t) || isTableRowLine2(line) || isTableDelimiterLine2(line) || t.startsWith("```") || /^(-{3,}|\*{3,}|_{3,})\s*$/.test(t);
73354
- }
73355
- function shouldPromoteBreak2(prev, next, placeholder) {
73356
- if (isMarkerLine2(prev, placeholder) || isMarkerLine2(next, placeholder))
73357
- return false;
73358
- const nextTrimmed = next.trimStart();
73359
- if (nextTrimmed.length === 0)
73360
- return false;
73361
- const prevTrimmed = prev.trimEnd();
73362
- const unwrapped = prevTrimmed.replace(/[)"'\u2019\u201d\]]+$/, "");
73363
- const terminator = unwrapped.slice(-1);
73364
- if (terminator === "." || terminator === "!" || terminator === "?" || terminator === ":") {
73365
- return true;
73366
- }
73367
- const statStripped = prevTrimmed.replace(/[*_`]/g, "");
73368
- const colonIdx = statStripped.indexOf(":");
73369
- if (colonIdx > 0) {
73370
- const label = statStripped.slice(0, colonIdx).trim();
73371
- const value = statStripped.slice(colonIdx + 1);
73372
- const labelWords = label.length === 0 ? 0 : label.split(/\s+/).length;
73373
- const isStatLine = /^[ \t]+\S/.test(value) && labelWords >= 1 && labelWords <= 3 && label.length <= 24;
73374
- if (isStatLine && !/^[a-z]/.test(nextTrimmed)) {
73375
- return true;
73376
- }
73377
- }
73378
- return false;
73379
- }
73380
73271
  function hardSliceToCap2(text4, cap = RICH_MESSAGE_MAX_CHARS2) {
73381
73272
  if (cap <= 0)
73382
73273
  return [text4];
@@ -73666,121 +73557,221 @@ function decideRedeliver(input) {
73666
73557
  return { redeliver: true, framedText: frameRedelivery(text4) };
73667
73558
  }
73668
73559
 
73669
- // text-voice-scrub.ts
73670
- var NULL4 = "\x00";
73671
- var FENCE_PH2 = `${NULL4}VS_FENCE`;
73672
- var INLINE_PH4 = `${NULL4}VS_INLINE`;
73673
- var HTML_CODE_PH2 = `${NULL4}VS_HTMLCODE`;
73674
- var HTML_PRE_PH2 = `${NULL4}VS_HTMLPRE`;
73675
- var URL_PH2 = `${NULL4}VS_URL`;
73676
- var URL_RE3 = /https?:\/\/\S+/g;
73677
- function enabled4() {
73678
- const v = process.env.SWITCHROOM_DISABLE_VOICE_SCRUB;
73679
- return !(v === "1" || v === "true");
73560
+ // gateway/outbound-send-path.ts
73561
+ init_format();
73562
+ init_text_voice_scrub();
73563
+
73564
+ // temporal-normalize.ts
73565
+ function maskTemporalRegions(text4) {
73566
+ const nonce = Math.random().toString(36).slice(2);
73567
+ const PH = `\x00TN${nonce}_`;
73568
+ const masks = [];
73569
+ const push2 = (s) => {
73570
+ masks.push(s);
73571
+ return `${PH}${masks.length - 1}\x00`;
73572
+ };
73573
+ const masked = text4.replace(/```[\s\S]*?```/g, (m) => push2(m)).replace(/`[^`\n]+`/g, (m) => push2(m)).replace(/(\]\()([^)\n]*)(\))/g, (_m, open, href, close) => `${open}${push2(href)}${close}`).replace(/(<)([a-zA-Z][a-zA-Z0-9+.-]*:[^>\s]*)(>)/g, (_m, lt, uri, gt) => `${lt}${push2(uri)}${gt}`);
73574
+ const esc = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
73575
+ const re = new RegExp(`\x00TN${esc}_(\\d+)\x00`, "g");
73576
+ const restore2 = (s) => s.replace(re, (_m, idx) => masks[Number(idx)] ?? _m);
73577
+ return { masked, restore: restore2 };
73578
+ }
73579
+ function isBlockquoteLine3(line) {
73580
+ return line.trimStart().startsWith(">");
73680
73581
  }
73681
- function openerStripEnabled2() {
73682
- const v = process.env.SWITCHROOM_VOICE_STRIP_OPENERS;
73683
- return v === "1" || v === "true";
73582
+ function perNonQuoteLine(masked, fn) {
73583
+ return masked.split(`
73584
+ `).map((line) => isBlockquoteLine3(line) ? line : fn(line)).join(`
73585
+ `);
73684
73586
  }
73685
- var LEADING_AFFIRMATION_RE2 = /^(\s*)(you(?:['\u2019]| a)re absolutely right|you(?:['\u2019]| a)re so right|you(?:['\u2019]| a)re absolutely correct|absolutely right|exactly right|great catch|good catch|nice catch|spot on)\b(?:\s*$|\s*[!.,;:\u2014\u2013-][\s!.,;:\u2014\u2013-]*)/i;
73686
- function stripLeadingAffirmation2(text4) {
73687
- const m = LEADING_AFFIRMATION_RE2.exec(text4);
73688
- if (!m)
73689
- return { out: text4, count: 0 };
73690
- const leadingWs = m[1] ?? "";
73691
- const rest = text4.slice(m[0].length);
73692
- if (rest.trim().length === 0)
73693
- return { out: text4, count: 0 };
73694
- const recapped = rest.replace(/^(\s*)([a-z])/, (_m, ws, ch) => ws + ch.toUpperCase());
73695
- return { out: leadingWs + recapped, count: 1 };
73587
+ function renderLocalDate(ms, tz) {
73588
+ const parts = new Intl.DateTimeFormat("en-US", {
73589
+ timeZone: tz,
73590
+ weekday: "short",
73591
+ day: "numeric",
73592
+ month: "short"
73593
+ }).formatToParts(new Date(ms));
73594
+ const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
73595
+ return `${get("weekday")} ${get("day")} ${get("month")}`;
73696
73596
  }
73697
- function park2(text4) {
73698
- const parts = [];
73699
- let parked = text4;
73700
- parked = parked.replace(/```[\s\S]*?```/g, (m) => {
73701
- const idx = parts.length;
73702
- parts.push({ prefix: FENCE_PH2, idx, raw: m });
73703
- return `${FENCE_PH2}${idx}${NULL4}`;
73704
- });
73705
- parked = parked.replace(/<pre>[\s\S]*?<\/pre>/gi, (m) => {
73706
- const idx = parts.length;
73707
- parts.push({ prefix: HTML_PRE_PH2, idx, raw: m });
73708
- return `${HTML_PRE_PH2}${idx}${NULL4}`;
73709
- });
73710
- parked = parked.replace(/<code[^>]*>[\s\S]*?<\/code>/gi, (m) => {
73711
- const idx = parts.length;
73712
- parts.push({ prefix: HTML_CODE_PH2, idx, raw: m });
73713
- return `${HTML_CODE_PH2}${idx}${NULL4}`;
73714
- });
73715
- parked = parked.replace(/`[^`\n]+`/g, (m) => {
73716
- const idx = parts.length;
73717
- parts.push({ prefix: INLINE_PH4, idx, raw: m });
73718
- return `${INLINE_PH4}${idx}${NULL4}`;
73719
- });
73720
- parked = parked.replace(URL_RE3, (m) => {
73721
- const idx = parts.length;
73722
- parts.push({ prefix: URL_PH2, idx, raw: m });
73723
- return `${URL_PH2}${idx}${NULL4}`;
73724
- });
73725
- return { parked, parts };
73597
+ function renderLocalClock(ms, tz) {
73598
+ return new Intl.DateTimeFormat("en-US", {
73599
+ timeZone: tz,
73600
+ hour: "numeric",
73601
+ minute: "2-digit",
73602
+ hour12: true
73603
+ }).format(new Date(ms)).replace(/\s([AP])M$/, (_m, p) => ` ${p.toLowerCase()}m`);
73726
73604
  }
73727
- function restore2(text4, parts) {
73728
- let restored = text4;
73729
- for (let i = parts.length - 1;i >= 0; i--) {
73730
- const p = parts[i];
73731
- restored = restored.replace(`${p.prefix}${p.idx}${NULL4}`, () => p.raw);
73732
- }
73733
- return restored;
73605
+ function renderLocalDatetime(ms, tz) {
73606
+ return `${renderLocalDate(ms, tz)} ${renderLocalClock(ms, tz)} ${tzAbbrev(ms, tz)}`;
73734
73607
  }
73735
- function replaceDashes2(text4) {
73736
- let replaced = 0;
73737
- let out = text4;
73738
- const capFirst = (after) => {
73739
- if (/^[a-z][A-Z]/.test(after))
73740
- return after;
73741
- return after.replace(/^([a-z])/, (_m, ch) => ch.toUpperCase());
73742
- };
73743
- out = out.replace(/(\S) [\u2014\u2013] (\S)(\S?)/g, (m, before, after, peek) => {
73744
- if (/\d/.test(before) && /\d/.test(after))
73608
+ var ISO_UTC_RE = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|\+00:00)/g;
73609
+ var LABELLED_UTC_RE = /\b(\d{1,2})(?::(\d{2}))?\s?(am|pm)?\s?(UTC|GMT)\b/gi;
73610
+ function convertUtcInLine(line, tz, nowMs2) {
73611
+ let out = line.replace(ISO_UTC_RE, (m) => {
73612
+ const ms = Date.parse(m);
73613
+ if (Number.isNaN(ms))
73745
73614
  return m;
73746
- replaced++;
73747
- return `${before}. ${capFirst(after + peek)}`;
73748
- });
73749
- out = out.replace(/ [\u2014\u2013](\s*\n)/g, (_m, ws) => {
73750
- replaced++;
73751
- return `.${ws}`;
73615
+ return renderLocalDatetime(ms, tz);
73752
73616
  });
73753
- out = out.replace(/(\w)[\u2014\u2013](\w)(\w?)/g, (m, before, after, peek) => {
73754
- if (/\d/.test(before) && /\d/.test(after))
73617
+ out = out.replace(LABELLED_UTC_RE, (m, hh, mm, ampm) => {
73618
+ if (mm == null && ampm == null)
73755
73619
  return m;
73756
- replaced++;
73757
- return `${before}. ${capFirst(after + peek)}`;
73758
- });
73759
- out = out.replace(/[\u2014\u2013]/g, () => {
73760
- replaced++;
73761
- return "-";
73620
+ let h = Number(hh);
73621
+ const pm = ampm != null && /pm/i.test(ampm);
73622
+ if (ampm != null) {
73623
+ if (h > 12)
73624
+ return m;
73625
+ if (h === 12)
73626
+ h = pm ? 12 : 0;
73627
+ else if (pm)
73628
+ h += 12;
73629
+ }
73630
+ if (h > 23)
73631
+ return m;
73632
+ const minute = mm != null ? Number(mm) : 0;
73633
+ if (minute > 59)
73634
+ return m;
73635
+ const now = new Date(nowMs2);
73636
+ const ms = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), h, minute);
73637
+ return `${renderLocalClock(ms, tz)} ${tzAbbrev(ms, tz)}`;
73762
73638
  });
73763
- return { out, replaced };
73639
+ return out;
73764
73640
  }
73765
- function scrubVoice2(text4) {
73766
- if (!enabled4() || text4.length === 0) {
73767
- return { scrubbed: text4, replaced: 0, openersStripped: 0 };
73641
+ function normalizeUtcDatetimes(text4, tz, nowMs2) {
73642
+ if (!text4)
73643
+ return text4;
73644
+ try {
73645
+ if (!/\d{4}-\d{2}-\d{2}T|\b(?:UTC|GMT)\b/i.test(text4))
73646
+ return text4;
73647
+ const { masked, restore: restore2 } = maskTemporalRegions(text4);
73648
+ const out = perNonQuoteLine(masked, (line) => convertUtcInLine(line, tz, nowMs2));
73649
+ return restore2(out);
73650
+ } catch {
73651
+ return text4;
73768
73652
  }
73769
- const { parked, parts } = park2(text4);
73770
- const opener = openerStripEnabled2() ? stripLeadingAffirmation2(parked) : { out: parked, count: 0 };
73771
- const { out, replaced } = replaceDashes2(opener.out);
73772
- const total = replaced + opener.count;
73773
- if (total === 0) {
73774
- return { scrubbed: text4, replaced: 0, openersStripped: 0 };
73653
+ }
73654
+ var REL = "(today|tonight|this\\s+morning|this\\s+afternoon|this\\s+evening|tomorrow|yesterday)";
73655
+ var SEP = "([\\s,]*(?:on\\s+)?\\(?\\s*)";
73656
+ var WD = "(?:mon|tue|wed|thu|fri|sat|sun)[a-z]*";
73657
+ var MON = "(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*";
73658
+ var WD_GROUP = `((?:${WD})[\\s,]+)?`;
73659
+ var DATE_CORE = `((?:\\d{1,2}\\s+${MON})|(?:${MON}\\s+\\d{1,2})|(?:\\d{4}-\\d{2}-\\d{2})|(?:\\d{1,2}\\/\\d{2}))`;
73660
+ var MONTHS4 = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
73661
+ var WEEKDAY_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
73662
+ function buildRelRegex() {
73663
+ return new RegExp(REL + SEP + WD_GROUP + DATE_CORE, "gi");
73664
+ }
73665
+ function parseDateCore(core) {
73666
+ let m;
73667
+ if (m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(core)) {
73668
+ return { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
73669
+ }
73670
+ if (m = /^(\d{1,2})\/(\d{2})$/.exec(core)) {
73671
+ return { year: null, month: Number(m[2]), day: Number(m[1]) };
73672
+ }
73673
+ if (m = /^(\d{1,2})\s+([a-z]+)$/i.exec(core)) {
73674
+ const mon = MONTHS4.indexOf(m[2].slice(0, 3).toLowerCase());
73675
+ if (mon < 0)
73676
+ return null;
73677
+ return { year: null, month: mon + 1, day: Number(m[1]) };
73678
+ }
73679
+ if (m = /^([a-z]+)\s+(\d{1,2})$/i.exec(core)) {
73680
+ const mon = MONTHS4.indexOf(m[1].slice(0, 3).toLowerCase());
73681
+ if (mon < 0)
73682
+ return null;
73683
+ return { year: null, month: mon + 1, day: Number(m[2]) };
73684
+ }
73685
+ return null;
73686
+ }
73687
+ function dayIndex(y, mo1, d) {
73688
+ return Math.floor(Date.UTC(y, mo1 - 1, d) / 86400000);
73689
+ }
73690
+ function resolveYear(parsed, todayY, todayIdx) {
73691
+ if (parsed.year != null)
73692
+ return parsed.year;
73693
+ let best = todayY;
73694
+ let bestAbs = Infinity;
73695
+ for (const y of [todayY - 1, todayY, todayY + 1]) {
73696
+ const diff = Math.abs(dayIndex(y, parsed.month, parsed.day) - todayIdx);
73697
+ if (diff < bestAbs) {
73698
+ bestAbs = diff;
73699
+ best = y;
73700
+ }
73701
+ }
73702
+ return best;
73703
+ }
73704
+ function matchCase(sample, replacement) {
73705
+ if (sample.length > 0 && sample[0] === sample[0].toUpperCase() && sample[0] !== sample[0].toLowerCase()) {
73706
+ return replacement.charAt(0).toUpperCase() + replacement.slice(1);
73707
+ }
73708
+ return replacement;
73709
+ }
73710
+ var TIME_OF_DAY_RE = /^(tonight|this\s+)/i;
73711
+ function rewriteRelativeInLine(line, tz, nowMs2) {
73712
+ const today = localDay(nowMs2, tz);
73713
+ const [ty, tm, td] = today.split("-").map(Number);
73714
+ const todayIdx = dayIndex(ty, tm, td);
73715
+ return line.replace(buildRelRegex(), (full, rel, sep4, wdGroup, core) => {
73716
+ const parsed = parseDateCore(core);
73717
+ if (parsed == null)
73718
+ return full;
73719
+ const year = resolveYear(parsed, ty, todayIdx);
73720
+ const targetIdx = dayIndex(year, parsed.month, parsed.day);
73721
+ const diff = targetIdx - todayIdx;
73722
+ if (diff < -1 || diff > 1)
73723
+ return full;
73724
+ const isTimeOfDay = TIME_OF_DAY_RE.test(rel);
73725
+ let newRel = rel;
73726
+ if (isTimeOfDay) {
73727
+ if (diff !== 0)
73728
+ return full;
73729
+ } else {
73730
+ const want = diff === 1 ? "tomorrow" : diff === 0 ? "today" : "yesterday";
73731
+ newRel = matchCase(rel, want);
73732
+ }
73733
+ let newWdGroup = wdGroup;
73734
+ if (wdGroup != null) {
73735
+ const trueDow = new Date(Date.UTC(year, parsed.month - 1, parsed.day)).getUTCDay();
73736
+ const trueShort = WEEKDAY_SHORT[trueDow];
73737
+ const wdMatch = /^([a-z]+)([\s,]+)$/i.exec(wdGroup);
73738
+ if (wdMatch != null) {
73739
+ const writtenShort = wdMatch[1].slice(0, 3).toLowerCase();
73740
+ if (writtenShort !== trueShort.toLowerCase()) {
73741
+ const wasLong = wdMatch[1].length > 3;
73742
+ const corrected = wasLong ? longWeekday(trueDow) : trueShort;
73743
+ newWdGroup = matchCase(wdMatch[1], corrected) + wdMatch[2];
73744
+ }
73745
+ }
73746
+ }
73747
+ return `${newRel}${sep4}${newWdGroup ?? ""}${core}`;
73748
+ });
73749
+ }
73750
+ var WEEKDAY_LONG = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
73751
+ function longWeekday(dow) {
73752
+ return WEEKDAY_LONG[dow];
73753
+ }
73754
+ function normalizeRelativeDays(text4, tz, nowMs2) {
73755
+ if (!text4)
73756
+ return text4;
73757
+ try {
73758
+ if (!/\b(today|tonight|this\s+(?:morning|afternoon|evening)|tomorrow|yesterday)\b/i.test(text4)) {
73759
+ return text4;
73760
+ }
73761
+ const { masked, restore: restore2 } = maskTemporalRegions(text4);
73762
+ const out = perNonQuoteLine(masked, (line) => rewriteRelativeInLine(line, tz, nowMs2));
73763
+ return restore2(out);
73764
+ } catch {
73765
+ return text4;
73775
73766
  }
73776
- return { scrubbed: restore2(out, parts), replaced: total, openersStripped: opener.count };
73767
+ }
73768
+ function normalizeTemporal(text4, tz, nowMs2) {
73769
+ return normalizeRelativeDays(normalizeUtcDatetimes(text4, tz, nowMs2), tz, nowMs2);
73777
73770
  }
73778
73771
 
73779
73772
  // gateway/outbound-send-path.ts
73780
- init_format();
73781
- init_text_voice_scrub();
73782
73773
  var import_grammy10 = __toESM(require_mod2(), 1);
73783
- import { statSync as statSync9 } from "fs";
73774
+ import { statSync as statSync10 } from "fs";
73784
73775
  import { extname } from "path";
73785
73776
 
73786
73777
  // gateway/backstop-delivery.ts
@@ -73911,6 +73902,314 @@ function combineReadBackResults(perId) {
73911
73902
  init_format();
73912
73903
  init_rich_send();
73913
73904
 
73905
+ // outbox.ts
73906
+ import { createHash as createHash3 } from "node:crypto";
73907
+ import {
73908
+ existsSync as existsSync24,
73909
+ mkdirSync as mkdirSync25,
73910
+ readFileSync as readFileSync24,
73911
+ readdirSync as readdirSync6,
73912
+ renameSync as renameSync8,
73913
+ statSync as statSync9,
73914
+ unlinkSync as unlinkSync12,
73915
+ writeFileSync as writeFileSync22,
73916
+ appendFileSync as appendFileSync4
73917
+ } from "node:fs";
73918
+ import { join as join28 } from "node:path";
73919
+ import { homedir as homedir10 } from "node:os";
73920
+ function sha256Hex(s) {
73921
+ return createHash3("sha256").update(s, "utf8").digest("hex");
73922
+ }
73923
+ function resolveStateDir2(explicit) {
73924
+ if (explicit != null && explicit !== "")
73925
+ return explicit;
73926
+ const env = process.env.TELEGRAM_STATE_DIR;
73927
+ if (env != null && env !== "")
73928
+ return env;
73929
+ const home2 = process.env.HOME ?? homedir10();
73930
+ return join28(home2, ".claude", "channels", "telegram");
73931
+ }
73932
+ function resolveOutboxDir(stateDir) {
73933
+ return join28(resolveStateDir2(stateDir), "outbox");
73934
+ }
73935
+ var JOURNAL_FILE = "delivered.jsonl";
73936
+ var OUTBOX_MAX_AGE_MS = 30 * 60000;
73937
+ var OUTBOX_QUIET_MS = 5000;
73938
+ var OUTBOX_SENDING_TIMEOUT_MS = 60000;
73939
+ function listPendingRecords(stateDir) {
73940
+ const dir = resolveOutboxDir(stateDir);
73941
+ try {
73942
+ return readdirSync6(dir).filter((f) => f.endsWith(".json") && f !== JOURNAL_FILE && !f.startsWith("."));
73943
+ } catch {
73944
+ return [];
73945
+ }
73946
+ }
73947
+ function readOutboxRecord(fileName, stateDir) {
73948
+ try {
73949
+ const raw = readFileSync24(join28(resolveOutboxDir(stateDir), fileName), "utf8");
73950
+ return JSON.parse(raw);
73951
+ } catch {
73952
+ return null;
73953
+ }
73954
+ }
73955
+ function claimRecord(nonce, stateDir) {
73956
+ const dir = resolveOutboxDir(stateDir);
73957
+ const from = join28(dir, `${nonce}.json`);
73958
+ const to = join28(dir, `${nonce}.sending`);
73959
+ try {
73960
+ renameSync8(from, to);
73961
+ return to;
73962
+ } catch {
73963
+ return null;
73964
+ }
73965
+ }
73966
+ function releaseClaim(nonce, stateDir) {
73967
+ const dir = resolveOutboxDir(stateDir);
73968
+ try {
73969
+ renameSync8(join28(dir, `${nonce}.sending`), join28(dir, `${nonce}.json`));
73970
+ } catch {}
73971
+ }
73972
+ function removeClaimed(nonce, stateDir) {
73973
+ try {
73974
+ unlinkSync12(join28(resolveOutboxDir(stateDir), `${nonce}.sending`));
73975
+ } catch {}
73976
+ }
73977
+ function reclaimStaleSending(stateDir, now = Date.now()) {
73978
+ const dir = resolveOutboxDir(stateDir);
73979
+ let files;
73980
+ try {
73981
+ files = readdirSync6(dir).filter((f) => f.endsWith(".sending"));
73982
+ } catch {
73983
+ return;
73984
+ }
73985
+ for (const f of files) {
73986
+ try {
73987
+ const st = statSync9(join28(dir, f));
73988
+ if (now - st.mtimeMs > OUTBOX_SENDING_TIMEOUT_MS) {
73989
+ renameSync8(join28(dir, f), join28(dir, f.replace(/\.sending$/, ".json")));
73990
+ }
73991
+ } catch {}
73992
+ }
73993
+ }
73994
+ function readDeliveredNonces(stateDir) {
73995
+ const set = new Set;
73996
+ const path2 = join28(resolveOutboxDir(stateDir), JOURNAL_FILE);
73997
+ if (!existsSync24(path2))
73998
+ return set;
73999
+ try {
74000
+ for (const line of readFileSync24(path2, "utf8").split(`
74001
+ `)) {
74002
+ if (!line)
74003
+ continue;
74004
+ try {
74005
+ const e = JSON.parse(line);
74006
+ if (e.turnNonce)
74007
+ set.add(e.turnNonce);
74008
+ } catch {}
74009
+ }
74010
+ } catch {}
74011
+ return set;
74012
+ }
74013
+ var JOURNAL_KEEP = 2000;
74014
+ var JOURNAL_ROTATE_AT = 4000;
74015
+ function appendDelivered(entry, stateDir) {
74016
+ const dir = resolveOutboxDir(stateDir);
74017
+ const path2 = join28(dir, JOURNAL_FILE);
74018
+ try {
74019
+ mkdirSync25(dir, { recursive: true });
74020
+ appendFileSync4(path2, JSON.stringify(entry) + `
74021
+ `, { mode: 384 });
74022
+ compactJournalIfLarge(path2);
74023
+ } catch {}
74024
+ }
74025
+ function compactJournalIfLarge(path2) {
74026
+ try {
74027
+ const lines = readFileSync24(path2, "utf8").split(`
74028
+ `).filter((l) => l.length > 0);
74029
+ if (lines.length <= JOURNAL_ROTATE_AT)
74030
+ return;
74031
+ const kept = lines.slice(lines.length - JOURNAL_KEEP);
74032
+ const tmp = `${path2}.${process.pid}.compact`;
74033
+ writeFileSync22(tmp, kept.join(`
74034
+ `) + `
74035
+ `, { mode: 384 });
74036
+ renameSync8(tmp, path2);
74037
+ } catch {}
74038
+ }
74039
+ function clearOutboxRecord(nonce, stateDir) {
74040
+ const dir = resolveOutboxDir(stateDir);
74041
+ for (const suffix of [".json", ".sending"]) {
74042
+ try {
74043
+ unlinkSync12(join28(dir, `${nonce}${suffix}`));
74044
+ } catch {}
74045
+ }
74046
+ }
74047
+ function decideOutboxSweep(input) {
74048
+ const {
74049
+ record,
74050
+ now,
74051
+ deliveredNonces,
74052
+ textAlreadyDelivered,
74053
+ routable,
74054
+ routePrefix = "",
74055
+ quietMs = OUTBOX_QUIET_MS,
74056
+ maxAgeMs = OUTBOX_MAX_AGE_MS
74057
+ } = input;
74058
+ if (deliveredNonces.has(record.turnNonce))
74059
+ return { action: "skip-journaled" };
74060
+ const age = now - record.createdAt;
74061
+ if (age < quietMs)
74062
+ return { action: "skip-quiet" };
74063
+ if (textAlreadyDelivered)
74064
+ return { action: "skip-dedup" };
74065
+ if (!routable)
74066
+ return { action: "skip-unroutable" };
74067
+ const delayed = age > maxAgeMs;
74068
+ const prefix = (delayed ? "(delayed) " : "") + routePrefix;
74069
+ return { action: delayed ? "send-delayed" : "send", text: prefix + record.text };
74070
+ }
74071
+ function resolveOutboxChat(record, deps) {
74072
+ if (record.chatId != null && record.chatId !== "") {
74073
+ return { chatId: record.chatId, threadId: record.threadId ?? null, via: "anchor" };
74074
+ }
74075
+ if (record.anchorContent && deps.registryChainLookup) {
74076
+ const hit = deps.registryChainLookup(record.anchorContent);
74077
+ if (hit != null)
74078
+ return { chatId: hit.chatId, threadId: hit.threadId, via: "registry" };
74079
+ }
74080
+ if (record.originChatId != null && record.originChatId !== "") {
74081
+ return { chatId: record.originChatId, threadId: record.originThreadId ?? null, via: "origin" };
74082
+ }
74083
+ return null;
74084
+ }
74085
+ function extractTaskId(anchorContent) {
74086
+ if (typeof anchorContent !== "string")
74087
+ return null;
74088
+ const m = anchorContent.match(/<task-id>\s*([^<\s]+)\s*<\/task-id>/) ?? anchorContent.match(/task[_-]?id="([^"]+)"/i) ?? anchorContent.match(/task[_-]?id:\s*([^\s,}"']+)/i);
74089
+ return m ? m[1] : null;
74090
+ }
74091
+
74092
+ // registry/subagents-schema.ts
74093
+ function countRunningBackgroundSubagents(db2) {
74094
+ const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
74095
+ return row?.n ?? 0;
74096
+ }
74097
+ function recordSubagentEnd(db2, args) {
74098
+ db2.prepare(`
74099
+ UPDATE subagents
74100
+ SET
74101
+ ended_at = ?,
74102
+ status = ?,
74103
+ result_summary = COALESCE(?, result_summary)
74104
+ WHERE id = ?
74105
+ AND status NOT IN ('completed', 'failed')
74106
+ `).run(args.endedAt, args.status, args.resultSummary ?? null, args.id);
74107
+ }
74108
+ function recordSubagentStall(db2, args) {
74109
+ args.stalledAt;
74110
+ if (args.reason !== undefined) {
74111
+ db2.prepare(`
74112
+ UPDATE subagents
74113
+ SET status = 'stalled',
74114
+ result_summary = ?
74115
+ WHERE id = ?
74116
+ AND status NOT IN ('completed', 'failed')
74117
+ `).run(args.reason, args.id);
74118
+ return;
74119
+ }
74120
+ db2.prepare(`
74121
+ UPDATE subagents
74122
+ SET status = 'stalled'
74123
+ WHERE id = ?
74124
+ AND status NOT IN ('completed', 'failed')
74125
+ `).run(args.id);
74126
+ }
74127
+ function reapStuckRunningRows(db2, args) {
74128
+ const cutoff = args.now - args.ttlMs;
74129
+ const rows = db2.prepare(`
74130
+ SELECT id, jsonl_agent_id FROM subagents
74131
+ WHERE status = 'running'
74132
+ AND background = 1
74133
+ AND COALESCE(last_activity_at, started_at) < ?
74134
+ `).all(cutoff);
74135
+ const candidates = args.isLive ? rows.filter((r) => !args.isLive(r.jsonl_agent_id)) : rows;
74136
+ for (const row of candidates) {
74137
+ recordSubagentStall(db2, {
74138
+ id: row.id,
74139
+ stalledAt: args.now,
74140
+ reason: `reaped: stuck in running past ${Math.round(args.ttlMs / 60000)}min ttl (jsonl linkage likely missing)`
74141
+ });
74142
+ }
74143
+ return { reaped: candidates.length, ids: candidates.map((r) => r.id) };
74144
+ }
74145
+ function recordSubagentResume(db2, args) {
74146
+ args.resumedAt;
74147
+ db2.prepare(`
74148
+ UPDATE subagents
74149
+ SET status = 'running'
74150
+ WHERE id = ?
74151
+ AND status = 'stalled'
74152
+ `).run(args.id);
74153
+ }
74154
+ function bumpSubagentActivity(db2, args) {
74155
+ db2.prepare(`
74156
+ UPDATE subagents
74157
+ SET last_activity_at = ?
74158
+ WHERE id = ?
74159
+ `).run(args.ts, args.id);
74160
+ }
74161
+ function recordSubagentModel(db2, args) {
74162
+ db2.prepare(`
74163
+ UPDATE subagents
74164
+ SET model = ?
74165
+ WHERE id = ?
74166
+ `).run(args.model, args.id);
74167
+ }
74168
+ function recordNestedSubagentDispatch(db2, args) {
74169
+ db2.prepare(`
74170
+ INSERT OR IGNORE INTO subagents
74171
+ (id, parent_session_id, parent_turn_key, agent_type, description,
74172
+ background, started_at, last_activity_at, status, jsonl_agent_id,
74173
+ parent_agent_id)
74174
+ VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, 'running', NULL, ?)
74175
+ `).run(args.toolUseId, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now, args.parentJsonlAgentId);
74176
+ db2.prepare(`
74177
+ UPDATE subagents
74178
+ SET parent_agent_id = COALESCE(parent_agent_id, ?),
74179
+ parent_turn_key = COALESCE(
74180
+ parent_turn_key,
74181
+ (SELECT p.parent_turn_key FROM subagents p
74182
+ WHERE p.jsonl_agent_id = ? LIMIT 1)
74183
+ )
74184
+ WHERE id = ?
74185
+ `).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
74186
+ }
74187
+ function resolveSubagentOriginTurnKey(db2, jsonlAgentId, maxHops = 5) {
74188
+ const seen = new Set;
74189
+ let currentJsonlId = jsonlAgentId;
74190
+ for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
74191
+ if (seen.has(currentJsonlId))
74192
+ return null;
74193
+ seen.add(currentJsonlId);
74194
+ const row = db2.prepare("SELECT parent_turn_key, parent_agent_id FROM subagents WHERE jsonl_agent_id = ? LIMIT 1").get(currentJsonlId);
74195
+ if (row == null)
74196
+ return null;
74197
+ if (row.parent_turn_key != null && row.parent_turn_key.length > 0)
74198
+ return row.parent_turn_key;
74199
+ currentJsonlId = row.parent_agent_id ?? null;
74200
+ }
74201
+ return null;
74202
+ }
74203
+
74204
+ // gateway/outbox-sweep.ts
74205
+ function journalExternalDelivery(args, stateDir, now = Date.now()) {
74206
+ const nonce = args.turnNonce;
74207
+ if (nonce == null || nonce === "")
74208
+ return;
74209
+ appendDelivered({ turnNonce: nonce, textSha256: sha256Hex(args.text), tgMessageId: args.tgMessageId, ts: now }, stateDir);
74210
+ clearOutboxRecord(nonce, stateDir);
74211
+ }
74212
+
73914
74213
  // gateway/chat-id-fallback.ts
73915
74214
  function resolveChatIdFallback(rawChatId, access, turnSessionChatId, lastKnownChatId, hasLiveTurn) {
73916
74215
  if (access.allowFrom.includes(rawChatId) || rawChatId in access.groups) {
@@ -73951,12 +74250,12 @@ function decideOverPing(input) {
73951
74250
  // silent-reply-anchor.ts
73952
74251
  init_format();
73953
74252
  var TELEGRAM_MSG_CAP2 = RICH_MESSAGE_MAX_CHARS;
73954
- function enabled5() {
74253
+ function enabled4() {
73955
74254
  const v = process.env.SWITCHROOM_DISABLE_SILENT_REPLY_AUTOEDIT;
73956
74255
  return !(v === "1" || v === "true");
73957
74256
  }
73958
74257
  function decideSilentReplyAnchor(input) {
73959
- if (!enabled5()) {
74258
+ if (!enabled4()) {
73960
74259
  return { kind: "fresh", becomesAnchor: false };
73961
74260
  }
73962
74261
  if (!input.effectivelySilent) {
@@ -74279,11 +74578,11 @@ function classifyPhotoFile(path2) {
74279
74578
  }
74280
74579
 
74281
74580
  // streaming-metrics.ts
74282
- function enabled6() {
74581
+ function enabled5() {
74283
74582
  return process.env.SWITCHROOM_STREAMING_METRICS === "1";
74284
74583
  }
74285
74584
  function logStreamingEvent2(ev) {
74286
- if (!enabled6())
74585
+ if (!enabled5())
74287
74586
  return;
74288
74587
  const line = JSON.stringify({ ts: monotonicNowMs2(), ...ev });
74289
74588
  try {
@@ -74300,26 +74599,26 @@ function monotonicNowMs2() {
74300
74599
  }
74301
74600
 
74302
74601
  // silent-end.ts
74303
- import { existsSync as existsSync24, readFileSync as readFileSync24, writeFileSync as writeFileSync22, unlinkSync as unlinkSync12, mkdirSync as mkdirSync25 } from "node:fs";
74304
- import { dirname as dirname11, join as join28 } from "node:path";
74305
- import { homedir as homedir10 } from "node:os";
74602
+ import { existsSync as existsSync25, readFileSync as readFileSync25, writeFileSync as writeFileSync23, unlinkSync as unlinkSync13, mkdirSync as mkdirSync26 } from "node:fs";
74603
+ import { dirname as dirname11, join as join29 } from "node:path";
74604
+ import { homedir as homedir11 } from "node:os";
74306
74605
  var SILENT_END_MAX_RETRIES2 = 2;
74307
74606
  var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
74308
74607
  function silentEndFallbackText(turnDurationMs) {
74309
74608
  const elapsed = typeof turnDurationMs === "number" && Number.isFinite(turnDurationMs) && turnDurationMs > 0 ? ` (waited ${Math.round(turnDurationMs / 1000)}s)` : "";
74310
74609
  return "\u26a0\ufe0f The agent finished working but didn\u2019t send a reply" + elapsed + " \u2014 your last message may not have been answered. Please try asking again.";
74311
74610
  }
74312
- function resolveStateDir2(deps) {
74611
+ function resolveStateDir3(deps) {
74313
74612
  if (deps?.stateDir != null)
74314
74613
  return deps.stateDir;
74315
74614
  const env = process.env.TELEGRAM_STATE_DIR;
74316
74615
  if (env != null && env !== "")
74317
74616
  return env;
74318
- const home2 = process.env.HOME ?? homedir10();
74319
- return join28(home2, ".claude", "channels", "telegram");
74617
+ const home2 = process.env.HOME ?? homedir11();
74618
+ return join29(home2, ".claude", "channels", "telegram");
74320
74619
  }
74321
74620
  function resolveStatePath3(deps) {
74322
- return join28(resolveStateDir2(deps), "silent-end-pending.json");
74621
+ return join29(resolveStateDir3(deps), "silent-end-pending.json");
74323
74622
  }
74324
74623
  function emitLog2(deps, line) {
74325
74624
  if (deps?.log != null)
@@ -74331,8 +74630,8 @@ function writeSilentEndState2(args, deps) {
74331
74630
  const statePath = resolveStatePath3(deps);
74332
74631
  let retryCount = 0;
74333
74632
  try {
74334
- if (existsSync24(statePath)) {
74335
- const prev = JSON.parse(readFileSync24(statePath, "utf8"));
74633
+ if (existsSync25(statePath)) {
74634
+ const prev = JSON.parse(readFileSync25(statePath, "utf8"));
74336
74635
  if (prev.turnKey === args.turnKey && typeof prev.retryCount === "number") {
74337
74636
  retryCount = prev.retryCount;
74338
74637
  }
@@ -74348,8 +74647,8 @@ function writeSilentEndState2(args, deps) {
74348
74647
  timestamp: Date.now()
74349
74648
  };
74350
74649
  try {
74351
- mkdirSync25(dirname11(statePath), { recursive: true });
74352
- writeFileSync22(statePath, JSON.stringify(state3), "utf8");
74650
+ mkdirSync26(dirname11(statePath), { recursive: true });
74651
+ writeFileSync23(statePath, JSON.stringify(state3), "utf8");
74353
74652
  emitLog2(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
74354
74653
  `);
74355
74654
  } catch (err) {
@@ -74359,13 +74658,13 @@ function writeSilentEndState2(args, deps) {
74359
74658
  }
74360
74659
  function clearSilentEndState2(turnKey3, deps) {
74361
74660
  const statePath = resolveStatePath3(deps);
74362
- if (!existsSync24(statePath))
74661
+ if (!existsSync25(statePath))
74363
74662
  return;
74364
74663
  try {
74365
- const prev = JSON.parse(readFileSync24(statePath, "utf8"));
74664
+ const prev = JSON.parse(readFileSync25(statePath, "utf8"));
74366
74665
  if (prev.turnKey != null && prev.turnKey !== turnKey3)
74367
74666
  return;
74368
- unlinkSync12(statePath);
74667
+ unlinkSync13(statePath);
74369
74668
  emitLog2(deps, `silent-end: cleared state file turnKey=${turnKey3}
74370
74669
  `);
74371
74670
  } catch {}
@@ -74396,10 +74695,10 @@ function settleCapturedProseDelivery(outcome, effects) {
74396
74695
  }
74397
74696
  function readSilentEndState2(deps) {
74398
74697
  const statePath = resolveStatePath3(deps);
74399
- if (!existsSync24(statePath))
74698
+ if (!existsSync25(statePath))
74400
74699
  return null;
74401
74700
  try {
74402
- return JSON.parse(readFileSync24(statePath, "utf8"));
74701
+ return JSON.parse(readFileSync25(statePath, "utf8"));
74403
74702
  } catch {
74404
74703
  return null;
74405
74704
  }
@@ -74428,10 +74727,21 @@ function recordSilentTurnEnd2(args, deps) {
74428
74727
  var recordUndeliveredTurnEnd2 = recordSilentTurnEnd2;
74429
74728
 
74430
74729
  // gateway/outbound-send-path.ts
74431
- function normalizeOutboundBody(rawText, site, redact2) {
74432
- let text4 = normalizeParagraphBreaks(repairEscapedWhitespace(rawText));
74730
+ function normalizeOutboundBody(rawText, site, redact2, opts = {}) {
74731
+ const { literalText = false, addSpacers = false, tz, nowMs: nowMs2 } = opts;
74732
+ let text4 = repairEscapedWhitespace(rawText);
74733
+ if (!literalText)
74734
+ text4 = normalizeParagraphBreaks(text4);
74433
74735
  text4 = redact2(text4, site);
74434
- text4 = stripExcessBold(normalizePunctuation(text4));
74736
+ if (!literalText) {
74737
+ let formatted = stripExcessBold(normalizePunctuation(text4));
74738
+ if (addSpacers)
74739
+ formatted = addParagraphSpacers(formatted);
74740
+ text4 = formatted;
74741
+ if (tz != null && nowMs2 != null) {
74742
+ text4 = normalizeTemporal(text4, tz, nowMs2);
74743
+ }
74744
+ }
74435
74745
  let voiceReplaced = 0;
74436
74746
  const scrub = scrubVoice(text4);
74437
74747
  if (scrub.replaced > 0) {
@@ -74683,7 +74993,10 @@ async function sendReply(deps, req) {
74683
74993
  const rawText = args.text;
74684
74994
  if (rawText == null || rawText === "")
74685
74995
  throw new Error("reply: text is required and cannot be empty");
74686
- const _normalized = normalizeOutboundBody(rawText, "reply", redactOutboundText);
74996
+ const _normalized = normalizeOutboundBody(rawText, "reply", redactOutboundText, {
74997
+ tz: resolveEnvTimezone(),
74998
+ nowMs: Date.now()
74999
+ });
74687
75000
  let text4 = _normalized.text;
74688
75001
  if (_normalized.voiceReplaced > 0) {
74689
75002
  emitRuntimeMetric2({
@@ -74862,7 +75175,7 @@ ${url}`;
74862
75175
  }
74863
75176
  for (const f of files) {
74864
75177
  assertSendable(f);
74865
- const st = statSync9(f);
75178
+ const st = statSync10(f);
74866
75179
  if (st.size > MAX_ATTACHMENT_BYTES) {
74867
75180
  throw new Error(`file too large: ${f} (${(st.size / 1024 / 1024).toFixed(1)}MB, max 50MB)`);
74868
75181
  }
@@ -75063,6 +75376,9 @@ ${url}`;
75063
75376
  closeObligationOnSubstantiveReply(args, turn2, replyRoutedOriginTurn);
75064
75377
  }
75065
75378
  outboundDedup.record(chat_id, threadId, decision.mergedText, Date.now(), turn2?.registryKey ?? null);
75379
+ if (isFinalAnswerReply({ text: decision.mergedText, disableNotification: modelDisableNotification })) {
75380
+ journalExternalDelivery({ turnNonce: turn2?.turnId ?? null, text: decision.mergedText, tgMessageId: decision.messageId });
75381
+ }
75066
75382
  silentAnchorEditDone = true;
75067
75383
  } catch (err) {
75068
75384
  process.stderr.write(`telegram gateway: silent-reply auto-edit failed, ` + `falling back to fresh send: ${err instanceof Error ? err.message : String(err)}
@@ -75336,7 +75652,11 @@ ${url}`;
75336
75652
  process.stderr.write(`telegram channel: reply: finalized chatId=${chat_id} messageIds=[${sentIds.join(",")}] chunks=${chunks.length}
75337
75653
  `);
75338
75654
  if (sentIds.length > 0) {
75339
- outboundDedup.record(chat_id, threadId, text4, Date.now(), getCurrentTurn()?.registryKey ?? null);
75655
+ const t = getCurrentTurn();
75656
+ outboundDedup.record(chat_id, threadId, text4, Date.now(), t?.registryKey ?? null);
75657
+ if (shouldJournalReplySiteDelivery({ text: rawText, disableNotification: modelDisableNotification })) {
75658
+ journalExternalDelivery({ turnNonce: t?.turnId ?? null, text: text4, tgMessageId: sentIds[sentIds.length - 1] });
75659
+ }
75340
75660
  }
75341
75661
  return { content: [{ type: "text", text: result }] };
75342
75662
  }
@@ -75389,6 +75709,7 @@ async function deliverCapturedProse(deps, args) {
75389
75709
  } catch {}
75390
75710
  }
75391
75711
  outboundDedup.record(chatId, threadId, text4, now, registryKey);
75712
+ journalExternalDelivery({ turnNonce: originTurnId, text: text4, tgMessageId: sentIds[sentIds.length - 1] });
75392
75713
  process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
75393
75714
  `);
75394
75715
  outcome = "sent";
@@ -75841,8 +76162,37 @@ class LivenessTracker {
75841
76162
  }
75842
76163
  }
75843
76164
 
75844
- // gateway/stream-render.ts
76165
+ // gateway/outbound-send-path.ts
76166
+ init_format();
76167
+ init_text_voice_scrub();
76168
+ var import_grammy11 = __toESM(require_mod2(), 1);
75845
76169
  init_format();
76170
+ init_rich_send();
76171
+ function normalizeOutboundBody2(rawText, site, redact2, opts = {}) {
76172
+ const { literalText = false, addSpacers = false, tz, nowMs: nowMs2 } = opts;
76173
+ let text4 = repairEscapedWhitespace(rawText);
76174
+ if (!literalText)
76175
+ text4 = normalizeParagraphBreaks(text4);
76176
+ text4 = redact2(text4, site);
76177
+ if (!literalText) {
76178
+ let formatted = stripExcessBold(normalizePunctuation(text4));
76179
+ if (addSpacers)
76180
+ formatted = addParagraphSpacers(formatted);
76181
+ text4 = formatted;
76182
+ if (tz != null && nowMs2 != null) {
76183
+ text4 = normalizeTemporal(text4, tz, nowMs2);
76184
+ }
76185
+ }
76186
+ let voiceReplaced = 0;
76187
+ const scrub = scrubVoice(text4);
76188
+ if (scrub.replaced > 0) {
76189
+ text4 = scrub.scrubbed;
76190
+ voiceReplaced = scrub.replaced;
76191
+ }
76192
+ return { text: text4, voiceReplaced };
76193
+ }
76194
+
76195
+ // gateway/stream-render.ts
75846
76196
  init_history();
75847
76197
 
75848
76198
  // narrative-dedup.ts
@@ -75908,20 +76258,20 @@ var INTERRUPTED_VIA = new Set([
75908
76258
  init_rich_send();
75909
76259
 
75910
76260
  // runtime-metrics.ts
75911
- import { mkdirSync as mkdirSync27, appendFileSync as appendFileSync4 } from "node:fs";
75912
- import { dirname as dirname12, join as join30 } from "node:path";
76261
+ import { mkdirSync as mkdirSync28, appendFileSync as appendFileSync5 } from "node:fs";
76262
+ import { dirname as dirname12, join as join31 } from "node:path";
75913
76263
  function resolveJsonlPath2() {
75914
76264
  const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
75915
76265
  if (override && override.trim() !== "")
75916
76266
  return override.trim();
75917
76267
  const base = process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent";
75918
- return join30(base, "runtime-metrics.jsonl");
76268
+ return join31(base, "runtime-metrics.jsonl");
75919
76269
  }
75920
76270
  function appendJsonl2(line) {
75921
76271
  const path2 = resolveJsonlPath2();
75922
76272
  try {
75923
- mkdirSync27(dirname12(path2), { recursive: true });
75924
- appendFileSync4(path2, line + `
76273
+ mkdirSync28(dirname12(path2), { recursive: true });
76274
+ appendFileSync5(path2, line + `
75925
76275
  `, "utf-8");
75926
76276
  } catch (err) {
75927
76277
  process.stderr.write(`runtime-metrics: jsonl write failed: ${err.message}
@@ -75942,9 +76292,6 @@ function emitRuntimeMetric2(event) {
75942
76292
  captureEvent(event.kind, { ...event, ts: wrapped.ts });
75943
76293
  }
75944
76294
 
75945
- // gateway/stream-render.ts
75946
- init_text_voice_scrub();
75947
-
75948
76295
  // gateway/feed-open-gate.ts
75949
76296
  function shouldEarlyOpenLiveness(input) {
75950
76297
  if (!input.enabled)
@@ -76414,28 +76761,28 @@ function detectStatusSurfaceDegraded(t) {
76414
76761
  // gateway/turn-active-marker.ts
76415
76762
  import {
76416
76763
  closeSync as closeSync5,
76417
- existsSync as existsSync27,
76418
- mkdirSync as mkdirSync28,
76764
+ existsSync as existsSync28,
76765
+ mkdirSync as mkdirSync29,
76419
76766
  openSync as openSync5,
76420
- readFileSync as readFileSync25,
76421
- statSync as statSync10,
76422
- unlinkSync as unlinkSync13,
76767
+ readFileSync as readFileSync26,
76768
+ statSync as statSync11,
76769
+ unlinkSync as unlinkSync14,
76423
76770
  utimesSync,
76424
- writeFileSync as writeFileSync23
76771
+ writeFileSync as writeFileSync24
76425
76772
  } from "node:fs";
76426
- import { join as join31 } from "node:path";
76773
+ import { join as join32 } from "node:path";
76427
76774
  var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
76428
76775
  var TURN_ACTIVE_HARD_TTL_MS = 10 * 60000;
76429
76776
  function writeTurnActiveMarker(stateDir, marker) {
76430
76777
  try {
76431
- mkdirSync28(stateDir, { recursive: true });
76432
- writeFileSync23(join31(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
76778
+ mkdirSync29(stateDir, { recursive: true });
76779
+ writeFileSync24(join32(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
76433
76780
  `, { mode: 384 });
76434
76781
  } catch {}
76435
76782
  }
76436
76783
  function touchTurnActiveMarker(stateDir) {
76437
- const path2 = join31(stateDir, TURN_ACTIVE_MARKER_FILE);
76438
- if (!existsSync27(path2))
76784
+ const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
76785
+ if (!existsSync28(path2))
76439
76786
  return;
76440
76787
  const now = new Date;
76441
76788
  try {
@@ -76449,13 +76796,13 @@ function touchTurnActiveMarker(stateDir) {
76449
76796
  }
76450
76797
  function removeTurnActiveMarker(stateDir) {
76451
76798
  try {
76452
- unlinkSync13(join31(stateDir, TURN_ACTIVE_MARKER_FILE));
76799
+ unlinkSync14(join32(stateDir, TURN_ACTIVE_MARKER_FILE));
76453
76800
  } catch {}
76454
76801
  }
76455
76802
  function readTurnActiveMarkerAgeMs(stateDir, now) {
76456
- const path2 = join31(stateDir, TURN_ACTIVE_MARKER_FILE);
76803
+ const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
76457
76804
  try {
76458
- const st = statSync10(path2);
76805
+ const st = statSync11(path2);
76459
76806
  return (now ?? Date.now()) - st.mtimeMs;
76460
76807
  } catch {
76461
76808
  return null;
@@ -76512,7 +76859,7 @@ function finalizeBackstopSendGated(turn, send) {
76512
76859
  var MAX_LIFETIME_MS2 = 30 * 60000;
76513
76860
  var stateByKey2 = new Map;
76514
76861
  var activeDeps3 = null;
76515
- function enabled7() {
76862
+ function enabled6() {
76516
76863
  const v = process.env.SWITCHROOM_DISABLE_PENDING_PROGRESS;
76517
76864
  return !(v === "1" || v === "true");
76518
76865
  }
@@ -76520,7 +76867,7 @@ function nowMs2() {
76520
76867
  return activeDeps3?.nowMs ? activeDeps3.nowMs() : Date.now();
76521
76868
  }
76522
76869
  function noteTurnEnd2(key) {
76523
- if (!enabled7())
76870
+ if (!enabled6())
76524
76871
  return;
76525
76872
  const s = stateByKey2.get(key);
76526
76873
  if (s == null)
@@ -77231,24 +77578,18 @@ function handleSessionEvent(deps, ev) {
77231
77578
  return;
77232
77579
  }
77233
77580
  if (turnEndDecision === "flush" && flushDecision.kind === "flush") {
77234
- let capturedText = flushDecision.text;
77235
- capturedText = normalizeParagraphBreaks(repairEscapedWhitespace(capturedText));
77236
77581
  const backstopChatId = chatId;
77237
77582
  const backstopThreadId = threadId;
77238
77583
  const backstopCtrl = ctrl;
77239
- capturedText = redactOutboundText(capturedText, "turn_flush");
77240
- capturedText = stripExcessBold(normalizePunctuation(capturedText));
77241
- {
77242
- const scrub = scrubVoice(capturedText);
77243
- if (scrub.replaced > 0) {
77244
- capturedText = scrub.scrubbed;
77245
- emitRuntimeMetric2({
77246
- kind: "voice_scrub_applied",
77247
- chatKey: statusKey(backstopChatId, backstopThreadId),
77248
- replaced: scrub.replaced,
77249
- site: "turn_flush"
77250
- });
77251
- }
77584
+ const _flushNorm = normalizeOutboundBody2(flushDecision.text, "turn_flush", redactOutboundText, { tz: resolveEnvTimezone(), nowMs: Date.now() });
77585
+ let capturedText = _flushNorm.text;
77586
+ if (_flushNorm.voiceReplaced > 0) {
77587
+ emitRuntimeMetric2({
77588
+ kind: "voice_scrub_applied",
77589
+ chatKey: statusKey(backstopChatId, backstopThreadId),
77590
+ replaced: _flushNorm.voiceReplaced,
77591
+ site: "turn_flush"
77592
+ });
77252
77593
  }
77253
77594
  turn.finalAnswerDelivered = true;
77254
77595
  turn.finalAnswerSubstantive = true;
@@ -78346,19 +78687,19 @@ function resolveAgentDirFromEnv() {
78346
78687
  }
78347
78688
 
78348
78689
  // active-reactions.ts
78349
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync24, renameSync as renameSync8, existsSync as existsSync28, unlinkSync as unlinkSync14 } from "node:fs";
78350
- import { join as join32 } from "node:path";
78690
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync25, renameSync as renameSync9, existsSync as existsSync29, unlinkSync as unlinkSync15 } from "node:fs";
78691
+ import { join as join33 } from "node:path";
78351
78692
  var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
78352
78693
  function reactionsPath(agentDir) {
78353
- return join32(agentDir, ACTIVE_REACTIONS_FILENAME);
78694
+ return join33(agentDir, ACTIVE_REACTIONS_FILENAME);
78354
78695
  }
78355
78696
  function readActiveReactions(agentDir) {
78356
78697
  const p = reactionsPath(agentDir);
78357
- if (!existsSync28(p))
78698
+ if (!existsSync29(p))
78358
78699
  return [];
78359
78700
  let raw;
78360
78701
  try {
78361
- raw = readFileSync26(p, "utf-8");
78702
+ raw = readFileSync27(p, "utf-8");
78362
78703
  } catch {
78363
78704
  return [];
78364
78705
  }
@@ -78384,15 +78725,15 @@ function writeActiveReactions(agentDir, reactions) {
78384
78725
  const p = reactionsPath(agentDir);
78385
78726
  if (reactions.length === 0) {
78386
78727
  try {
78387
- unlinkSync14(p);
78728
+ unlinkSync15(p);
78388
78729
  } catch {}
78389
78730
  return;
78390
78731
  }
78391
78732
  const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
78392
78733
  try {
78393
- writeFileSync24(tmp, JSON.stringify(reactions) + `
78734
+ writeFileSync25(tmp, JSON.stringify(reactions) + `
78394
78735
  `, "utf-8");
78395
- renameSync8(tmp, p);
78736
+ renameSync9(tmp, p);
78396
78737
  } catch {}
78397
78738
  }
78398
78739
  function addActiveReaction(agentDir, reaction) {
@@ -78409,24 +78750,24 @@ function removeActiveReaction(agentDir, chatId, messageId) {
78409
78750
  }
78410
78751
  function clearActiveReactions(agentDir) {
78411
78752
  try {
78412
- unlinkSync14(reactionsPath(agentDir));
78753
+ unlinkSync15(reactionsPath(agentDir));
78413
78754
  } catch {}
78414
78755
  }
78415
78756
 
78416
78757
  // active-reactions.ts
78417
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync25, renameSync as renameSync9, existsSync as existsSync29, unlinkSync as unlinkSync15 } from "node:fs";
78418
- import { join as join33 } from "node:path";
78758
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync26, renameSync as renameSync10, existsSync as existsSync30, unlinkSync as unlinkSync16 } from "node:fs";
78759
+ import { join as join34 } from "node:path";
78419
78760
  var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
78420
78761
  function reactionsPath2(agentDir) {
78421
- return join33(agentDir, ACTIVE_REACTIONS_FILENAME2);
78762
+ return join34(agentDir, ACTIVE_REACTIONS_FILENAME2);
78422
78763
  }
78423
78764
  function readActiveReactions2(agentDir) {
78424
78765
  const p = reactionsPath2(agentDir);
78425
- if (!existsSync29(p))
78766
+ if (!existsSync30(p))
78426
78767
  return [];
78427
78768
  let raw;
78428
78769
  try {
78429
- raw = readFileSync27(p, "utf-8");
78770
+ raw = readFileSync28(p, "utf-8");
78430
78771
  } catch {
78431
78772
  return [];
78432
78773
  }
@@ -78450,7 +78791,7 @@ function readActiveReactions2(agentDir) {
78450
78791
  }
78451
78792
  function clearActiveReactions2(agentDir) {
78452
78793
  try {
78453
- unlinkSync15(reactionsPath2(agentDir));
78794
+ unlinkSync16(reactionsPath2(agentDir));
78454
78795
  } catch {}
78455
78796
  }
78456
78797
 
@@ -78871,7 +79212,7 @@ async function loadFromAuthBroker(options = {}) {
78871
79212
  }
78872
79213
 
78873
79214
  // gateway/folder-picker-handler.ts
78874
- var import_grammy11 = __toESM(require_mod2(), 1);
79215
+ var import_grammy12 = __toESM(require_mod2(), 1);
78875
79216
 
78876
79217
  // ../src/drive/folder-picker.ts
78877
79218
  var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
@@ -79190,7 +79531,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
79190
79531
  const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
79191
79532
  ` + `Scope: \`${scope}\`
79192
79533
  ` + `Revoke with: \`/approvals revoke ${decisionId}\``;
79193
- const kb = new import_grammy11.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
79534
+ const kb = new import_grammy12.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
79194
79535
  try {
79195
79536
  await ctx.editMessageText({ markdown: confirmText }, {
79196
79537
  reply_markup: kb
@@ -79199,7 +79540,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
79199
79540
  await ctx.answerCallbackQuery({ text: "Allowed" });
79200
79541
  }
79201
79542
  function toKeyboard(spec) {
79202
- const kb = new import_grammy11.InlineKeyboard;
79543
+ const kb = new import_grammy12.InlineKeyboard;
79203
79544
  for (let r = 0;r < spec.rows.length; r++) {
79204
79545
  const row = spec.rows[r];
79205
79546
  for (const btn of row) {
@@ -79294,17 +79635,17 @@ async function approvalRecord(args, opts) {
79294
79635
  }
79295
79636
 
79296
79637
  // quota-check.ts
79297
- import { readFileSync as readFileSync28, existsSync as existsSync30 } from "fs";
79298
- import { join as join34 } from "path";
79638
+ import { readFileSync as readFileSync29, existsSync as existsSync31 } from "fs";
79639
+ import { join as join35 } from "path";
79299
79640
  var OAUTH_BETA2 = "oauth-2025-04-20";
79300
79641
  var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
79301
79642
  var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
79302
79643
  function readOauthToken2(claudeConfigDir) {
79303
- const tokenFile = join34(claudeConfigDir, ".oauth-token");
79304
- if (!existsSync30(tokenFile))
79644
+ const tokenFile = join35(claudeConfigDir, ".oauth-token");
79645
+ if (!existsSync31(tokenFile))
79305
79646
  return null;
79306
79647
  try {
79307
- const raw = readFileSync28(tokenFile, "utf-8").trim();
79648
+ const raw = readFileSync29(tokenFile, "utf-8").trim();
79308
79649
  return raw.length > 0 ? raw : null;
79309
79650
  } catch {
79310
79651
  return null;
@@ -80640,15 +80981,15 @@ async function menuWithBannerStatic(deps, banner) {
80640
80981
  }
80641
80982
 
80642
80983
  // gateway/session-model-file.ts
80643
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync26, renameSync as renameSync10, rmSync as rmSync4 } from "node:fs";
80644
- import { join as join35 } from "node:path";
80984
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync27, renameSync as renameSync11, rmSync as rmSync4 } from "node:fs";
80985
+ import { join as join36 } from "node:path";
80645
80986
  var SESSION_MODEL_FILE = ".session-model";
80646
80987
  var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
80647
80988
  var SESSION_MODEL_BOOT_ATTEMPTS_FILE = ".session-model-boot-attempts";
80648
80989
  function atomicWrite(path2, content3) {
80649
80990
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
80650
- writeFileSync26(tmp, content3, "utf8");
80651
- renameSync10(tmp, path2);
80991
+ writeFileSync27(tmp, content3, "utf8");
80992
+ renameSync11(tmp, path2);
80652
80993
  }
80653
80994
  function serializeSessionModel(rec) {
80654
80995
  return `${JSON.stringify({
@@ -80663,28 +81004,28 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
80663
81004
  throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
80664
81005
  }
80665
81006
  try {
80666
- rmSync4(join35(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
81007
+ rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
80667
81008
  } catch {}
80668
- atomicWrite(join35(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
81009
+ atomicWrite(join36(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
80669
81010
  }
80670
81011
  function readSessionModelFileRaw(agentDir) {
80671
81012
  try {
80672
- return readFileSync29(join35(agentDir, SESSION_MODEL_FILE), "utf8");
81013
+ return readFileSync30(join36(agentDir, SESSION_MODEL_FILE), "utf8");
80673
81014
  } catch {
80674
81015
  return null;
80675
81016
  }
80676
81017
  }
80677
81018
  function clearSessionModelFile(agentDir) {
80678
81019
  try {
80679
- rmSync4(join35(agentDir, SESSION_MODEL_FILE), { force: true });
81020
+ rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
80680
81021
  } catch {}
80681
81022
  }
80682
81023
  function consumeSessionModelCarrierOnHealthyBoot(agentDir) {
80683
81024
  try {
80684
- rmSync4(join35(agentDir, SESSION_MODEL_FILE), { force: true });
81025
+ rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
80685
81026
  } catch {}
80686
81027
  try {
80687
- rmSync4(join35(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
81028
+ rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
80688
81029
  } catch {}
80689
81030
  }
80690
81031
  function restoreSessionModelFileRaw(agentDir, raw) {
@@ -80693,12 +81034,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
80693
81034
  return;
80694
81035
  }
80695
81036
  try {
80696
- atomicWrite(join35(agentDir, SESSION_MODEL_FILE), raw);
81037
+ atomicWrite(join36(agentDir, SESSION_MODEL_FILE), raw);
80697
81038
  } catch {}
80698
81039
  }
80699
81040
  function readConfiguredDefaultModel(agentDir) {
80700
81041
  try {
80701
- const v = readFileSync29(join35(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
81042
+ const v = readFileSync30(join36(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
80702
81043
  return v.length > 0 ? v : null;
80703
81044
  } catch {
80704
81045
  return null;
@@ -80710,12 +81051,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
80710
81051
  if (!EFFORT_LEVEL_RE.test(level)) {
80711
81052
  throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
80712
81053
  }
80713
- atomicWrite(join35(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
81054
+ atomicWrite(join36(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
80714
81055
  `);
80715
81056
  }
80716
81057
  function clearSessionEffortFile(agentDir) {
80717
81058
  try {
80718
- rmSync4(join35(agentDir, SESSION_EFFORT_FILE), { force: true });
81059
+ rmSync4(join36(agentDir, SESSION_EFFORT_FILE), { force: true });
80719
81060
  } catch {}
80720
81061
  }
80721
81062
  var PREMIUM_RECOVERY_FILE = ".premium-recovery";
@@ -80738,13 +81079,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
80738
81079
  if (clean.length === 0) {
80739
81080
  throw new Error("refusing to persist premium-recovery marker with no chats to notify");
80740
81081
  }
80741
- atomicWrite(join35(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
81082
+ atomicWrite(join36(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
80742
81083
  `);
80743
81084
  }
80744
81085
  function readPremiumRecoveryFile(agentDir) {
80745
81086
  let raw;
80746
81087
  try {
80747
- raw = readFileSync29(join35(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
81088
+ raw = readFileSync30(join36(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
80748
81089
  } catch {
80749
81090
  return null;
80750
81091
  }
@@ -80757,7 +81098,7 @@ function readPremiumRecoveryFile(agentDir) {
80757
81098
  }
80758
81099
  function clearPremiumRecoveryFile(agentDir) {
80759
81100
  try {
80760
- rmSync4(join35(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
81101
+ rmSync4(join36(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
80761
81102
  } catch {}
80762
81103
  }
80763
81104
 
@@ -81046,7 +81387,7 @@ async function discoverModels(agentName3, opts = {}) {
81046
81387
  }
81047
81388
 
81048
81389
  // ../src/agents/scaffold.ts
81049
- import { dirname as dirname14, isAbsolute, join as join38, relative, resolve as resolve8 } from "node:path";
81390
+ import { dirname as dirname14, isAbsolute, join as join39, relative, resolve as resolve8 } from "node:path";
81050
81391
  init_atomic();
81051
81392
 
81052
81393
  // ../src/agents/agent-uid.ts
@@ -81063,8 +81404,8 @@ init_merge();
81063
81404
  init_timezone();
81064
81405
 
81065
81406
  // ../src/cli/agent-config.ts
81066
- import { join as join36 } from "node:path";
81067
- import { homedir as homedir11 } from "node:os";
81407
+ import { join as join37 } from "node:path";
81408
+ import { homedir as homedir12 } from "node:os";
81068
81409
 
81069
81410
  // ../src/cli/helpers.ts
81070
81411
  init_loader();
@@ -81084,12 +81425,12 @@ var WEBKITE_VAULT_KEYS = new Set([
81084
81425
  init_overlay_loader();
81085
81426
 
81086
81427
  // ../src/cli/agent-config.ts
81087
- var AUDIT_ROOT = join36(homedir11(), ".switchroom", "audit");
81428
+ var AUDIT_ROOT = join37(homedir12(), ".switchroom", "audit");
81088
81429
 
81089
81430
  // ../src/agents/profiles.ts
81090
81431
  var import_handlebars = __toESM(require_lib(), 1);
81091
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync27, existsSync as existsSync31, readdirSync as readdirSync6, statSync as statSync11, copyFileSync, mkdirSync as mkdirSync29, realpathSync as realpathSync2 } from "node:fs";
81092
- import { resolve as resolve7, join as join37, sep as pathSep } from "node:path";
81432
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync28, existsSync as existsSync32, readdirSync as readdirSync7, statSync as statSync12, copyFileSync, mkdirSync as mkdirSync30, realpathSync as realpathSync2 } from "node:fs";
81433
+ import { resolve as resolve7, join as join38, sep as pathSep } from "node:path";
81093
81434
  function resolveProfilesRoot() {
81094
81435
  const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
81095
81436
  if (envOverride) {
@@ -81100,7 +81441,7 @@ function resolveProfilesRoot() {
81100
81441
  resolve7(import.meta.dirname, "profiles")
81101
81442
  ];
81102
81443
  for (const candidate of candidates) {
81103
- if (existsSync31(candidate)) {
81444
+ if (existsSync32(candidate)) {
81104
81445
  return candidate;
81105
81446
  }
81106
81447
  }
@@ -81116,9 +81457,9 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
81116
81457
  var SHARED_FRAGMENTS_DIR = resolve7(PROFILES_ROOT, "_shared");
81117
81458
  var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
81118
81459
  for (const name of SHARED_FRAGMENTS) {
81119
- const fragPath = join37(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
81120
- if (existsSync31(fragPath)) {
81121
- import_handlebars.default.registerPartial(name, readFileSync30(fragPath, "utf-8"));
81460
+ const fragPath = join38(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
81461
+ if (existsSync32(fragPath)) {
81462
+ import_handlebars.default.registerPartial(name, readFileSync31(fragPath, "utf-8"));
81122
81463
  }
81123
81464
  }
81124
81465
 
@@ -81725,8 +82066,8 @@ init_overlay_loader();
81725
82066
  init_merge();
81726
82067
  init_timezone();
81727
82068
  var import_yaml4 = __toESM(require_dist(), 1);
81728
- import { readFileSync as readFileSync31, existsSync as existsSync32 } from "node:fs";
81729
- import { homedir as homedir12 } from "node:os";
82069
+ import { readFileSync as readFileSync32, existsSync as existsSync33 } from "node:fs";
82070
+ import { homedir as homedir13 } from "node:os";
81730
82071
  import { resolve as resolve9 } from "node:path";
81731
82072
 
81732
82073
  class ConfigError2 extends Error {
@@ -81783,7 +82124,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
81783
82124
  }
81784
82125
  function findConfigFile2(startDir) {
81785
82126
  const envPath = process.env.SWITCHROOM_CONFIG;
81786
- const home2 = homedir12();
82127
+ const home2 = homedir13();
81787
82128
  const userDir = resolve9(home2, ".switchroom");
81788
82129
  const searchPaths = [
81789
82130
  envPath ? resolve9(envPath) : null,
@@ -81801,7 +82142,7 @@ function findConfigFile2(startDir) {
81801
82142
  resolve9(userDir, "clerk.yml")
81802
82143
  ].filter(Boolean);
81803
82144
  for (const path2 of searchPaths) {
81804
- if (existsSync32(path2)) {
82145
+ if (existsSync33(path2)) {
81805
82146
  return path2;
81806
82147
  }
81807
82148
  }
@@ -81809,12 +82150,12 @@ function findConfigFile2(startDir) {
81809
82150
  }
81810
82151
  function loadConfig2(configPath) {
81811
82152
  const filePath = configPath ?? findConfigFile2();
81812
- if (!existsSync32(filePath)) {
82153
+ if (!existsSync33(filePath)) {
81813
82154
  throw new ConfigError2(`Config file not found: ${filePath}`);
81814
82155
  }
81815
82156
  let raw;
81816
82157
  try {
81817
- raw = readFileSync31(filePath, "utf-8");
82158
+ raw = readFileSync32(filePath, "utf-8");
81818
82159
  } catch (err) {
81819
82160
  throw new ConfigError2(`Failed to read config file: ${filePath}`, [
81820
82161
  ` ${err.message}`
@@ -82344,15 +82685,15 @@ function topicForRecipient(args) {
82344
82685
  }
82345
82686
 
82346
82687
  // ../src/agents/perf.ts
82347
- import { existsSync as existsSync33, readFileSync as readFileSync32 } from "node:fs";
82688
+ import { existsSync as existsSync34, readFileSync as readFileSync33 } from "node:fs";
82348
82689
  function readTurnUsages(jsonlPath, lastN) {
82349
- if (!existsSync33(jsonlPath))
82690
+ if (!existsSync34(jsonlPath))
82350
82691
  return [];
82351
82692
  if (lastN <= 0)
82352
82693
  return [];
82353
82694
  let raw;
82354
82695
  try {
82355
- raw = readFileSync32(jsonlPath, "utf-8");
82696
+ raw = readFileSync33(jsonlPath, "utf-8");
82356
82697
  } catch {
82357
82698
  return [];
82358
82699
  }
@@ -82443,8 +82784,8 @@ function numField(obj, key) {
82443
82784
  }
82444
82785
 
82445
82786
  // gateway/context-occupancy.ts
82446
- import { mkdirSync as mkdirSync30, writeFileSync as writeFileSync28 } from "node:fs";
82447
- import { join as join39 } from "node:path";
82787
+ import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync29 } from "node:fs";
82788
+ import { join as join40 } from "node:path";
82448
82789
  var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
82449
82790
  var TIGHT_FRACTION = 0.8;
82450
82791
  function buildContextOccupancy(occupancy, cap, now) {
@@ -82467,9 +82808,9 @@ function buildContextOccupancy(occupancy, cap, now) {
82467
82808
  }
82468
82809
  function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
82469
82810
  try {
82470
- const path2 = join39(stateDir, CONTEXT_OCCUPANCY_FILENAME);
82471
- (deps?.mkdir ?? ((p, o) => mkdirSync30(p, o)))(stateDir, { recursive: true });
82472
- (deps?.writeFile ?? ((p, d) => writeFileSync28(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
82811
+ const path2 = join40(stateDir, CONTEXT_OCCUPANCY_FILENAME);
82812
+ (deps?.mkdir ?? ((p, o) => mkdirSync31(p, o)))(stateDir, { recursive: true });
82813
+ (deps?.writeFile ?? ((p, d) => writeFileSync29(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
82473
82814
  `);
82474
82815
  } catch {}
82475
82816
  }
@@ -82623,7 +82964,7 @@ function nextCompactNotify(state6, ev) {
82623
82964
  }
82624
82965
 
82625
82966
  // gateway/hostd-dispatch.ts
82626
- import { existsSync as existsSync34 } from "node:fs";
82967
+ import { existsSync as existsSync35 } from "node:fs";
82627
82968
  import { randomBytes as randomBytes7 } from "node:crypto";
82628
82969
  init_loader();
82629
82970
  var _hostdEnabled2;
@@ -82647,13 +82988,13 @@ function hostdSocketPath2(agentName3) {
82647
82988
  function hostdWillBeUsed2(agentName3) {
82648
82989
  if (!isHostdEnabled2())
82649
82990
  return false;
82650
- return existsSync34(hostdSocketPath2(agentName3));
82991
+ return existsSync35(hostdSocketPath2(agentName3));
82651
82992
  }
82652
82993
  async function tryHostdDispatch2(agentName3, req, timeoutMs = 5000) {
82653
82994
  if (!isHostdEnabled2())
82654
82995
  return "not-configured";
82655
82996
  const sockPath = hostdSocketPath2(agentName3);
82656
- if (!existsSync34(sockPath))
82997
+ if (!existsSync35(sockPath))
82657
82998
  return "not-configured";
82658
82999
  try {
82659
83000
  return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
@@ -82682,7 +83023,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
82682
83023
  if (!isHostdEnabled2())
82683
83024
  return "not-configured";
82684
83025
  const sockPath = hostdSocketPath2(agentName3);
82685
- if (!existsSync34(sockPath))
83026
+ if (!existsSync35(sockPath))
82686
83027
  return "not-configured";
82687
83028
  const now = opts.now ?? Date.now;
82688
83029
  const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
@@ -82801,7 +83142,7 @@ function createDmPinSweeper(deps) {
82801
83142
 
82802
83143
  // gateway/webhook-ingest-server.ts
82803
83144
  import net4 from "node:net";
82804
- import { chmodSync as chmodSync10, existsSync as existsSync35, unlinkSync as unlinkSync16 } from "node:fs";
83145
+ import { chmodSync as chmodSync10, existsSync as existsSync36, unlinkSync as unlinkSync17 } from "node:fs";
82805
83146
  var MAX_REQUEST_BYTES = 1024 * 1024;
82806
83147
  function fdOf(conn) {
82807
83148
  const handle = conn._handle;
@@ -82813,8 +83154,8 @@ function startWebhookIngestServer(opts) {
82813
83154
  const log = opts.log ?? ((s) => process.stderr.write(s));
82814
83155
  const allowed = new Set(opts.allowedUids);
82815
83156
  try {
82816
- if (existsSync35(opts.socketPath))
82817
- unlinkSync16(opts.socketPath);
83157
+ if (existsSync36(opts.socketPath))
83158
+ unlinkSync17(opts.socketPath);
82818
83159
  } catch (err) {
82819
83160
  log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
82820
83161
  `);
@@ -82906,28 +83247,28 @@ function startWebhookIngestServer(opts) {
82906
83247
  server.close();
82907
83248
  } catch {}
82908
83249
  try {
82909
- if (existsSync35(opts.socketPath))
82910
- unlinkSync16(opts.socketPath);
83250
+ if (existsSync36(opts.socketPath))
83251
+ unlinkSync17(opts.socketPath);
82911
83252
  } catch {}
82912
83253
  }
82913
83254
  };
82914
83255
  }
82915
83256
 
82916
83257
  // ../src/web/webhook-gateway-record.ts
82917
- import { appendFileSync as appendFileSync6, mkdirSync as mkdirSync33 } from "fs";
82918
- import { join as join42 } from "path";
82919
- import { homedir as homedir14 } from "os";
83258
+ import { appendFileSync as appendFileSync7, mkdirSync as mkdirSync34 } from "fs";
83259
+ import { join as join43 } from "path";
83260
+ import { homedir as homedir15 } from "os";
82920
83261
 
82921
83262
  // ../src/web/webhook-handler.ts
82922
- import { appendFileSync as appendFileSync5, existsSync as existsSync36, mkdirSync as mkdirSync31, readFileSync as readFileSync33, writeFileSync as writeFileSync29 } from "fs";
82923
- import { join as join40 } from "path";
83263
+ import { appendFileSync as appendFileSync6, existsSync as existsSync37, mkdirSync as mkdirSync32, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "fs";
83264
+ import { join as join41 } from "path";
82924
83265
  var DEDUP_MAX = 1000;
82925
83266
  var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
82926
83267
  function loadDedupFile(path2) {
82927
83268
  try {
82928
- if (!existsSync36(path2))
83269
+ if (!existsSync37(path2))
82929
83270
  return {};
82930
- const raw = JSON.parse(readFileSync33(path2, "utf-8"));
83271
+ const raw = JSON.parse(readFileSync34(path2, "utf-8"));
82931
83272
  return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
82932
83273
  } catch {
82933
83274
  return {};
@@ -82941,7 +83282,7 @@ function saveDedupFile(path2, deliveries, now) {
82941
83282
  }
82942
83283
  const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
82943
83284
  const final = Object.fromEntries(sorted);
82944
- writeFileSync29(path2, JSON.stringify({ deliveries: final }), {
83285
+ writeFileSync30(path2, JSON.stringify({ deliveries: final }), {
82945
83286
  mode: 384
82946
83287
  });
82947
83288
  }
@@ -82949,8 +83290,8 @@ var agentDedupCache = new Map;
82949
83290
  function createFileDedupStore(resolveAgentDir) {
82950
83291
  return {
82951
83292
  check(agent, deliveryId, now) {
82952
- const telegramDir = join40(resolveAgentDir(agent), "telegram");
82953
- const filePath = join40(telegramDir, "webhook-dedup.json");
83293
+ const telegramDir = join41(resolveAgentDir(agent), "telegram");
83294
+ const filePath = join41(telegramDir, "webhook-dedup.json");
82954
83295
  if (!agentDedupCache.has(agent)) {
82955
83296
  agentDedupCache.set(agent, loadDedupFile(filePath));
82956
83297
  }
@@ -82960,7 +83301,7 @@ function createFileDedupStore(resolveAgentDir) {
82960
83301
  }
82961
83302
  deliveries[deliveryId] = now;
82962
83303
  try {
82963
- mkdirSync31(telegramDir, { recursive: true });
83304
+ mkdirSync32(telegramDir, { recursive: true });
82964
83305
  saveDedupFile(filePath, deliveries, now);
82965
83306
  } catch {}
82966
83307
  return;
@@ -82971,9 +83312,9 @@ var tokenBuckets = new Map;
82971
83312
  var throttleIssueWindow = new Map;
82972
83313
 
82973
83314
  // ../src/web/webhook-dispatch.ts
82974
- import { existsSync as existsSync37, mkdirSync as mkdirSync32, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "fs";
82975
- import { join as join41 } from "path";
82976
- import { homedir as homedir13 } from "os";
83315
+ import { existsSync as existsSync38, mkdirSync as mkdirSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
83316
+ import { join as join42 } from "path";
83317
+ import { homedir as homedir14 } from "os";
82977
83318
 
82978
83319
  // ../src/agent-scheduler/ipc-client.ts
82979
83320
  import { createConnection as createConnection2 } from "node:net";
@@ -83271,9 +83612,9 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
83271
83612
  }
83272
83613
  function loadCooldownFile(path2) {
83273
83614
  try {
83274
- if (!existsSync37(path2))
83615
+ if (!existsSync38(path2))
83275
83616
  return {};
83276
- const raw = JSON.parse(readFileSync34(path2, "utf-8"));
83617
+ const raw = JSON.parse(readFileSync35(path2, "utf-8"));
83277
83618
  return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
83278
83619
  } catch {
83279
83620
  return {};
@@ -83281,7 +83622,7 @@ function loadCooldownFile(path2) {
83281
83622
  }
83282
83623
  function saveCooldownFile(path2, dispatches) {
83283
83624
  try {
83284
- writeFileSync30(path2, JSON.stringify({ dispatches }), {
83625
+ writeFileSync31(path2, JSON.stringify({ dispatches }), {
83285
83626
  mode: 384
83286
83627
  });
83287
83628
  } catch {}
@@ -83292,8 +83633,8 @@ function createFileCooldownStore(resolveAgentDir) {
83292
83633
  isCoolingDown(agent, key, cooldownMs, now) {
83293
83634
  if (cooldownMs <= 0)
83294
83635
  return false;
83295
- const telegramDir = join41(resolveAgentDir(agent), "telegram");
83296
- const filePath = join41(telegramDir, "webhook-cooldown.json");
83636
+ const telegramDir = join42(resolveAgentDir(agent), "telegram");
83637
+ const filePath = join42(telegramDir, "webhook-cooldown.json");
83297
83638
  if (!cache.has(agent)) {
83298
83639
  cache.set(agent, loadCooldownFile(filePath));
83299
83640
  }
@@ -83304,7 +83645,7 @@ function createFileCooldownStore(resolveAgentDir) {
83304
83645
  }
83305
83646
  dispatches[key] = now;
83306
83647
  try {
83307
- mkdirSync32(telegramDir, { recursive: true });
83648
+ mkdirSync33(telegramDir, { recursive: true });
83308
83649
  saveCooldownFile(filePath, dispatches);
83309
83650
  } catch {}
83310
83651
  return false;
@@ -83350,9 +83691,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
83350
83691
  }
83351
83692
  function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
83352
83693
  const log = deps.log ?? ((s) => process.stderr.write(s));
83353
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join41(homedir13(), ".switchroom", "agents", a));
83694
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
83354
83695
  const now = (deps.now ?? Date.now)();
83355
- const socketPath = join41(resolveAgentDir(agent), "telegram", "gateway.sock");
83696
+ const socketPath = join42(resolveAgentDir(agent), "telegram", "gateway.sock");
83356
83697
  const inbound = {
83357
83698
  type: "inbound",
83358
83699
  chatId: ctx.chatId,
@@ -83424,7 +83765,7 @@ function evaluateDispatch(args, deps = {}) {
83424
83765
  const log = deps.log ?? ((s) => process.stderr.write(s));
83425
83766
  const now = (deps.now ?? Date.now)();
83426
83767
  const nowDate = deps.nowDate ?? (() => new Date(now));
83427
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join41(homedir13(), ".switchroom", "agents", a));
83768
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
83428
83769
  const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
83429
83770
  if (!DISPATCH_SOURCES.includes(args.source))
83430
83771
  return 0;
@@ -83502,10 +83843,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
83502
83843
  function recordWebhookEvent(rec, deps = {}) {
83503
83844
  const log = deps.log ?? ((s) => process.stderr.write(s));
83504
83845
  const now = rec.ts || (deps.now ?? Date.now)();
83505
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
83846
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join43(homedir15(), ".switchroom", "agents", a));
83506
83847
  const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
83507
83848
  const agent = rec.agent;
83508
- const telegramDir = join42(resolveAgentDir(agent), "telegram");
83849
+ const telegramDir = join43(resolveAgentDir(agent), "telegram");
83509
83850
  if (rec.source === "github" && rec.delivery_id) {
83510
83851
  const originalTs = dedupStore.check(agent, rec.delivery_id, now);
83511
83852
  if (originalTs !== undefined) {
@@ -83514,9 +83855,9 @@ function recordWebhookEvent(rec, deps = {}) {
83514
83855
  return { status: "deduped", ts: originalTs };
83515
83856
  }
83516
83857
  }
83517
- const logPath = join42(telegramDir, "webhook-events.jsonl");
83858
+ const logPath = join43(telegramDir, "webhook-events.jsonl");
83518
83859
  try {
83519
- mkdirSync33(telegramDir, { recursive: true });
83860
+ mkdirSync34(telegramDir, { recursive: true });
83520
83861
  const record = {
83521
83862
  ts: now,
83522
83863
  source: rec.source,
@@ -83524,7 +83865,7 @@ function recordWebhookEvent(rec, deps = {}) {
83524
83865
  rendered_text: rec.rendered_text,
83525
83866
  payload: rec.payload
83526
83867
  };
83527
- appendFileSync6(logPath, JSON.stringify(record) + `
83868
+ appendFileSync7(logPath, JSON.stringify(record) + `
83528
83869
  `, { mode: 384 });
83529
83870
  } catch (err) {
83530
83871
  log(`webhook-gateway: agent='${agent}' source='${rec.source}' write failed: ${err.message}
@@ -83626,7 +83967,7 @@ function recordWebhookEvent(rec, deps = {}) {
83626
83967
 
83627
83968
  // gateway/ipc-server.ts
83628
83969
  init_format();
83629
- import { renameSync as renameSync12, unlinkSync as unlinkSync17, chmodSync as chmodSync11 } from "fs";
83970
+ import { renameSync as renameSync13, unlinkSync as unlinkSync18, chmodSync as chmodSync11 } from "fs";
83630
83971
  var MAX_BUFFER_SIZE = 1024 * 1024;
83631
83972
  var VALID_OPERATOR_KINDS = new Set([
83632
83973
  "credentials-expired",
@@ -83835,10 +84176,10 @@ function createIpcServer(options) {
83835
84176
  heartbeatTimeoutMs = 30000
83836
84177
  } = options;
83837
84178
  try {
83838
- renameSync12(socketPath, socketPath + ".bak");
84179
+ renameSync13(socketPath, socketPath + ".bak");
83839
84180
  } catch {}
83840
84181
  try {
83841
- unlinkSync17(socketPath + ".bak");
84182
+ unlinkSync18(socketPath + ".bak");
83842
84183
  } catch {}
83843
84184
  const clients = new Set;
83844
84185
  const agentIndex = new Map;
@@ -84225,7 +84566,7 @@ function createIpcServer(options) {
84225
84566
  clientBySocketId.clear();
84226
84567
  server.stop(true);
84227
84568
  try {
84228
- renameSync12(socketPath, socketPath + ".bak");
84569
+ renameSync13(socketPath, socketPath + ".bak");
84229
84570
  } catch {}
84230
84571
  }
84231
84572
  };
@@ -84679,7 +85020,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
84679
85020
 
84680
85021
  // gateway/diff-preview-card.ts
84681
85022
  init_format();
84682
- var import_grammy12 = __toESM(require_mod2(), 1);
85023
+ var import_grammy13 = __toESM(require_mod2(), 1);
84683
85024
  var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
84684
85025
  var PENDING_FILE_ID_SENTINEL = "pending-create";
84685
85026
  function buildDiffPreviewCard(input) {
@@ -84697,7 +85038,7 @@ function buildDiffPreviewCard(input) {
84697
85038
  }
84698
85039
  const text4 = bodyLines.join(`
84699
85040
  `);
84700
- const kb = new import_grammy12.InlineKeyboard;
85041
+ const kb = new import_grammy13.InlineKeyboard;
84701
85042
  const ROW_BREAK_AFTER = [
84702
85043
  "apply_suggestion"
84703
85044
  ];
@@ -88160,27 +88501,27 @@ function skillProposalKeyboard(id) {
88160
88501
  // ../src/self-improve/skill-proposals.ts
88161
88502
  import {
88162
88503
  closeSync as closeSync7,
88163
- existsSync as existsSync38,
88164
- mkdirSync as mkdirSync34,
88504
+ existsSync as existsSync39,
88505
+ mkdirSync as mkdirSync35,
88165
88506
  openSync as openSync7,
88166
- readFileSync as readFileSync35,
88507
+ readFileSync as readFileSync36,
88167
88508
  writeSync as writeSync5
88168
88509
  } from "node:fs";
88169
- import { join as join43 } from "node:path";
88510
+ import { join as join44 } from "node:path";
88170
88511
  import { randomUUID as randomUUID5 } from "node:crypto";
88171
88512
  var PROPOSALS_FILE2 = "skill-proposals.jsonl";
88172
88513
  var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
88173
88514
  var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
88174
88515
  var PROPOSAL_SIM_THRESHOLD = 0.5;
88175
88516
  function proposalsPath2(stateDir) {
88176
- return join43(stateDir, PROPOSALS_FILE2);
88517
+ return join44(stateDir, PROPOSALS_FILE2);
88177
88518
  }
88178
88519
  function rejectedPath2(stateDir) {
88179
- return join43(stateDir, REJECTED_FILE2);
88520
+ return join44(stateDir, REJECTED_FILE2);
88180
88521
  }
88181
88522
  function ensureDir3(stateDir) {
88182
- if (!existsSync38(stateDir)) {
88183
- mkdirSync34(stateDir, { recursive: true, mode: 493 });
88523
+ if (!existsSync39(stateDir)) {
88524
+ mkdirSync35(stateDir, { recursive: true, mode: 493 });
88184
88525
  }
88185
88526
  }
88186
88527
  function appendLine2(path2, obj) {
@@ -88193,11 +88534,11 @@ function appendLine2(path2, obj) {
88193
88534
  }
88194
88535
  }
88195
88536
  function readLines2(path2, isValid2) {
88196
- if (!existsSync38(path2))
88537
+ if (!existsSync39(path2))
88197
88538
  return [];
88198
88539
  let raw;
88199
88540
  try {
88200
- raw = readFileSync35(path2, "utf-8");
88541
+ raw = readFileSync36(path2, "utf-8");
88201
88542
  } catch {
88202
88543
  return [];
88203
88544
  }
@@ -88794,15 +89135,15 @@ function escapeBody2(s) {
88794
89135
  }
88795
89136
 
88796
89137
  // gateway/pid-file.ts
88797
- import { writeFileSync as writeFileSync31, readFileSync as readFileSync36, unlinkSync as unlinkSync18, renameSync as renameSync13 } from "node:fs";
89138
+ import { writeFileSync as writeFileSync32, readFileSync as readFileSync37, unlinkSync as unlinkSync19, renameSync as renameSync14 } from "node:fs";
88798
89139
  function writePidFile(path2, record) {
88799
89140
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
88800
- writeFileSync31(tmp, JSON.stringify(record), "utf-8");
88801
- renameSync13(tmp, path2);
89141
+ writeFileSync32(tmp, JSON.stringify(record), "utf-8");
89142
+ renameSync14(tmp, path2);
88802
89143
  }
88803
89144
  function clearPidFile(path2) {
88804
89145
  try {
88805
- unlinkSync18(path2);
89146
+ unlinkSync19(path2);
88806
89147
  } catch {}
88807
89148
  }
88808
89149
 
@@ -88813,10 +89154,10 @@ import {
88813
89154
  writeFile as writeFileAsync,
88814
89155
  readFile as readFileAsync
88815
89156
  } from "node:fs/promises";
88816
- import { readFileSync as readFileSync37 } from "node:fs";
89157
+ import { readFileSync as readFileSync38 } from "node:fs";
88817
89158
  function readCurrentBootId() {
88818
89159
  try {
88819
- const stat = readFileSync37("/proc/1/stat", "utf-8");
89160
+ const stat = readFileSync38("/proc/1/stat", "utf-8");
88820
89161
  const lastParen = stat.lastIndexOf(")");
88821
89162
  if (lastParen < 0)
88822
89163
  return null;
@@ -88964,14 +89305,14 @@ var DEFAULT_LOG3 = (line) => {
88964
89305
  `) ? line : line + `
88965
89306
  `);
88966
89307
  };
88967
- var DEFAULT_SLEEP3 = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
89308
+ var DEFAULT_SLEEP4 = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
88968
89309
  var DEFAULT_NOW = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
88969
89310
  function fmtAgent2(agentName3) {
88970
89311
  return agentName3 ? ` agent=${agentName3}` : "";
88971
89312
  }
88972
89313
  async function drainShutdown(opts) {
88973
89314
  const log = opts.log ?? DEFAULT_LOG3;
88974
- const sleep2 = opts.sleep ?? DEFAULT_SLEEP3;
89315
+ const sleep2 = opts.sleep ?? DEFAULT_SLEEP4;
88975
89316
  const now = opts.now ?? DEFAULT_NOW;
88976
89317
  const budgetMs = opts.budgetMs ?? 35000;
88977
89318
  const pollIntervalMs = opts.pollIntervalMs ?? 100;
@@ -89019,15 +89360,15 @@ function safeCount(fn) {
89019
89360
  }
89020
89361
 
89021
89362
  // gateway/session-marker.ts
89022
- import { writeFileSync as writeFileSync32, readFileSync as readFileSync38, renameSync as renameSync14, unlinkSync as unlinkSync19 } from "node:fs";
89363
+ import { writeFileSync as writeFileSync33, readFileSync as readFileSync39, renameSync as renameSync15, unlinkSync as unlinkSync20 } from "node:fs";
89023
89364
  function writeSessionMarker(path2, marker) {
89024
89365
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
89025
- writeFileSync32(tmp, JSON.stringify(marker), "utf-8");
89026
- renameSync14(tmp, path2);
89366
+ writeFileSync33(tmp, JSON.stringify(marker), "utf-8");
89367
+ renameSync15(tmp, path2);
89027
89368
  }
89028
89369
  function readSessionMarker(path2) {
89029
89370
  try {
89030
- const raw = readFileSync38(path2, "utf-8");
89371
+ const raw = readFileSync39(path2, "utf-8");
89031
89372
  const parsed = JSON.parse(raw);
89032
89373
  if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
89033
89374
  return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
@@ -89049,16 +89390,16 @@ function shouldFireRestartBanner(input) {
89049
89390
  }
89050
89391
 
89051
89392
  // gateway/clean-shutdown-marker.ts
89052
- import { writeFileSync as writeFileSync33, readFileSync as readFileSync39, renameSync as renameSync15, unlinkSync as unlinkSync20 } from "node:fs";
89393
+ import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync16, unlinkSync as unlinkSync21 } from "node:fs";
89053
89394
  var DEFAULT_MAX_AGE_MS = 60000;
89054
89395
  function writeCleanShutdownMarker(path2, marker) {
89055
89396
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
89056
- writeFileSync33(tmp, JSON.stringify(marker), "utf-8");
89057
- renameSync15(tmp, path2);
89397
+ writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
89398
+ renameSync16(tmp, path2);
89058
89399
  }
89059
89400
  function readCleanShutdownMarker(path2) {
89060
89401
  try {
89061
- const raw = readFileSync39(path2, "utf-8");
89402
+ const raw = readFileSync40(path2, "utf-8");
89062
89403
  const parsed = JSON.parse(raw);
89063
89404
  if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
89064
89405
  const out = { ts: parsed.ts, signal: parsed.signal };
@@ -89073,7 +89414,7 @@ function readCleanShutdownMarker(path2) {
89073
89414
  }
89074
89415
  function clearCleanShutdownMarker(path2) {
89075
89416
  try {
89076
- unlinkSync20(path2);
89417
+ unlinkSync21(path2);
89077
89418
  } catch {}
89078
89419
  }
89079
89420
  function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
@@ -89430,16 +89771,16 @@ function classifyAdminGate(text4, myAgentName) {
89430
89771
 
89431
89772
  // subagent-watcher.ts
89432
89773
  import {
89433
- existsSync as existsSync39,
89774
+ existsSync as existsSync40,
89434
89775
  openSync as openSync8,
89435
89776
  readSync as readSync2,
89436
- statSync as statSync12,
89777
+ statSync as statSync13,
89437
89778
  closeSync as closeSync8,
89438
89779
  watch,
89439
- readdirSync as readdirSync7,
89440
- readFileSync as readFileSync40
89780
+ readdirSync as readdirSync8,
89781
+ readFileSync as readFileSync41
89441
89782
  } from "fs";
89442
- import { join as join44 } from "path";
89783
+ import { join as join45 } from "path";
89443
89784
 
89444
89785
  // session-tail.ts
89445
89786
  function sanitizeCwdToProjectName2(cwd) {
@@ -89753,104 +90094,6 @@ function redactSecrets(text4) {
89753
90094
 
89754
90095
  // subagent-watcher.ts
89755
90096
  init_card_format();
89756
-
89757
- // registry/subagents-schema.ts
89758
- function countRunningBackgroundSubagents(db3) {
89759
- const row = db3.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
89760
- return row?.n ?? 0;
89761
- }
89762
- function recordSubagentEnd(db3, args) {
89763
- db3.prepare(`
89764
- UPDATE subagents
89765
- SET
89766
- ended_at = ?,
89767
- status = ?,
89768
- result_summary = COALESCE(?, result_summary)
89769
- WHERE id = ?
89770
- AND status NOT IN ('completed', 'failed')
89771
- `).run(args.endedAt, args.status, args.resultSummary ?? null, args.id);
89772
- }
89773
- function recordSubagentStall(db3, args) {
89774
- args.stalledAt;
89775
- if (args.reason !== undefined) {
89776
- db3.prepare(`
89777
- UPDATE subagents
89778
- SET status = 'stalled',
89779
- result_summary = ?
89780
- WHERE id = ?
89781
- AND status NOT IN ('completed', 'failed')
89782
- `).run(args.reason, args.id);
89783
- return;
89784
- }
89785
- db3.prepare(`
89786
- UPDATE subagents
89787
- SET status = 'stalled'
89788
- WHERE id = ?
89789
- AND status NOT IN ('completed', 'failed')
89790
- `).run(args.id);
89791
- }
89792
- function reapStuckRunningRows(db3, args) {
89793
- const cutoff = args.now - args.ttlMs;
89794
- const rows = db3.prepare(`
89795
- SELECT id, jsonl_agent_id FROM subagents
89796
- WHERE status = 'running'
89797
- AND background = 1
89798
- AND COALESCE(last_activity_at, started_at) < ?
89799
- `).all(cutoff);
89800
- const candidates = args.isLive ? rows.filter((r) => !args.isLive(r.jsonl_agent_id)) : rows;
89801
- for (const row of candidates) {
89802
- recordSubagentStall(db3, {
89803
- id: row.id,
89804
- stalledAt: args.now,
89805
- reason: `reaped: stuck in running past ${Math.round(args.ttlMs / 60000)}min ttl (jsonl linkage likely missing)`
89806
- });
89807
- }
89808
- return { reaped: candidates.length, ids: candidates.map((r) => r.id) };
89809
- }
89810
- function recordSubagentResume(db3, args) {
89811
- args.resumedAt;
89812
- db3.prepare(`
89813
- UPDATE subagents
89814
- SET status = 'running'
89815
- WHERE id = ?
89816
- AND status = 'stalled'
89817
- `).run(args.id);
89818
- }
89819
- function bumpSubagentActivity(db3, args) {
89820
- db3.prepare(`
89821
- UPDATE subagents
89822
- SET last_activity_at = ?
89823
- WHERE id = ?
89824
- `).run(args.ts, args.id);
89825
- }
89826
- function recordSubagentModel(db3, args) {
89827
- db3.prepare(`
89828
- UPDATE subagents
89829
- SET model = ?
89830
- WHERE id = ?
89831
- `).run(args.model, args.id);
89832
- }
89833
- function recordNestedSubagentDispatch(db3, args) {
89834
- db3.prepare(`
89835
- INSERT OR IGNORE INTO subagents
89836
- (id, parent_session_id, parent_turn_key, agent_type, description,
89837
- background, started_at, last_activity_at, status, jsonl_agent_id,
89838
- parent_agent_id)
89839
- VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, 'running', NULL, ?)
89840
- `).run(args.toolUseId, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now, args.parentJsonlAgentId);
89841
- db3.prepare(`
89842
- UPDATE subagents
89843
- SET parent_agent_id = COALESCE(parent_agent_id, ?),
89844
- parent_turn_key = COALESCE(
89845
- parent_turn_key,
89846
- (SELECT p.parent_turn_key FROM subagents p
89847
- WHERE p.jsonl_agent_id = ? LIMIT 1)
89848
- )
89849
- WHERE id = ?
89850
- `).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
89851
- }
89852
-
89853
- // subagent-watcher.ts
89854
90097
  var DEFAULT_RESCAN_MS = 1000;
89855
90098
  var DEFAULT_STALL_THRESHOLD_MS = 60000;
89856
90099
  var DEFAULT_SILENT_SYNTHESIS_STALL_THRESHOLD_MS = 300000;
@@ -89887,7 +90130,7 @@ function backfillJsonlAgentId(db3, jsonlPath, agentId, log) {
89887
90130
  const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
89888
90131
  let meta;
89889
90132
  try {
89890
- const raw = readFileSync40(metaPath, "utf8");
90133
+ const raw = readFileSync41(metaPath, "utf8");
89891
90134
  meta = JSON.parse(raw);
89892
90135
  } catch {
89893
90136
  log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
@@ -90342,9 +90585,9 @@ function startSubagentWatcher(config) {
90342
90585
  clearTimeout(ref.ref);
90343
90586
  });
90344
90587
  const fs2 = config.fs ?? {
90345
- existsSync: existsSync39,
90346
- readdirSync: readdirSync7,
90347
- statSync: statSync12,
90588
+ existsSync: existsSync40,
90589
+ readdirSync: readdirSync8,
90590
+ statSync: statSync13,
90348
90591
  openSync: openSync8,
90349
90592
  closeSync: closeSync8,
90350
90593
  readSync: readSync2,
@@ -90860,8 +91103,8 @@ function startSubagentWatcher(config) {
90860
91103
  if (stopped)
90861
91104
  return;
90862
91105
  pruneVanishedDirWatchers();
90863
- const claudeHome = join44(agentDir, ".claude");
90864
- const projectsRoot = join44(claudeHome, "projects");
91106
+ const claudeHome = join45(agentDir, ".claude");
91107
+ const projectsRoot = join45(claudeHome, "projects");
90865
91108
  if (!fs2.existsSync(projectsRoot))
90866
91109
  return;
90867
91110
  let projectDirs;
@@ -90895,7 +91138,7 @@ function startSubagentWatcher(config) {
90895
91138
  continue;
90896
91139
  }
90897
91140
  warnedForeignSlugs.delete(pDir);
90898
- const projectPath = join44(projectsRoot, pDir);
91141
+ const projectPath = join45(projectsRoot, pDir);
90899
91142
  let sessionDirs;
90900
91143
  try {
90901
91144
  sessionDirs = fs2.readdirSync(projectPath);
@@ -90905,7 +91148,7 @@ function startSubagentWatcher(config) {
90905
91148
  for (const sDir of sessionDirs) {
90906
91149
  if (sDir.endsWith(".jsonl"))
90907
91150
  continue;
90908
- const subagentsPath = join44(projectPath, sDir, "subagents");
91151
+ const subagentsPath = join45(projectPath, sDir, "subagents");
90909
91152
  if (!fs2.existsSync(subagentsPath))
90910
91153
  continue;
90911
91154
  const watchAndScan = (dirPath) => {
@@ -90914,7 +91157,7 @@ function startSubagentWatcher(config) {
90914
91157
  const w = fs2.watch(dirPath, (_event, filename) => {
90915
91158
  if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
90916
91159
  return;
90917
- const filePath = join44(dirPath, filename.toString());
91160
+ const filePath = join45(dirPath, filename.toString());
90918
91161
  if (!knownFiles.has(filePath)) {
90919
91162
  scanSubagentsDir(dirPath);
90920
91163
  }
@@ -90928,7 +91171,7 @@ function startSubagentWatcher(config) {
90928
91171
  scanSubagentsDir(dirPath);
90929
91172
  };
90930
91173
  watchAndScan(subagentsPath);
90931
- const workflowsPath = join44(subagentsPath, "workflows");
91174
+ const workflowsPath = join45(subagentsPath, "workflows");
90932
91175
  if (fs2.existsSync(workflowsPath)) {
90933
91176
  let wfDirs;
90934
91177
  try {
@@ -90938,7 +91181,7 @@ function startSubagentWatcher(config) {
90938
91181
  }
90939
91182
  for (const wfDir of wfDirs) {
90940
91183
  try {
90941
- const wfPath = join44(workflowsPath, wfDir);
91184
+ const wfPath = join45(workflowsPath, wfDir);
90942
91185
  if (!fs2.statSync(wfPath).isDirectory())
90943
91186
  continue;
90944
91187
  watchAndScan(wfPath);
@@ -90958,7 +91201,7 @@ function startSubagentWatcher(config) {
90958
91201
  for (const e of entries) {
90959
91202
  if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
90960
91203
  continue;
90961
- const filePath = join44(subagentsPath, e);
91204
+ const filePath = join45(subagentsPath, e);
90962
91205
  if (knownFiles.has(filePath))
90963
91206
  continue;
90964
91207
  const agentId = e.slice("agent-".length, -".jsonl".length);
@@ -91086,37 +91329,37 @@ function startSubagentWatcher(config) {
91086
91329
 
91087
91330
  // ../src/worktree/registry.ts
91088
91331
  import {
91089
- mkdirSync as mkdirSync35,
91090
- writeFileSync as writeFileSync34,
91091
- readFileSync as readFileSync41,
91092
- readdirSync as readdirSync8,
91093
- unlinkSync as unlinkSync21,
91094
- existsSync as existsSync40,
91095
- renameSync as renameSync16
91332
+ mkdirSync as mkdirSync36,
91333
+ writeFileSync as writeFileSync35,
91334
+ readFileSync as readFileSync42,
91335
+ readdirSync as readdirSync9,
91336
+ unlinkSync as unlinkSync22,
91337
+ existsSync as existsSync41,
91338
+ renameSync as renameSync17
91096
91339
  } from "node:fs";
91097
- import { join as join45, resolve as resolve10 } from "node:path";
91098
- import { homedir as homedir15 } from "node:os";
91340
+ import { join as join46, resolve as resolve10 } from "node:path";
91341
+ import { homedir as homedir16 } from "node:os";
91099
91342
  function registryDir() {
91100
- return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join45(homedir15(), ".switchroom", "worktrees"));
91343
+ return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join46(homedir16(), ".switchroom", "worktrees"));
91101
91344
  }
91102
91345
  function recordPath(id) {
91103
- return join45(registryDir(), `${id}.json`);
91346
+ return join46(registryDir(), `${id}.json`);
91104
91347
  }
91105
91348
  function ensureDir4() {
91106
- mkdirSync35(registryDir(), { recursive: true });
91349
+ mkdirSync36(registryDir(), { recursive: true });
91107
91350
  }
91108
91351
  function writeRecord(record) {
91109
91352
  ensureDir4();
91110
91353
  const target = recordPath(record.id);
91111
91354
  const tmp = `${target}.tmp${process.pid}`;
91112
- writeFileSync34(tmp, JSON.stringify(record, null, 2) + `
91355
+ writeFileSync35(tmp, JSON.stringify(record, null, 2) + `
91113
91356
  `, { mode: 384 });
91114
- renameSync16(tmp, target);
91357
+ renameSync17(tmp, target);
91115
91358
  }
91116
91359
  function readRecord(id) {
91117
91360
  const path2 = recordPath(id);
91118
91361
  try {
91119
- const raw = readFileSync41(path2, "utf8");
91362
+ const raw = readFileSync42(path2, "utf8");
91120
91363
  return JSON.parse(raw);
91121
91364
  } catch {
91122
91365
  return null;
@@ -91126,7 +91369,7 @@ function listRecords() {
91126
91369
  ensureDir4();
91127
91370
  const dir = registryDir();
91128
91371
  const records = [];
91129
- for (const entry of readdirSync8(dir)) {
91372
+ for (const entry of readdirSync9(dir)) {
91130
91373
  if (!entry.endsWith(".json"))
91131
91374
  continue;
91132
91375
  const id = entry.slice(0, -5);
@@ -91146,7 +91389,7 @@ function touchHeartbeat(id, onAfterRead) {
91146
91389
  writeRecord({ ...rec, heartbeatAt: new Date().toISOString() });
91147
91390
  }
91148
91391
  function recordExists(id) {
91149
- return existsSync40(recordPath(id));
91392
+ return existsSync41(recordPath(id));
91150
91393
  }
91151
91394
 
91152
91395
  // worktree-watch-cwds.ts
@@ -91278,15 +91521,15 @@ function determineRestartReason(opts) {
91278
91521
  init_boot_card();
91279
91522
 
91280
91523
  // gateway/update-announce.ts
91281
- import { existsSync as existsSync45, mkdirSync as mkdirSync39, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync48 } from "node:fs";
91282
- import { join as join50 } from "node:path";
91283
- import { homedir as homedir17 } from "node:os";
91524
+ import { existsSync as existsSync46, mkdirSync as mkdirSync40, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync49 } from "node:fs";
91525
+ import { join as join51 } from "node:path";
91526
+ import { homedir as homedir18 } from "node:os";
91284
91527
 
91285
91528
  // ../src/host-control/audit-reader.ts
91286
- import { homedir as homedir16 } from "node:os";
91287
- import { join as join49 } from "node:path";
91288
- function defaultAuditLogPath(home2 = homedir16()) {
91289
- return join49(home2, ".switchroom", "host-control-audit.log");
91529
+ import { homedir as homedir17 } from "node:os";
91530
+ import { join as join50 } from "node:path";
91531
+ function defaultAuditLogPath(home2 = homedir17()) {
91532
+ return join50(home2, ".switchroom", "host-control-audit.log");
91290
91533
  }
91291
91534
  function parseAuditLine(line) {
91292
91535
  const trimmed = line.trim();
@@ -91409,8 +91652,8 @@ function readAndFilter(raw, filters, limit) {
91409
91652
  var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
91410
91653
  function readLastTerminalUpdateAudit(opts = {}) {
91411
91654
  const path2 = opts.auditLogPath ?? defaultAuditLogPath();
91412
- const exists = opts.exists ?? existsSync45;
91413
- const readFile = opts.readFile ?? ((p) => readFileSync48(p, "utf-8"));
91655
+ const exists = opts.exists ?? existsSync46;
91656
+ const readFile = opts.readFile ?? ((p) => readFileSync49(p, "utf-8"));
91414
91657
  if (!exists(path2))
91415
91658
  return null;
91416
91659
  let raw;
@@ -91471,15 +91714,15 @@ function renderUpdateOutcomeLine(entry) {
91471
91714
  `);
91472
91715
  }
91473
91716
  function claimUpdateAnnouncement(requestId, opts = {}) {
91474
- const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join50(homedir17(), ".switchroom");
91475
- const dir = join50(stateDir, "update-announced");
91717
+ const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join51(homedir18(), ".switchroom");
91718
+ const dir = join51(stateDir, "update-announced");
91476
91719
  try {
91477
- mkdirSync39(dir, { recursive: true });
91720
+ mkdirSync40(dir, { recursive: true });
91478
91721
  } catch {
91479
91722
  return false;
91480
91723
  }
91481
91724
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
91482
- const path2 = join50(dir, safeId);
91725
+ const path2 = join51(dir, safeId);
91483
91726
  try {
91484
91727
  const fd = openSync9(path2, "wx");
91485
91728
  closeSync9(fd);
@@ -91499,7 +91742,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
91499
91742
 
91500
91743
  // issues-card.ts
91501
91744
  init_card_format();
91502
- import { readFileSync as readFileSync49, writeFileSync as writeFileSync39 } from "node:fs";
91745
+ import { readFileSync as readFileSync50, writeFileSync as writeFileSync40 } from "node:fs";
91503
91746
  var SEVERITY_EMOJI = {
91504
91747
  info: "\u2139\ufe0f",
91505
91748
  warn: "\u26a0\ufe0f",
@@ -91591,7 +91834,7 @@ function extractRetryAfterSecs2(err) {
91591
91834
  var COOLDOWN_JITTER_MS2 = 500;
91592
91835
  function readPersistedMessageId(path2, log) {
91593
91836
  try {
91594
- const raw = readFileSync49(path2, "utf8");
91837
+ const raw = readFileSync50(path2, "utf8");
91595
91838
  const parsed = JSON.parse(raw);
91596
91839
  const v = parsed.messageId;
91597
91840
  if (typeof v === "number" && Number.isInteger(v) && v > 0)
@@ -91607,7 +91850,7 @@ function readPersistedMessageId(path2, log) {
91607
91850
  }
91608
91851
  function writePersistedMessageId(path2, messageId, log) {
91609
91852
  try {
91610
- writeFileSync39(path2, JSON.stringify({ messageId }) + `
91853
+ writeFileSync40(path2, JSON.stringify({ messageId }) + `
91611
91854
  `, { mode: 384 });
91612
91855
  } catch (err) {
91613
91856
  log(`issues-card: persist write failed (${err.message})`);
@@ -91700,24 +91943,24 @@ function createIssuesCardHandle(opts) {
91700
91943
  }
91701
91944
 
91702
91945
  // issues-watcher.ts
91703
- import { existsSync as existsSync47, statSync as statSync15 } from "node:fs";
91704
- import { join as join52 } from "node:path";
91946
+ import { existsSync as existsSync48, statSync as statSync16 } from "node:fs";
91947
+ import { join as join53 } from "node:path";
91705
91948
 
91706
91949
  // ../src/issues/store.ts
91707
91950
  import {
91708
91951
  closeSync as closeSync10,
91709
- existsSync as existsSync46,
91710
- mkdirSync as mkdirSync40,
91952
+ existsSync as existsSync47,
91953
+ mkdirSync as mkdirSync41,
91711
91954
  openSync as openSync10,
91712
- readdirSync as readdirSync10,
91713
- readFileSync as readFileSync50,
91714
- renameSync as renameSync19,
91715
- statSync as statSync14,
91716
- unlinkSync as unlinkSync22,
91717
- writeFileSync as writeFileSync40,
91955
+ readdirSync as readdirSync11,
91956
+ readFileSync as readFileSync51,
91957
+ renameSync as renameSync20,
91958
+ statSync as statSync15,
91959
+ unlinkSync as unlinkSync23,
91960
+ writeFileSync as writeFileSync41,
91718
91961
  writeSync as writeSync6
91719
91962
  } from "node:fs";
91720
- import { join as join51 } from "node:path";
91963
+ import { join as join52 } from "node:path";
91721
91964
  import { randomBytes as randomBytes8 } from "node:crypto";
91722
91965
  import { execSync } from "node:child_process";
91723
91966
 
@@ -91736,12 +91979,12 @@ init_redact();
91736
91979
  var ISSUES_FILE = "issues.jsonl";
91737
91980
  var ISSUES_LOCK = "issues.lock";
91738
91981
  function readAll(stateDir) {
91739
- const path2 = join51(stateDir, ISSUES_FILE);
91740
- if (!existsSync46(path2))
91982
+ const path2 = join52(stateDir, ISSUES_FILE);
91983
+ if (!existsSync47(path2))
91741
91984
  return [];
91742
91985
  let raw;
91743
91986
  try {
91744
- raw = readFileSync50(path2, "utf-8");
91987
+ raw = readFileSync51(path2, "utf-8");
91745
91988
  } catch {
91746
91989
  return [];
91747
91990
  }
@@ -91773,7 +92016,7 @@ function list2(stateDir, opts = {}) {
91773
92016
  });
91774
92017
  }
91775
92018
  function resolve11(stateDir, fingerprint, nowFn = Date.now) {
91776
- if (!existsSync46(join51(stateDir, ISSUES_FILE)))
92019
+ if (!existsSync47(join52(stateDir, ISSUES_FILE)))
91777
92020
  return 0;
91778
92021
  return withLock(stateDir, () => {
91779
92022
  const all2 = readAll(stateDir);
@@ -91791,21 +92034,21 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
91791
92034
  });
91792
92035
  }
91793
92036
  function writeAll(stateDir, events) {
91794
- const path2 = join51(stateDir, ISSUES_FILE);
92037
+ const path2 = join52(stateDir, ISSUES_FILE);
91795
92038
  sweepOrphanTmpFiles(stateDir);
91796
92039
  const tmp = `${path2}.tmp-${process.pid}-${randomBytes8(4).toString("hex")}`;
91797
92040
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
91798
92041
  `) + `
91799
92042
  `;
91800
- writeFileSync40(tmp, body, "utf-8");
91801
- renameSync19(tmp, path2);
92043
+ writeFileSync41(tmp, body, "utf-8");
92044
+ renameSync20(tmp, path2);
91802
92045
  }
91803
92046
  var ORPHAN_TMP_TTL_MS = 60000;
91804
92047
  var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
91805
92048
  function sweepOrphanTmpFiles(stateDir) {
91806
92049
  let entries;
91807
92050
  try {
91808
- entries = readdirSync10(stateDir);
92051
+ entries = readdirSync11(stateDir);
91809
92052
  } catch {
91810
92053
  return;
91811
92054
  }
@@ -91813,11 +92056,11 @@ function sweepOrphanTmpFiles(stateDir) {
91813
92056
  for (const entry of entries) {
91814
92057
  if (!entry.startsWith(TMP_PREFIX))
91815
92058
  continue;
91816
- const tmpPath2 = join51(stateDir, entry);
92059
+ const tmpPath2 = join52(stateDir, entry);
91817
92060
  try {
91818
- const stat = statSync14(tmpPath2);
92061
+ const stat = statSync15(tmpPath2);
91819
92062
  if (stat.mtimeMs < cutoff) {
91820
- unlinkSync22(tmpPath2);
92063
+ unlinkSync23(tmpPath2);
91821
92064
  }
91822
92065
  } catch {}
91823
92066
  }
@@ -91825,7 +92068,7 @@ function sweepOrphanTmpFiles(stateDir) {
91825
92068
  var LOCK_RETRY_MS = 25;
91826
92069
  var LOCK_TIMEOUT_MS = 1e4;
91827
92070
  function withLock(stateDir, fn) {
91828
- const lockPath = join51(stateDir, ISSUES_LOCK);
92071
+ const lockPath = join52(stateDir, ISSUES_LOCK);
91829
92072
  const startedAt = Date.now();
91830
92073
  let fd = null;
91831
92074
  while (fd === null) {
@@ -91853,27 +92096,27 @@ function withLock(stateDir, fn) {
91853
92096
  closeSync10(fd);
91854
92097
  } catch {}
91855
92098
  try {
91856
- unlinkSync22(lockPath);
92099
+ unlinkSync23(lockPath);
91857
92100
  } catch {}
91858
92101
  }
91859
92102
  }
91860
92103
  function tryStealStaleLock(lockPath) {
91861
92104
  let pidStr;
91862
92105
  try {
91863
- pidStr = readFileSync50(lockPath, "utf-8").trim();
92106
+ pidStr = readFileSync51(lockPath, "utf-8").trim();
91864
92107
  } catch {
91865
92108
  return true;
91866
92109
  }
91867
92110
  const pid = Number(pidStr);
91868
92111
  if (!Number.isFinite(pid) || pid <= 0) {
91869
92112
  try {
91870
- unlinkSync22(lockPath);
92113
+ unlinkSync23(lockPath);
91871
92114
  } catch {}
91872
92115
  return true;
91873
92116
  }
91874
92117
  if (pid === process.pid) {
91875
92118
  try {
91876
- unlinkSync22(lockPath);
92119
+ unlinkSync23(lockPath);
91877
92120
  } catch {}
91878
92121
  return true;
91879
92122
  }
@@ -91888,7 +92131,7 @@ function tryStealStaleLock(lockPath) {
91888
92131
  return false;
91889
92132
  }
91890
92133
  try {
91891
- unlinkSync22(lockPath);
92134
+ unlinkSync23(lockPath);
91892
92135
  } catch {}
91893
92136
  return true;
91894
92137
  }
@@ -91910,7 +92153,7 @@ function isIssueEvent(v) {
91910
92153
  // issues-watcher.ts
91911
92154
  var DEFAULT_POLL_INTERVAL_MS2 = 2000;
91912
92155
  function startIssuesWatcher(opts) {
91913
- const path2 = join52(opts.stateDir, ISSUES_FILE);
92156
+ const path2 = join53(opts.stateDir, ISSUES_FILE);
91914
92157
  const log = opts.log ?? (() => {});
91915
92158
  const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
91916
92159
  const setIntervalFn = opts.setInterval ?? setInterval;
@@ -91958,10 +92201,10 @@ function startIssuesWatcher(opts) {
91958
92201
  };
91959
92202
  }
91960
92203
  function defaultSignatureProvider(path2) {
91961
- if (!existsSync47(path2))
92204
+ if (!existsSync48(path2))
91962
92205
  return null;
91963
92206
  try {
91964
- const stat = statSync15(path2);
92207
+ const stat = statSync16(path2);
91965
92208
  return `${stat.mtimeMs}:${stat.size}`;
91966
92209
  } catch {
91967
92210
  return null;
@@ -92669,8 +92912,8 @@ function readBashCommand(inputPreview) {
92669
92912
  }
92670
92913
 
92671
92914
  // gateway/scoped-grant-store.ts
92672
- import { readFileSync as readFileSync51, writeFileSync as writeFileSync41 } from "node:fs";
92673
- import { join as join53 } from "node:path";
92915
+ import { readFileSync as readFileSync52, writeFileSync as writeFileSync42 } from "node:fs";
92916
+ import { join as join54 } from "node:path";
92674
92917
 
92675
92918
  // scoped-approval.ts
92676
92919
  var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
@@ -92712,11 +92955,11 @@ function scopedGrantPersistEnabled(env = process.env) {
92712
92955
  return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
92713
92956
  }
92714
92957
  function createScopedGrantStore(stateDir, env = process.env) {
92715
- const filePath = join53(stateDir, "scoped-grants.json");
92716
- const enabled8 = scopedGrantPersistEnabled(env);
92958
+ const filePath = join54(stateDir, "scoped-grants.json");
92959
+ const enabled7 = scopedGrantPersistEnabled(env);
92717
92960
  function read() {
92718
92961
  try {
92719
- const raw = readFileSync51(filePath, "utf-8");
92962
+ const raw = readFileSync52(filePath, "utf-8");
92720
92963
  const parsed = JSON.parse(raw);
92721
92964
  return Array.isArray(parsed) ? parsed : [];
92722
92965
  } catch {
@@ -92724,17 +92967,17 @@ function createScopedGrantStore(stateDir, env = process.env) {
92724
92967
  }
92725
92968
  }
92726
92969
  return {
92727
- enabled: enabled8,
92970
+ enabled: enabled7,
92728
92971
  load(now) {
92729
- if (!enabled8)
92972
+ if (!enabled7)
92730
92973
  return new Map;
92731
92974
  return deserializeScopedGrants(read(), now);
92732
92975
  },
92733
92976
  save(store2) {
92734
- if (!enabled8)
92977
+ if (!enabled7)
92735
92978
  return;
92736
92979
  try {
92737
- writeFileSync41(filePath, JSON.stringify(serializeScopedGrants(store2)), {
92980
+ writeFileSync42(filePath, JSON.stringify(serializeScopedGrants(store2)), {
92738
92981
  encoding: "utf-8",
92739
92982
  mode: 384
92740
92983
  });
@@ -93085,8 +93328,8 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
93085
93328
 
93086
93329
  // credits-watch.ts
93087
93330
  init_card_format();
93088
- import { readFileSync as readFileSync52, writeFileSync as writeFileSync42, existsSync as existsSync48, mkdirSync as mkdirSync41 } from "fs";
93089
- import { join as join54 } from "path";
93331
+ import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync49, mkdirSync as mkdirSync42 } from "fs";
93332
+ import { join as join55 } from "path";
93090
93333
  var STATE_FILE = "credits-watch.json";
93091
93334
  var DEFAULT_CREDIT_FATAL_REASONS = new Set;
93092
93335
  var KNOWN_CREDIT_REASONS = [
@@ -93108,12 +93351,12 @@ function emptyCreditState() {
93108
93351
  return { lastNotifiedReason: null, lastNotifiedAt: 0 };
93109
93352
  }
93110
93353
  function readClaudeJsonOverage(claudeConfigDir) {
93111
- const path2 = join54(claudeConfigDir, ".claude.json");
93112
- if (!existsSync48(path2))
93354
+ const path2 = join55(claudeConfigDir, ".claude.json");
93355
+ if (!existsSync49(path2))
93113
93356
  return null;
93114
93357
  let raw;
93115
93358
  try {
93116
- raw = readFileSync52(path2, "utf-8");
93359
+ raw = readFileSync53(path2, "utf-8");
93117
93360
  } catch {
93118
93361
  return null;
93119
93362
  }
@@ -93191,11 +93434,11 @@ function humanizeReason(reason) {
93191
93434
  }
93192
93435
  }
93193
93436
  function loadCreditState(stateDir) {
93194
- const path2 = join54(stateDir, STATE_FILE);
93195
- if (!existsSync48(path2))
93437
+ const path2 = join55(stateDir, STATE_FILE);
93438
+ if (!existsSync49(path2))
93196
93439
  return emptyCreditState();
93197
93440
  try {
93198
- const raw = readFileSync52(path2, "utf-8");
93441
+ const raw = readFileSync53(path2, "utf-8");
93199
93442
  const parsed = JSON.parse(raw);
93200
93443
  if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
93201
93444
  return {
@@ -93207,17 +93450,17 @@ function loadCreditState(stateDir) {
93207
93450
  return emptyCreditState();
93208
93451
  }
93209
93452
  function saveCreditState(stateDir, state7) {
93210
- mkdirSync41(stateDir, { recursive: true });
93211
- const path2 = join54(stateDir, STATE_FILE);
93212
- writeFileSync42(path2, JSON.stringify(state7, null, 2) + `
93453
+ mkdirSync42(stateDir, { recursive: true });
93454
+ const path2 = join55(stateDir, STATE_FILE);
93455
+ writeFileSync43(path2, JSON.stringify(state7, null, 2) + `
93213
93456
  `, { mode: 384 });
93214
93457
  }
93215
93458
 
93216
93459
  // quota-watch.ts
93217
93460
  init_auth_snapshot_format();
93218
93461
  init_card_format();
93219
- import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync49, mkdirSync as mkdirSync42 } from "fs";
93220
- import { join as join55 } from "path";
93462
+ import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync50, mkdirSync as mkdirSync43 } from "fs";
93463
+ import { join as join56 } from "path";
93221
93464
  var STATE_FILE2 = "quota-watch.json";
93222
93465
  function emptyQuotaWatchState() {
93223
93466
  return {};
@@ -93455,11 +93698,11 @@ function buildRecoveryMessage(agentName3, snap) {
93455
93698
  `);
93456
93699
  }
93457
93700
  function loadQuotaWatchState(stateDir) {
93458
- const path2 = join55(stateDir, STATE_FILE2);
93459
- if (!existsSync49(path2))
93701
+ const path2 = join56(stateDir, STATE_FILE2);
93702
+ if (!existsSync50(path2))
93460
93703
  return emptyQuotaWatchState();
93461
93704
  try {
93462
- const raw = readFileSync53(path2, "utf-8");
93705
+ const raw = readFileSync54(path2, "utf-8");
93463
93706
  const parsed = JSON.parse(raw);
93464
93707
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
93465
93708
  return emptyQuotaWatchState();
@@ -93476,9 +93719,9 @@ function loadQuotaWatchState(stateDir) {
93476
93719
  }
93477
93720
  }
93478
93721
  function saveQuotaWatchState(stateDir, state7) {
93479
- mkdirSync42(stateDir, { recursive: true });
93480
- const path2 = join55(stateDir, STATE_FILE2);
93481
- writeFileSync43(path2, JSON.stringify(state7, null, 2) + `
93722
+ mkdirSync43(stateDir, { recursive: true });
93723
+ const path2 = join56(stateDir, STATE_FILE2);
93724
+ writeFileSync44(path2, JSON.stringify(state7, null, 2) + `
93482
93725
  `, { mode: 384 });
93483
93726
  }
93484
93727
  function patchQuotaWatchState(current, accountLabel, accountState) {
@@ -93526,31 +93769,31 @@ init_auth_snapshot_format2();
93526
93769
  // gateway/turn-active-marker.ts
93527
93770
  import {
93528
93771
  closeSync as closeSync11,
93529
- existsSync as existsSync50,
93530
- mkdirSync as mkdirSync43,
93772
+ existsSync as existsSync51,
93773
+ mkdirSync as mkdirSync44,
93531
93774
  openSync as openSync11,
93532
- readFileSync as readFileSync54,
93533
- statSync as statSync16,
93534
- unlinkSync as unlinkSync23,
93775
+ readFileSync as readFileSync55,
93776
+ statSync as statSync17,
93777
+ unlinkSync as unlinkSync24,
93535
93778
  utimesSync as utimesSync2,
93536
- writeFileSync as writeFileSync44
93779
+ writeFileSync as writeFileSync45
93537
93780
  } from "node:fs";
93538
- import { join as join56 } from "node:path";
93781
+ import { join as join57 } from "node:path";
93539
93782
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
93540
93783
  var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
93541
93784
  var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
93542
93785
  function removeTurnActiveMarker2(stateDir) {
93543
93786
  try {
93544
- unlinkSync23(join56(stateDir, TURN_ACTIVE_MARKER_FILE2));
93787
+ unlinkSync24(join57(stateDir, TURN_ACTIVE_MARKER_FILE2));
93545
93788
  } catch {}
93546
93789
  }
93547
93790
  function sweepStaleTurnActiveMarker(stateDir, opts) {
93548
- const path2 = join56(stateDir, TURN_ACTIVE_MARKER_FILE2);
93549
- if (!existsSync50(path2))
93791
+ const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
93792
+ if (!existsSync51(path2))
93550
93793
  return false;
93551
93794
  const now = opts.now ?? Date.now();
93552
93795
  try {
93553
- const st = statSync16(path2);
93796
+ const st = statSync17(path2);
93554
93797
  const ageMs = now - st.mtimeMs;
93555
93798
  const hardExpired = ageMs > opts.hardTtlMs;
93556
93799
  const idleExpired = !opts.turnInFlight && ageMs > opts.idleSweepMs;
@@ -93558,9 +93801,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
93558
93801
  return false;
93559
93802
  let payload = null;
93560
93803
  try {
93561
- payload = readFileSync54(path2, "utf8");
93804
+ payload = readFileSync55(path2, "utf8");
93562
93805
  } catch {}
93563
- unlinkSync23(path2);
93806
+ unlinkSync24(path2);
93564
93807
  if (opts.onRemove) {
93565
93808
  try {
93566
93809
  opts.onRemove({
@@ -93576,9 +93819,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
93576
93819
  }
93577
93820
  }
93578
93821
  function readTurnActiveMarkerAgeMs2(stateDir, now) {
93579
- const path2 = join56(stateDir, TURN_ACTIVE_MARKER_FILE2);
93822
+ const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
93580
93823
  try {
93581
- const st = statSync16(path2);
93824
+ const st = statSync17(path2);
93582
93825
  return (now ?? Date.now()) - st.mtimeMs;
93583
93826
  } catch {
93584
93827
  return null;
@@ -93589,19 +93832,19 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
93589
93832
  }
93590
93833
 
93591
93834
  // gateway/gateway-heartbeat.ts
93592
- import { mkdirSync as mkdirSync44, utimesSync as utimesSync3, writeFileSync as writeFileSync45 } from "node:fs";
93593
- import { join as join57 } from "node:path";
93835
+ import { mkdirSync as mkdirSync45, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
93836
+ import { join as join58 } from "node:path";
93594
93837
  var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
93595
93838
  var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
93596
93839
  function touchGatewayHeartbeat(stateDir) {
93597
- const path2 = join57(stateDir, GATEWAY_HEARTBEAT_FILE);
93840
+ const path2 = join58(stateDir, GATEWAY_HEARTBEAT_FILE);
93598
93841
  const now = new Date;
93599
93842
  try {
93600
93843
  utimesSync3(path2, now, now);
93601
93844
  } catch {
93602
93845
  try {
93603
- mkdirSync44(stateDir, { recursive: true });
93604
- writeFileSync45(path2, `${Date.now()}
93846
+ mkdirSync45(stateDir, { recursive: true });
93847
+ writeFileSync46(path2, `${Date.now()}
93605
93848
  `, { mode: 384 });
93606
93849
  } catch {}
93607
93850
  }
@@ -93613,11 +93856,120 @@ function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL
93613
93856
  return timer3;
93614
93857
  }
93615
93858
 
93859
+ // gateway/outbox-sweep.ts
93860
+ async function sweepOutbox(deps) {
93861
+ const now = deps.now?.() ?? Date.now();
93862
+ const log = deps.log ?? (() => {});
93863
+ const summary = { scanned: 0, delivered: 0, skipped: 0 };
93864
+ reclaimStaleSending(deps.stateDir, now);
93865
+ const pending = listPendingRecords(deps.stateDir);
93866
+ if (pending.length === 0)
93867
+ return summary;
93868
+ const deliveredNonces = readDeliveredNonces(deps.stateDir);
93869
+ for (const fileName of pending) {
93870
+ const record2 = readOutboxRecord(fileName, deps.stateDir);
93871
+ if (record2 == null)
93872
+ continue;
93873
+ summary.scanned++;
93874
+ const resolved = resolveOutboxChat(record2, {
93875
+ registryChainLookup: (anchorContent) => {
93876
+ const taskId = extractTaskId(anchorContent);
93877
+ if (taskId == null || deps.registryChainLookup == null)
93878
+ return null;
93879
+ return deps.registryChainLookup(taskId);
93880
+ }
93881
+ });
93882
+ const routePrefix = resolved?.via === "origin" ? "(from background task) " : "";
93883
+ const decision = decideOutboxSweep({
93884
+ record: record2,
93885
+ now,
93886
+ deliveredNonces,
93887
+ textAlreadyDelivered: resolved != null && deps.textAlreadyDelivered(resolved.chatId, resolved.threadId, record2.text),
93888
+ routable: resolved != null,
93889
+ routePrefix,
93890
+ quietMs: deps.quietMs ?? OUTBOX_QUIET_MS
93891
+ });
93892
+ if (decision.action !== "send" && decision.action !== "send-delayed") {
93893
+ summary.skipped++;
93894
+ if (decision.action === "skip-journaled") {
93895
+ clearOutboxRecord(record2.turnNonce, deps.stateDir);
93896
+ } else if (decision.action === "skip-dedup") {
93897
+ appendDelivered({ turnNonce: record2.turnNonce, textSha256: record2.textSha256, ts: now }, deps.stateDir);
93898
+ clearOutboxRecord(record2.turnNonce, deps.stateDir);
93899
+ }
93900
+ continue;
93901
+ }
93902
+ const claimed = claimRecord(record2.turnNonce, deps.stateDir);
93903
+ if (claimed == null) {
93904
+ summary.skipped++;
93905
+ continue;
93906
+ }
93907
+ const resolvedChat = resolved;
93908
+ try {
93909
+ const messageId = await deps.send(resolvedChat.chatId, resolvedChat.threadId, decision.text ?? record2.text);
93910
+ appendDelivered({ turnNonce: record2.turnNonce, textSha256: record2.textSha256, tgMessageId: messageId, ts: now }, deps.stateDir);
93911
+ removeClaimed(record2.turnNonce, deps.stateDir);
93912
+ summary.delivered++;
93913
+ log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
93914
+ `);
93915
+ } catch (err) {
93916
+ releaseClaim(record2.turnNonce, deps.stateDir);
93917
+ summary.skipped++;
93918
+ log(`outbox-sweep: send failed nonce=${record2.turnNonce}: ${err.message} \u2014 will retry
93919
+ `);
93920
+ }
93921
+ }
93922
+ return summary;
93923
+ }
93924
+ function chatFromTurnKey(turnKey3) {
93925
+ const idx = turnKey3.indexOf(":");
93926
+ const chatId = idx === -1 ? turnKey3 : turnKey3.slice(0, idx);
93927
+ const tRaw = idx === -1 ? "_" : turnKey3.slice(idx + 1);
93928
+ const t = tRaw === "_" || tRaw === "" ? null : Number(tRaw);
93929
+ return { chatId, threadId: Number.isFinite(t) ? t : null };
93930
+ }
93931
+ var OUTBOX_SWEEP_INTERVAL_MS = 5000;
93932
+ function startOutboxSweep(deps) {
93933
+ if (!deps.isGatewayMain || process.env.SWITCHROOM_TG_OUTBOX_DELIVERY === "0")
93934
+ return;
93935
+ const retry = createRetryApiCall({ log: deps.log });
93936
+ const tick3 = () => {
93937
+ const bot = deps.getBot();
93938
+ if (bot == null)
93939
+ return;
93940
+ sweepOutbox({
93941
+ stateDir: deps.stateDir,
93942
+ log: deps.log,
93943
+ send: async (chatId, threadId, text4) => {
93944
+ let lastId;
93945
+ for (let i = 0;i < text4.length; i += 4000) {
93946
+ const chunk2 = text4.slice(i, i + 4000);
93947
+ const res = await retryWithThreadFallback(retry, (tid) => bot.api.sendMessage(chatId, chunk2, tid != null ? { message_thread_id: tid } : {}), { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
93948
+ lastId = res?.message_id;
93949
+ }
93950
+ return lastId;
93951
+ },
93952
+ textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
93953
+ registryChainLookup: (taskId) => {
93954
+ const db3 = deps.getTurnsDb();
93955
+ if (db3 == null)
93956
+ return null;
93957
+ const turnKey3 = resolveSubagentOriginTurnKey(db3, taskId);
93958
+ return turnKey3 == null ? null : chatFromTurnKey(turnKey3);
93959
+ }
93960
+ }).catch((err) => deps.log?.(`outbox-sweep: tick failed: ${err.message}
93961
+ `));
93962
+ };
93963
+ const timer3 = setInterval(tick3, OUTBOX_SWEEP_INTERVAL_MS);
93964
+ timer3.unref?.();
93965
+ return timer3;
93966
+ }
93967
+
93616
93968
  // ../src/build-info.ts
93617
- var VERSION = "0.19.11";
93618
- var COMMIT_SHA = "cd60a0c0";
93619
- var COMMIT_DATE = "2026-07-21T21:06:25Z";
93620
- var LATEST_PR = 3497;
93969
+ var VERSION = "0.19.13";
93970
+ var COMMIT_SHA = "fda10488";
93971
+ var COMMIT_DATE = "2026-07-23T04:34:01Z";
93972
+ var LATEST_PR = 3508;
93621
93973
  var COMMITS_AHEAD_OF_TAG = 0;
93622
93974
 
93623
93975
  // gateway/boot-version.ts
@@ -93662,10 +94014,10 @@ function composeBootVersionString(inputs) {
93662
94014
  }
93663
94015
 
93664
94016
  // gateway/unhandled-rejection-policy.ts
93665
- var import_grammy13 = __toESM(require_mod2(), 1);
94017
+ var import_grammy14 = __toESM(require_mod2(), 1);
93666
94018
  function classifyRejection(err, opts = {}) {
93667
- const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy13.GrammyError;
93668
- const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy13.HttpError;
94019
+ const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy14.GrammyError;
94020
+ const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy14.HttpError;
93669
94021
  if (isHttp)
93670
94022
  return "log_only";
93671
94023
  if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
@@ -93694,12 +94046,12 @@ init_protocol();
93694
94046
  init_peercred();
93695
94047
  import * as net5 from "node:net";
93696
94048
  import * as fs2 from "node:fs";
93697
- import { homedir as homedir18 } from "node:os";
93698
- import { join as join58 } from "node:path";
94049
+ import { homedir as homedir19 } from "node:os";
94050
+ import { join as join59 } from "node:path";
93699
94051
  var DEFAULT_TIMEOUT_MS4 = 2000;
93700
94052
  var UNLOCK_TIMEOUT_MS = 30000;
93701
- var LEGACY_SOCKET_PATH2 = join58(homedir18(), ".switchroom", "vault-broker.sock");
93702
- var OPERATOR_SOCKET_PATH2 = join58(homedir18(), ".switchroom", "broker-operator", "sock");
94053
+ var LEGACY_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "vault-broker.sock");
94054
+ var OPERATOR_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "broker-operator", "sock");
93703
94055
  function defaultBrokerSocketPath2() {
93704
94056
  if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
93705
94057
  return OPERATOR_SOCKET_PATH2;
@@ -94581,8 +94933,8 @@ function resolveVaultApprovalPosture(broker) {
94581
94933
  }
94582
94934
 
94583
94935
  // registry/turns-schema.ts
94584
- import { chmodSync as chmodSync12, mkdirSync as mkdirSync45 } from "fs";
94585
- import { join as join59 } from "path";
94936
+ import { chmodSync as chmodSync12, mkdirSync as mkdirSync46 } from "fs";
94937
+ import { join as join60 } from "path";
94586
94938
  var DatabaseClass3 = null;
94587
94939
  function loadDatabaseClass3() {
94588
94940
  if (DatabaseClass3 != null)
@@ -94654,9 +95006,9 @@ function applySchema(db3) {
94654
95006
  }
94655
95007
  function openTurnsDb(agentDir) {
94656
95008
  const Database = loadDatabaseClass3();
94657
- const dir = join59(agentDir, "telegram");
94658
- mkdirSync45(dir, { recursive: true, mode: 448 });
94659
- const path2 = join59(dir, "registry.db");
95009
+ const dir = join60(agentDir, "telegram");
95010
+ mkdirSync46(dir, { recursive: true, mode: 448 });
95011
+ const path2 = join60(dir, "registry.db");
94660
95012
  const db3 = new Database(path2, { create: true });
94661
95013
  applySchema(db3);
94662
95014
  try {
@@ -95000,7 +95352,7 @@ function selectResumeBuilder(endedVia, opts) {
95000
95352
  }
95001
95353
 
95002
95354
  // gateway/bridge-dead-watchdog.ts
95003
- import { readFileSync as readFileSync56, writeFileSync as writeFileSync46, renameSync as renameSync20, unlinkSync as unlinkSync24 } from "node:fs";
95355
+ import { readFileSync as readFileSync57, writeFileSync as writeFileSync47, renameSync as renameSync21, unlinkSync as unlinkSync25 } from "node:fs";
95004
95356
 
95005
95357
  // gateway/cron-session.ts
95006
95358
  var CRON_IDENTITY_SUFFIX2 = "-cron";
@@ -95033,7 +95385,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
95033
95385
  const nowMs3 = opts.nowMs ?? Date.now();
95034
95386
  const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
95035
95387
  const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
95036
- const readFile = opts.readFile ?? ((p) => readFileSync56(p, "utf8"));
95388
+ const readFile = opts.readFile ?? ((p) => readFileSync57(p, "utf8"));
95037
95389
  let raw;
95038
95390
  try {
95039
95391
  raw = readFile(path2);
@@ -95054,13 +95406,13 @@ function readFreshCrashLogTail(path2, opts = {}) {
95054
95406
  }
95055
95407
  function writeBridgeDeadEscalationMarker(path2, marker) {
95056
95408
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
95057
- writeFileSync46(tmp, JSON.stringify(marker), "utf8");
95058
- renameSync20(tmp, path2);
95409
+ writeFileSync47(tmp, JSON.stringify(marker), "utf8");
95410
+ renameSync21(tmp, path2);
95059
95411
  }
95060
95412
  function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
95061
95413
  let marker = null;
95062
95414
  try {
95063
- const parsed = JSON.parse(readFileSync56(path2, "utf8"));
95415
+ const parsed = JSON.parse(readFileSync57(path2, "utf8"));
95064
95416
  if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
95065
95417
  const age = nowMs3 - parsed.ts;
95066
95418
  if (age >= 0 && age < maxAgeMs) {
@@ -95072,7 +95424,7 @@ function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs
95072
95424
  }
95073
95425
  } catch {}
95074
95426
  try {
95075
- unlinkSync24(path2);
95427
+ unlinkSync25(path2);
95076
95428
  } catch {}
95077
95429
  return marker;
95078
95430
  }
@@ -95213,15 +95565,15 @@ function createBridgeDeadWatchdog(opts) {
95213
95565
  }
95214
95566
 
95215
95567
  // gateway/boot-probes.ts
95216
- import { readFileSync as readFileSync57, readdirSync as readdirSync11, existsSync as existsSync52 } from "fs";
95568
+ import { readFileSync as readFileSync58, readdirSync as readdirSync12, existsSync as existsSync53 } from "fs";
95217
95569
  init_quota_cache();
95218
95570
  init_quota_check();
95219
95571
  import { execFile as execFileCb2 } from "child_process";
95220
95572
  import { promisify as promisify2 } from "util";
95221
95573
  var execFile2 = promisify2(execFileCb2);
95222
95574
  var realProcFs2 = {
95223
- readdir: (p) => readdirSync11(p),
95224
- readFile: (p) => readFileSync57(p, "utf-8")
95575
+ readdir: (p) => readdirSync12(p),
95576
+ readFile: (p) => readFileSync58(p, "utf-8")
95225
95577
  };
95226
95578
  function findAgentProcessInContainer2(fs3 = realProcFs2) {
95227
95579
  let entries;
@@ -95353,7 +95705,7 @@ function listNonTerminalSubagentsForTurn(db3, parentTurnKey) {
95353
95705
  `).all(parentTurnKey);
95354
95706
  return rows.map(mapSubagentRow);
95355
95707
  }
95356
- function resolveSubagentOriginTurnKey(db3, jsonlAgentId, maxHops = 5) {
95708
+ function resolveSubagentOriginTurnKey2(db3, jsonlAgentId, maxHops = 5) {
95357
95709
  const seen = new Set;
95358
95710
  let currentJsonlId = jsonlAgentId;
95359
95711
  for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
@@ -95471,7 +95823,7 @@ if (isGatewayMain) {
95471
95823
  shutdownAnalytics();
95472
95824
  });
95473
95825
  }
95474
- var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join60(homedir19(), ".claude", "channels", "telegram");
95826
+ var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join61(homedir20(), ".claude", "channels", "telegram");
95475
95827
  var permCardStore = createPermissionCardStore(STATE_DIR);
95476
95828
  var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
95477
95829
  var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
@@ -95510,7 +95862,7 @@ function alwaysAllowDrainDeps() {
95510
95862
  return {
95511
95863
  readConfigText: () => {
95512
95864
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
95513
- return readFileSync58(cfgPath, "utf8");
95865
+ return readFileSync59(cfgPath, "utf8");
95514
95866
  },
95515
95867
  resolveAllowList: (_configText, agentName3) => {
95516
95868
  const cfg = loadConfig2();
@@ -95571,11 +95923,11 @@ function scheduleAlwaysAllowPersistDrain() {
95571
95923
  }, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
95572
95924
  timer3.unref?.();
95573
95925
  }
95574
- var ACCESS_FILE = join60(STATE_DIR, "access.json");
95575
- var APPROVED_DIR = join60(STATE_DIR, "approved");
95576
- var ENV_FILE = join60(STATE_DIR, ".env");
95577
- var INBOX_DIR = join60(STATE_DIR, "inbox");
95578
- var PEOPLE_FILE = join60(STATE_DIR, "people.json");
95926
+ var ACCESS_FILE = join61(STATE_DIR, "access.json");
95927
+ var APPROVED_DIR = join61(STATE_DIR, "approved");
95928
+ var ENV_FILE = join61(STATE_DIR, ".env");
95929
+ var INBOX_DIR = join61(STATE_DIR, "inbox");
95930
+ var PEOPLE_FILE = join61(STATE_DIR, "people.json");
95579
95931
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
95580
95932
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
95581
95933
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -95640,7 +95992,7 @@ function formatBootVersion() {
95640
95992
  }
95641
95993
  try {
95642
95994
  chmodSync14(ENV_FILE, 384);
95643
- for (const line of readFileSync58(ENV_FILE, "utf8").split(`
95995
+ for (const line of readFileSync59(ENV_FILE, "utf8").split(`
95644
95996
  `)) {
95645
95997
  const m = line.match(/^(\w+)=(.*)$/);
95646
95998
  if (m && process.env[m[1]] === undefined)
@@ -95661,7 +96013,7 @@ var bot;
95661
96013
  var lastGetUpdatesHeartbeatMs = Date.now();
95662
96014
  var GRAMMY_VERSION = (() => {
95663
96015
  try {
95664
- const raw = readFileSync58(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
96016
+ const raw = readFileSync59(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
95665
96017
  return JSON.parse(raw).version ?? "unknown";
95666
96018
  } catch {
95667
96019
  return "unknown";
@@ -95724,7 +96076,7 @@ function assertSendable(f) {
95724
96076
  } catch {
95725
96077
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
95726
96078
  }
95727
- const inbox = join60(stateReal, "inbox");
96079
+ const inbox = join61(stateReal, "inbox");
95728
96080
  if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
95729
96081
  throw new Error(`refusing to send channel state: ${f}`);
95730
96082
  }
@@ -95743,7 +96095,7 @@ function assertSendable(f) {
95743
96095
  }
95744
96096
  function readAccessFile() {
95745
96097
  try {
95746
- const raw = readFileSync58(ACCESS_FILE, "utf8");
96098
+ const raw = readFileSync59(ACCESS_FILE, "utf8");
95747
96099
  const parsed = JSON.parse(raw);
95748
96100
  const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
95749
96101
  const groups = {};
@@ -95783,7 +96135,7 @@ function readAccessFile() {
95783
96135
  if (err.code === "ENOENT")
95784
96136
  return defaultAccess();
95785
96137
  try {
95786
- renameSync21(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
96138
+ renameSync22(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
95787
96139
  } catch {}
95788
96140
  process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
95789
96141
  `);
@@ -95805,7 +96157,7 @@ function loadAccess() {
95805
96157
  }
95806
96158
  function readPeopleFile() {
95807
96159
  try {
95808
- const raw = readFileSync58(PEOPLE_FILE, "utf8");
96160
+ const raw = readFileSync59(PEOPLE_FILE, "utf8");
95809
96161
  const parsed = JSON.parse(raw);
95810
96162
  if (!Array.isArray(parsed.entries))
95811
96163
  return [];
@@ -95827,11 +96179,11 @@ function assertAllowedChat(chat_id) {
95827
96179
  function saveAccess(a) {
95828
96180
  if (STATIC)
95829
96181
  return;
95830
- mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
96182
+ mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
95831
96183
  const tmp = ACCESS_FILE + ".tmp";
95832
- writeFileSync48(tmp, JSON.stringify(a, null, 2) + `
96184
+ writeFileSync49(tmp, JSON.stringify(a, null, 2) + `
95833
96185
  `, { mode: 384 });
95834
- renameSync21(tmp, ACCESS_FILE);
96186
+ renameSync22(tmp, ACCESS_FILE);
95835
96187
  }
95836
96188
  function pruneExpired(a) {
95837
96189
  const now = Date.now();
@@ -95849,7 +96201,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
95849
96201
  if (isGatewayMain && HISTORY_ENABLED) {
95850
96202
  try {
95851
96203
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
95852
- process.stderr.write(`telegram gateway: history capture enabled at ${join60(STATE_DIR, "history.db")}
96204
+ process.stderr.write(`telegram gateway: history capture enabled at ${join61(STATE_DIR, "history.db")}
95853
96205
  `);
95854
96206
  } catch (err) {
95855
96207
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -95868,12 +96220,12 @@ if (isGatewayMain)
95868
96220
  let markerTurnKey = null;
95869
96221
  let markerAgeMs = null;
95870
96222
  try {
95871
- const markerPath = join60(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
95872
- if (existsSync54(markerPath)) {
95873
- const st = statSync19(markerPath);
96223
+ const markerPath = join61(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
96224
+ if (existsSync55(markerPath)) {
96225
+ const st = statSync20(markerPath);
95874
96226
  markerAgeMs = Date.now() - st.mtimeMs;
95875
96227
  try {
95876
- const payload = JSON.parse(readFileSync58(markerPath, "utf8"));
96228
+ const payload = JSON.parse(readFileSync59(markerPath, "utf8"));
95877
96229
  if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
95878
96230
  markerTurnKey = payload.turnKey;
95879
96231
  }
@@ -95893,10 +96245,10 @@ if (isGatewayMain)
95893
96245
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
95894
96246
  `);
95895
96247
  } else {
95896
- process.stderr.write(`telegram gateway: turn-registry initialized at ${join60(agentDir, "telegram", "registry.db")}
96248
+ process.stderr.write(`telegram gateway: turn-registry initialized at ${join61(agentDir, "telegram", "registry.db")}
95897
96249
  `);
95898
96250
  }
95899
- const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join60(STATE_DIR, "bridge-dead-escalation.json"));
96251
+ const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join61(STATE_DIR, "bridge-dead-escalation.json"));
95900
96252
  if (bridgeDeadMarker != null) {
95901
96253
  bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
95902
96254
  process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
@@ -95909,7 +96261,7 @@ if (isGatewayMain)
95909
96261
  const pending2 = findLatestTurnIfInterrupted(turnsDb);
95910
96262
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
95911
96263
  if (pending2 != null && selfAgent) {
95912
- const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join60(STATE_DIR, "clean-shutdown.json");
96264
+ const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
95913
96265
  const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
95914
96266
  const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
95915
96267
  const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
@@ -96022,7 +96374,7 @@ if (isGatewayMain)
96022
96374
  `);
96023
96375
  }
96024
96376
  }
96025
- const pendingEnvPath = join60(agentDir, ".pending-turn.env");
96377
+ const pendingEnvPath = join61(agentDir, ".pending-turn.env");
96026
96378
  try {
96027
96379
  if (pending2 != null) {
96028
96380
  const lines = [
@@ -96036,13 +96388,13 @@ if (isGatewayMain)
96036
96388
  pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
96037
96389
  ];
96038
96390
  const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
96039
- writeFileSync48(pendingEnvTmp, lines.join(`
96391
+ writeFileSync49(pendingEnvTmp, lines.join(`
96040
96392
  `) + `
96041
96393
  `, { mode: 384 });
96042
- renameSync21(pendingEnvTmp, pendingEnvPath);
96394
+ renameSync22(pendingEnvTmp, pendingEnvPath);
96043
96395
  process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
96044
96396
  `);
96045
- } else if (existsSync54(pendingEnvPath)) {
96397
+ } else if (existsSync55(pendingEnvPath)) {
96046
96398
  rmSync6(pendingEnvPath, { force: true });
96047
96399
  process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
96048
96400
  `);
@@ -96060,7 +96412,7 @@ function resolveSubagentOriginChat(agentId) {
96060
96412
  if (turnsDb == null)
96061
96413
  return null;
96062
96414
  try {
96063
- const originKey = resolveSubagentOriginTurnKey(turnsDb, agentId);
96415
+ const originKey = resolveSubagentOriginTurnKey2(turnsDb, agentId);
96064
96416
  if (originKey == null)
96065
96417
  return null;
96066
96418
  const turn = getTurnByKey(turnsDb, originKey);
@@ -96145,12 +96497,12 @@ if (isGatewayMain && !STATIC) {
96145
96497
  function checkApprovals() {
96146
96498
  let files;
96147
96499
  try {
96148
- files = readdirSync13(APPROVED_DIR);
96500
+ files = readdirSync14(APPROVED_DIR);
96149
96501
  } catch {
96150
96502
  return;
96151
96503
  }
96152
96504
  for (const senderId of files) {
96153
- const file = join60(APPROVED_DIR, senderId);
96505
+ const file = join61(APPROVED_DIR, senderId);
96154
96506
  bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
96155
96507
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
96156
96508
  `);
@@ -96323,12 +96675,12 @@ function noteAgentOutputAt(key, ts) {
96323
96675
  lastAgentOutputAt.delete(oldest);
96324
96676
  }
96325
96677
  }
96326
- var OBLIGATION_STORE_PATH = join60(STATE_DIR, "obligations.json");
96678
+ var OBLIGATION_STORE_PATH = join61(STATE_DIR, "obligations.json");
96327
96679
  var obligationStoreFs = {
96328
- readFileSync: (p) => readFileSync58(p, "utf8"),
96329
- writeFileSync: (p, d) => writeFileSync48(p, d),
96330
- renameSync: (a, b) => renameSync21(a, b),
96331
- existsSync: (p) => existsSync54(p)
96680
+ readFileSync: (p) => readFileSync59(p, "utf8"),
96681
+ writeFileSync: (p, d) => writeFileSync49(p, d),
96682
+ renameSync: (a, b) => renameSync22(a, b),
96683
+ existsSync: (p) => existsSync55(p)
96332
96684
  };
96333
96685
  var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
96334
96686
  onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
@@ -97102,14 +97454,14 @@ function emitTurnRecord(turn, endedAt) {
97102
97454
  maybeRotate(turnsPath, {
97103
97455
  statSize: (p) => {
97104
97456
  try {
97105
- return statSync19(p).size;
97457
+ return statSync20(p).size;
97106
97458
  } catch {
97107
97459
  return;
97108
97460
  }
97109
97461
  },
97110
- rename: (from, to) => renameSync21(from, to)
97462
+ rename: (from, to) => renameSync22(from, to)
97111
97463
  });
97112
- appendFileSync7(turnsPath, rec);
97464
+ appendFileSync8(turnsPath, rec);
97113
97465
  } catch {}
97114
97466
  }
97115
97467
  function maybeProactiveCompact() {
@@ -97591,7 +97943,7 @@ function wrapBootCardApi(threadId) {
97591
97943
  await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
97592
97944
  return "edited";
97593
97945
  } catch (err) {
97594
- const desc = err instanceof import_grammy15.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
97946
+ const desc = err instanceof import_grammy16.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
97595
97947
  if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
97596
97948
  return "edited";
97597
97949
  return "gone";
@@ -97735,7 +98087,7 @@ function sweepStaleAlwaysAllowCorrelations(now = Date.now()) {
97735
98087
  var MENTAL_MODEL_CORRELATION_TTL_MS = 720000;
97736
98088
  var pendingMentalModelCorrelations = createSweepableStore((entry, now) => now - entry.createdAt > MENTAL_MODEL_CORRELATION_TTL_MS);
97737
98089
  function mentalModelCorrelationKey(agentName3, unifiedDiff) {
97738
- return `${agentName3}::${createHash4("sha256").update(unifiedDiff).digest("hex")}`;
98090
+ return `${agentName3}::${createHash5("sha256").update(unifiedDiff).digest("hex")}`;
97739
98091
  }
97740
98092
  function sweepStaleMentalModelCorrelations(now = Date.now()) {
97741
98093
  pendingMentalModelCorrelations.sweep(now);
@@ -98671,28 +99023,28 @@ var statusPinState = new Map;
98671
99023
  var statusPinChatIds = new Map;
98672
99024
  var statusPinPinnedAt = new Map;
98673
99025
  var statusPinRightsCache = new PinRightsCache2;
98674
- var STATUS_PIN_STORE_PATH = join60(STATE_DIR, "status-pins.json");
99026
+ var STATUS_PIN_STORE_PATH = join61(STATE_DIR, "status-pins.json");
98675
99027
  var statusPinStoreFs = {
98676
- readFileSync: (p) => readFileSync58(p, "utf8"),
98677
- writeFileSync: (p, d) => writeFileSync48(p, d),
98678
- renameSync: (a, b) => renameSync21(a, b),
98679
- existsSync: (p) => existsSync54(p)
99028
+ readFileSync: (p) => readFileSync59(p, "utf8"),
99029
+ writeFileSync: (p, d) => writeFileSync49(p, d),
99030
+ renameSync: (a, b) => renameSync22(a, b),
99031
+ existsSync: (p) => existsSync55(p)
98680
99032
  };
98681
99033
  var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
98682
- var ACTIVITY_CARD_STORE_PATH = join60(STATE_DIR, "activity-cards-pending.json");
99034
+ var ACTIVITY_CARD_STORE_PATH = join61(STATE_DIR, "activity-cards-pending.json");
98683
99035
  var activityCardStoreFs = {
98684
- readFileSync: (p) => readFileSync58(p, "utf8"),
98685
- writeFileSync: (p, d) => writeFileSync48(p, d),
98686
- renameSync: (a, b) => renameSync21(a, b),
98687
- existsSync: (p) => existsSync54(p)
99036
+ readFileSync: (p) => readFileSync59(p, "utf8"),
99037
+ writeFileSync: (p, d) => writeFileSync49(p, d),
99038
+ renameSync: (a, b) => renameSync22(a, b),
99039
+ existsSync: (p) => existsSync55(p)
98688
99040
  };
98689
99041
  var activityCardPersistEnabled = !STATIC;
98690
- var QUEUED_CARD_STORE_PATH = join60(STATE_DIR, "queued-cards-pending.json");
99042
+ var QUEUED_CARD_STORE_PATH = join61(STATE_DIR, "queued-cards-pending.json");
98691
99043
  var queuedCardStoreFs = {
98692
- readFileSync: (p) => readFileSync58(p, "utf8"),
98693
- writeFileSync: (p, d) => writeFileSync48(p, d),
98694
- renameSync: (a, b) => renameSync21(a, b),
98695
- existsSync: (p) => existsSync54(p)
99044
+ readFileSync: (p) => readFileSync59(p, "utf8"),
99045
+ writeFileSync: (p, d) => writeFileSync49(p, d),
99046
+ renameSync: (a, b) => renameSync22(a, b),
99047
+ existsSync: (p) => existsSync55(p)
98696
99048
  };
98697
99049
  var queuedCardPersistEnabled = !STATIC;
98698
99050
  function persistQueuedCard(key, chatId, threadId, messageId) {
@@ -99065,12 +99417,12 @@ var getPinnedProgressCardMessageId = null;
99065
99417
  var completeProgressCardTurn = null;
99066
99418
  var subagentWatcher = null;
99067
99419
  var workerActivityFeed = null;
99068
- var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join60(STATE_DIR, "gateway.sock");
99420
+ var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join61(STATE_DIR, "gateway.sock");
99069
99421
  if (isGatewayMain)
99070
- mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
99071
- var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join60(STATE_DIR, "gateway.pid.json");
99072
- var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join60(STATE_DIR, "gateway-session.json");
99073
- var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join60(STATE_DIR, "clean-shutdown.json");
99422
+ mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
99423
+ var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join61(STATE_DIR, "gateway.pid.json");
99424
+ var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join61(STATE_DIR, "gateway-session.json");
99425
+ var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
99074
99426
  var GATEWAY_STARTED_AT_MS = Date.now();
99075
99427
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
99076
99428
  var activeBootCard = null;
@@ -99099,7 +99451,7 @@ function ensureIssuesCard(chatId, threadId) {
99099
99451
  bot: botApi,
99100
99452
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
99101
99453
  `),
99102
- persistPath: join60(stateDir, "issues-card.json")
99454
+ persistPath: join61(stateDir, "issues-card.json")
99103
99455
  });
99104
99456
  activeIssuesWatcher = startIssuesWatcher({
99105
99457
  stateDir,
@@ -99275,6 +99627,7 @@ function runDeliveryConfirmSweep() {
99275
99627
  }
99276
99628
  var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
99277
99629
  _deliveryConfirmSweep?.unref?.();
99630
+ startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, log: (l) => process.stderr.write(l) });
99278
99631
  if (isGatewayMain)
99279
99632
  startTimer2({
99280
99633
  editMessage: async (ctx) => {
@@ -99296,14 +99649,14 @@ if (isGatewayMain)
99296
99649
  var inboundSpool;
99297
99650
  if (isGatewayMain)
99298
99651
  inboundSpool = STATIC ? undefined : createInboundSpool({
99299
- path: join60(STATE_DIR, "inbound-spool.jsonl"),
99652
+ path: join61(STATE_DIR, "inbound-spool.jsonl"),
99300
99653
  fs: {
99301
- appendFileSync: (p, d) => appendFileSync7(p, d),
99302
- readFileSync: (p) => readFileSync58(p, "utf8"),
99303
- writeFileSync: (p, d) => writeFileSync48(p, d),
99304
- renameSync: (a, b) => renameSync21(a, b),
99305
- existsSync: (p) => existsSync54(p),
99306
- statSizeSync: (p) => statSync19(p).size
99654
+ appendFileSync: (p, d) => appendFileSync8(p, d),
99655
+ readFileSync: (p) => readFileSync59(p, "utf8"),
99656
+ writeFileSync: (p, d) => writeFileSync49(p, d),
99657
+ renameSync: (a, b) => renameSync22(a, b),
99658
+ existsSync: (p) => existsSync55(p),
99659
+ statSizeSync: (p) => statSync20(p).size
99307
99660
  },
99308
99661
  onDegraded: (info) => {
99309
99662
  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}` : ""}
@@ -99364,13 +99717,13 @@ async function maybeRedeliverUndeliveredAnswer() {
99364
99717
  let transcriptText;
99365
99718
  try {
99366
99719
  const projectsDir = getProjectsDirForCwd();
99367
- const path2 = join60(projectsDir, `${sessionId}.jsonl`);
99368
- if (!existsSync54(path2)) {
99720
+ const path2 = join61(projectsDir, `${sessionId}.jsonl`);
99721
+ if (!existsSync55(path2)) {
99369
99722
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
99370
99723
  `);
99371
99724
  return;
99372
99725
  }
99373
- transcriptText = readFileSync58(path2, "utf8");
99726
+ transcriptText = readFileSync59(path2, "utf8");
99374
99727
  } catch (err) {
99375
99728
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
99376
99729
  `);
@@ -99467,7 +99820,7 @@ if (isGatewayMain && inboundSpool != null) {
99467
99820
  }
99468
99821
  var pendingPermissionBuffer = createPendingPermissionBuffer();
99469
99822
  function buildPermissionActionRow(requestId, showAlways) {
99470
- const kb = new import_grammy15.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
99823
+ const kb = new import_grammy16.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
99471
99824
  if (showAlways)
99472
99825
  kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
99473
99826
  return kb;
@@ -99529,8 +99882,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
99529
99882
  isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
99530
99883
  isShuttingDown: () => shuttingDown,
99531
99884
  escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
99532
- crashLogPath: join60(STATE_DIR, "bridge-crash.log"),
99533
- markerPath: join60(STATE_DIR, "bridge-dead-escalation.json"),
99885
+ crashLogPath: join61(STATE_DIR, "bridge-crash.log"),
99886
+ markerPath: join61(STATE_DIR, "bridge-dead-escalation.json"),
99534
99887
  log: (line) => process.stderr.write(`${line}
99535
99888
  `),
99536
99889
  priorStreak: bridgeDeadPriorStreak,
@@ -99636,8 +99989,8 @@ if (isGatewayMain)
99636
99989
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
99637
99990
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
99638
99991
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
99639
- configSnapshotPath: join60(resolvedAgentDirForCard, ".config-snapshot.json"),
99640
- bootCardStatePath: join60(resolvedAgentDirForCard, ".boot-card-msgid.json"),
99992
+ configSnapshotPath: join61(resolvedAgentDirForCard, ".config-snapshot.json"),
99993
+ bootCardStatePath: join61(resolvedAgentDirForCard, ".boot-card-msgid.json"),
99641
99994
  floodStatePath: FLOOD_STATE_PATH,
99642
99995
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
99643
99996
  }, ackMsgId).then((handle) => {
@@ -100041,7 +100394,7 @@ if (isGatewayMain)
100041
100394
  }
100042
100395
  },
100043
100396
  postAttachment: async (args) => {
100044
- const input = new import_grammy15.InputFile(Buffer.from(args.content, "utf8"), args.filename);
100397
+ const input = new import_grammy16.InputFile(Buffer.from(args.content, "utf8"), args.filename);
100045
100398
  await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
100046
100399
  ...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
100047
100400
  }), {
@@ -100319,7 +100672,7 @@ if (isGatewayMain)
100319
100672
  const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
100320
100673
  if (Number.isInteger(receiverUid))
100321
100674
  allowedUids.push(receiverUid);
100322
- const socketPath = join60(STATE_DIR, "webhook.sock");
100675
+ const socketPath = join61(STATE_DIR, "webhook.sock");
100323
100676
  const webhookInject = (agentName3, inbound) => {
100324
100677
  const msg = inbound;
100325
100678
  const delivered = ipcServer.sendToAgent(agentName3, msg);
@@ -100547,9 +100900,9 @@ function redactOutboundText(text5, site) {
100547
100900
  var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
100548
100901
  var VOICE_OUT_HARD_CHUNK_CAP = 4096;
100549
100902
  var voiceOnDemandCache = new VoiceOnDemandCache({
100550
- persistPath: join60(STATE_DIR, "voice-ondemand.json")
100903
+ persistPath: join61(STATE_DIR, "voice-ondemand.json")
100551
100904
  });
100552
- var VOICE_CACHE_DIR = join60(STATE_DIR, "voice-cache");
100905
+ var VOICE_CACHE_DIR = join61(STATE_DIR, "voice-cache");
100553
100906
  var voicePreSynthQueue = new PreSynthQueue({
100554
100907
  runJob: async (job) => {
100555
100908
  const sidecarToken = await materializeSidecarToken2();
@@ -100816,7 +101169,7 @@ async function executeAskUser(rawArgs) {
100816
101169
  }
100817
101170
  }
100818
101171
  const askId = generateAskId();
100819
- const keyboard = new import_grammy15.InlineKeyboard;
101172
+ const keyboard = new import_grammy16.InlineKeyboard;
100820
101173
  for (let i = 0;i < args.options.length; i++) {
100821
101174
  keyboard.text(args.options[i], encodeAskCallback(askId, i));
100822
101175
  if (i < args.options.length - 1)
@@ -100951,11 +101304,11 @@ async function executeSendGif(rawArgs) {
100951
101304
  };
100952
101305
  }
100953
101306
  async function publishToTelegraph(text5, shortName, authorName) {
100954
- const accountPath = join60(STATE_DIR, "telegraph-account.json");
101307
+ const accountPath = join61(STATE_DIR, "telegraph-account.json");
100955
101308
  let account = null;
100956
101309
  try {
100957
- if (existsSync54(accountPath)) {
100958
- const raw = readFileSync58(accountPath, "utf-8");
101310
+ if (existsSync55(accountPath)) {
101311
+ const raw = readFileSync59(accountPath, "utf-8");
100959
101312
  const parsed = JSON.parse(raw);
100960
101313
  if (parsed.shortName && parsed.accessToken) {
100961
101314
  account = parsed;
@@ -100974,8 +101327,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
100974
101327
  }
100975
101328
  account = created.value;
100976
101329
  try {
100977
- mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
100978
- writeFileSync48(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
101330
+ mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
101331
+ writeFileSync49(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
100979
101332
  } catch (err) {
100980
101333
  process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
100981
101334
  `);
@@ -101091,7 +101444,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
101091
101444
  }
101092
101445
  function readLiveSwitchroomConfigText() {
101093
101446
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
101094
- return readFileSync58(cfgPath, "utf8");
101447
+ return readFileSync59(cfgPath, "utf8");
101095
101448
  }
101096
101449
  async function executeReact(args) {
101097
101450
  if (!args.chat_id)
@@ -101130,9 +101483,9 @@ async function executeDownloadAttachment(args) {
101130
101483
  fileUniqueId: file.file_unique_id,
101131
101484
  now: Date.now()
101132
101485
  });
101133
- mkdirSync47(INBOX_DIR, { recursive: true, mode: 448 });
101486
+ mkdirSync48(INBOX_DIR, { recursive: true, mode: 448 });
101134
101487
  assertInsideInbox2(INBOX_DIR, dlPath);
101135
- writeFileSync48(dlPath, buf, { mode: 384 });
101488
+ writeFileSync49(dlPath, buf, { mode: 384 });
101136
101489
  return { content: [{ type: "text", text: dlPath }] };
101137
101490
  }
101138
101491
  async function executeEditMessage(args) {
@@ -101146,23 +101499,20 @@ async function executeEditMessage(args) {
101146
101499
  const editAccess = loadAccess();
101147
101500
  const editFormat = args.format ?? (editAccess.parseMode ?? "html");
101148
101501
  const editLiteralText = editFormat === "text";
101149
- let editRawText = repairEscapedWhitespace2(args.text);
101150
- if (!editLiteralText)
101151
- editRawText = normalizeParagraphBreaks2(editRawText);
101152
- editRawText = redactOutboundText(editRawText, "edit_message");
101153
- if (!editLiteralText)
101154
- editRawText = addParagraphSpacers2(stripExcessBold2(normalizePunctuation2(editRawText)));
101155
- {
101156
- const scrub = scrubVoice2(editRawText);
101157
- if (scrub.replaced > 0) {
101158
- editRawText = scrub.scrubbed;
101159
- emitRuntimeMetric({
101160
- kind: "voice_scrub_applied",
101161
- chatKey: statusKey(String(args.chat_id ?? ""), undefined),
101162
- replaced: scrub.replaced,
101163
- site: "edit_message"
101164
- });
101165
- }
101502
+ const _editNorm = normalizeOutboundBody(args.text, "edit_message", redactOutboundText, {
101503
+ literalText: editLiteralText,
101504
+ addSpacers: !editLiteralText,
101505
+ tz: resolveEnvTimezone2(),
101506
+ nowMs: Date.now()
101507
+ });
101508
+ let editRawText = _editNorm.text;
101509
+ if (_editNorm.voiceReplaced > 0) {
101510
+ emitRuntimeMetric({
101511
+ kind: "voice_scrub_applied",
101512
+ chatKey: statusKey(String(args.chat_id ?? ""), undefined),
101513
+ replaced: _editNorm.voiceReplaced,
101514
+ site: "edit_message"
101515
+ });
101166
101516
  }
101167
101517
  const edited = await robustApiCall(() => lockedBot.api.editMessageText(String(args.chat_id ?? ""), Number(args.message_id), editLiteralText ? editRawText : richMessage2(editRawText)));
101168
101518
  const id = typeof edited === "object" && edited ? edited.message_id : args.message_id;
@@ -102495,14 +102845,14 @@ function restartMarkerPath() {
102495
102845
  const agentDir = resolveAgentDirFromEnv();
102496
102846
  if (!agentDir)
102497
102847
  return null;
102498
- return join60(agentDir, "restart-pending.json");
102848
+ return join61(agentDir, "restart-pending.json");
102499
102849
  }
102500
102850
  function writeRestartMarker(marker) {
102501
102851
  const p = restartMarkerPath();
102502
102852
  if (!p)
102503
102853
  return;
102504
102854
  try {
102505
- writeFileSync48(p, JSON.stringify(marker));
102855
+ writeFileSync49(p, JSON.stringify(marker));
102506
102856
  lastPlannedRestartAt = Date.now();
102507
102857
  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}
102508
102858
  `);
@@ -102521,7 +102871,7 @@ function readRestartMarker() {
102521
102871
  if (!p)
102522
102872
  return null;
102523
102873
  try {
102524
- return JSON.parse(readFileSync58(p, "utf8"));
102874
+ return JSON.parse(readFileSync59(p, "utf8"));
102525
102875
  } catch {
102526
102876
  return null;
102527
102877
  }
@@ -102670,7 +103020,7 @@ var _dockerReachable;
102670
103020
  function isDockerReachable() {
102671
103021
  if (_dockerReachable !== undefined)
102672
103022
  return _dockerReachable;
102673
- if (!existsSync54("/var/run/docker.sock")) {
103023
+ if (!existsSync55("/var/run/docker.sock")) {
102674
103024
  _dockerReachable = false;
102675
103025
  return _dockerReachable;
102676
103026
  }
@@ -102687,12 +103037,12 @@ function _resetDockerReachableCache() {
102687
103037
  }
102688
103038
  function spawnSwitchroomDetached(args, onFailure) {
102689
103039
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
102690
- const logPath = join60(STATE_DIR, "detached-spawn.log");
103040
+ const logPath = join61(STATE_DIR, "detached-spawn.log");
102691
103041
  let outFd = null;
102692
103042
  try {
102693
- mkdirSync47(STATE_DIR, { recursive: true });
103043
+ mkdirSync48(STATE_DIR, { recursive: true });
102694
103044
  outFd = openSync12(logPath, "a");
102695
- writeFileSync48(logPath, `
103045
+ writeFileSync49(logPath, `
102696
103046
  [${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
102697
103047
  `, { flag: "a" });
102698
103048
  } catch {}
@@ -102718,7 +103068,7 @@ function spawnSwitchroomDetached(args, onFailure) {
102718
103068
  return;
102719
103069
  let tail = "";
102720
103070
  try {
102721
- const full = readFileSync58(logPath, "utf8");
103071
+ const full = readFileSync59(logPath, "utf8");
102722
103072
  tail = full.split(`
102723
103073
  `).slice(-30).join(`
102724
103074
  `).trim();
@@ -102980,10 +103330,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
102980
103330
  }
102981
103331
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
102982
103332
  try {
102983
- const auditPath = join60(homedir19(), ".switchroom", "vault-audit.log");
102984
- if (!existsSync54(auditPath))
103333
+ const auditPath = join61(homedir20(), ".switchroom", "vault-audit.log");
103334
+ if (!existsSync55(auditPath))
102985
103335
  return [];
102986
- const raw = readFileSync58(auditPath, "utf8");
103336
+ const raw = readFileSync59(auditPath, "utf8");
102987
103337
  return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
102988
103338
  } catch {
102989
103339
  return [];
@@ -103034,7 +103384,7 @@ async function buildAgentMetadata(agentName3) {
103034
103384
  try {
103035
103385
  const agentDir = resolveAgentDirFromEnv();
103036
103386
  if (agentDir) {
103037
- const raw = readFileSync58(join60(agentDir, ".claude", ".claude.json"), "utf8");
103387
+ const raw = readFileSync59(join61(agentDir, ".claude", ".claude.json"), "utf8");
103038
103388
  claudeJson = JSON.parse(raw);
103039
103389
  }
103040
103390
  } catch {}
@@ -103163,7 +103513,7 @@ function buildModelDeps(restartCtx) {
103163
103513
  try {
103164
103514
  const agentDir = resolveAgentDirFromEnv();
103165
103515
  if (agentDir) {
103166
- const local = await fetchQuota2({ claudeConfigDir: join60(agentDir, ".claude") });
103516
+ const local = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
103167
103517
  if (local.ok)
103168
103518
  return formatQuotaLine2(local.data);
103169
103519
  }
@@ -103258,7 +103608,7 @@ function buildModelDeps(restartCtx) {
103258
103608
  function modelMenuReplyMarkup(reply) {
103259
103609
  if (!reply.keyboard)
103260
103610
  return;
103261
- const kb = new import_grammy15.InlineKeyboard;
103611
+ const kb = new import_grammy16.InlineKeyboard;
103262
103612
  for (const row of reply.keyboard) {
103263
103613
  for (const btn of row)
103264
103614
  kb.text(btn.text, btn.callback_data);
@@ -103398,7 +103748,7 @@ function buildEffortDeps() {
103398
103748
  function effortMenuReplyMarkup(reply) {
103399
103749
  if (!reply.keyboard)
103400
103750
  return;
103401
- const kb = new import_grammy15.InlineKeyboard;
103751
+ const kb = new import_grammy16.InlineKeyboard;
103402
103752
  for (const row of reply.keyboard) {
103403
103753
  for (const btn of row)
103404
103754
  kb.text(btn.text, btn.callback_data);
@@ -103409,10 +103759,10 @@ function effortMenuReplyMarkup(reply) {
103409
103759
  function flushAgentHandoff(agentDir) {
103410
103760
  let removed = 0;
103411
103761
  for (const fname of [".handoff.md", ".handoff-topic"]) {
103412
- const p = join60(agentDir, fname);
103762
+ const p = join61(agentDir, fname);
103413
103763
  try {
103414
- if (existsSync54(p)) {
103415
- unlinkSync26(p);
103764
+ if (existsSync55(p)) {
103765
+ unlinkSync27(p);
103416
103766
  removed++;
103417
103767
  }
103418
103768
  } catch (err) {
@@ -103467,7 +103817,7 @@ async function handleNewCommand(ctx) {
103467
103817
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
103468
103818
  if (agentDir != null) {
103469
103819
  try {
103470
- writeFileSync48(join60(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
103820
+ writeFileSync49(join61(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
103471
103821
  `, "utf8");
103472
103822
  } catch (err) {
103473
103823
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -103580,16 +103930,16 @@ function buildFolderPickerDeps() {
103580
103930
  };
103581
103931
  }
103582
103932
  var lockoutOps = {
103583
- readFileSync: (p, enc) => readFileSync58(p, enc),
103584
- writeFileSync: (p, data, opts) => writeFileSync48(p, data, opts),
103585
- existsSync: (p) => existsSync54(p),
103586
- mkdirSync: (p, opts) => mkdirSync47(p, opts),
103587
- joinPath: (...parts) => join60(...parts)
103933
+ readFileSync: (p, enc) => readFileSync59(p, enc),
103934
+ writeFileSync: (p, data, opts) => writeFileSync49(p, data, opts),
103935
+ existsSync: (p) => existsSync55(p),
103936
+ mkdirSync: (p, opts) => mkdirSync48(p, opts),
103937
+ joinPath: (...parts) => join61(...parts)
103588
103938
  };
103589
103939
  var FLEET_FALLBACK_DEDUP_MS = 30000;
103590
103940
  function isAuthBrokerSocketReachable() {
103591
103941
  try {
103592
- return existsSync54(resolveAuthBrokerSocketPath2());
103942
+ return existsSync55(resolveAuthBrokerSocketPath2());
103593
103943
  } catch {
103594
103944
  return false;
103595
103945
  }
@@ -103844,7 +104194,7 @@ async function runCreditWatch() {
103844
104194
  if (!agentDir)
103845
104195
  return;
103846
104196
  const agentName3 = getMyAgentName();
103847
- const claudeConfigDir = join60(agentDir, ".claude");
104197
+ const claudeConfigDir = join61(agentDir, ".claude");
103848
104198
  const stateDir = STATE_DIR;
103849
104199
  const reason = readClaudeJsonOverage(claudeConfigDir);
103850
104200
  const prev = loadCreditState(stateDir);
@@ -105051,7 +105401,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
105051
105401
  return;
105052
105402
  }
105053
105403
  const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
105054
- const keyboard = new import_grammy15.InlineKeyboard().url("\uD83D\uDD10 Sign in to Microsoft", started.device.verification_uri).row().text("\u2716 Cancel", `cn:cancel:${key}`);
105404
+ const keyboard = new import_grammy16.InlineKeyboard().url("\uD83D\uDD10 Sign in to Microsoft", started.device.verification_uri).row().text("\u2716 Cancel", `cn:cancel:${key}`);
105055
105405
  const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
105056
105406
 
105057
105407
  ` + `1. Tap **Sign in to Microsoft** below.
@@ -105187,7 +105537,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
105187
105537
  tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
105188
105538
  });
105189
105539
  if (reply.keyboard && reply.keyboard.length > 0) {
105190
- const kb = new import_grammy15.InlineKeyboard;
105540
+ const kb = new import_grammy16.InlineKeyboard;
105191
105541
  for (let i = 0;i < reply.keyboard.length; i++) {
105192
105542
  const row = reply.keyboard[i];
105193
105543
  for (const b of row) {
@@ -105329,7 +105679,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
105329
105679
  }
105330
105680
  lines.push("");
105331
105681
  const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
105332
- const denialKeyboard = new import_grammy15.InlineKeyboard;
105682
+ const denialKeyboard = new import_grammy16.InlineKeyboard;
105333
105683
  if (denials.length > 0) {
105334
105684
  lines.push("**Recent denials (last 7d):**");
105335
105685
  for (const d of denials) {
@@ -105377,7 +105727,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
105377
105727
  byAgent.set(g.agent_slug, list3);
105378
105728
  }
105379
105729
  const lines = ["**\uD83D\uDCDC Active grants**", ""];
105380
- const keyboard = new import_grammy15.InlineKeyboard;
105730
+ const keyboard = new import_grammy16.InlineKeyboard;
105381
105731
  for (const [agentName3, agentGrants] of byAgent) {
105382
105732
  lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
105383
105733
  for (const g of agentGrants) {
@@ -105574,7 +105924,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
105574
105924
  if (ctx.chat?.type !== "private") {
105575
105925
  kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
105576
105926
  }
105577
- const keyboard = new import_grammy15.InlineKeyboard;
105927
+ const keyboard = new import_grammy16.InlineKeyboard;
105578
105928
  kbRows.forEach((row, ri) => {
105579
105929
  if (ri > 0)
105580
105930
  keyboard.row();
@@ -105600,7 +105950,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
105600
105950
  await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
105601
105951
  return;
105602
105952
  }
105603
- const result = await fetchQuota2({ claudeConfigDir: join60(agentDir, ".claude") });
105953
+ const result = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
105604
105954
  if (!result.ok) {
105605
105955
  await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
105606
105956
  return;
@@ -105926,7 +106276,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
105926
106276
  await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
105927
106277
  return;
105928
106278
  }
105929
- keyboard = new import_grammy15.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
106279
+ keyboard = new import_grammy16.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
105930
106280
  if (choices.specific)
105931
106281
  keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
105932
106282
  keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
@@ -105986,7 +106336,7 @@ ${interimLabel}` : interimLabel
105986
106336
  const unifiedDiff = (() => {
105987
106337
  try {
105988
106338
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
105989
- const raw = readFileSync58(cfgPath, "utf8");
106339
+ const raw = readFileSync59(cfgPath, "utf8");
105990
106340
  return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
105991
106341
  } catch (err) {
105992
106342
  process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
@@ -106228,7 +106578,7 @@ ${labelWithResume}` : labelWithResume,
106228
106578
  verb: "voice-ondemand.sendVoice",
106229
106579
  ...threadId != null ? { threadId } : {}
106230
106580
  }),
106231
- sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy15.InputFile(Buffer.from(audio)), sendOpts), {
106581
+ sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy16.InputFile(Buffer.from(audio)), sendOpts), {
106232
106582
  chat_id: chatId,
106233
106583
  verb: "voice-ondemand.sendVoice",
106234
106584
  ...threadId != null ? { threadId } : {}
@@ -106487,7 +106837,7 @@ async function initGatewayBot() {
106487
106837
  `);
106488
106838
  process.exit(1);
106489
106839
  }
106490
- bot = new import_grammy15.Bot(TOKEN);
106840
+ bot = new import_grammy16.Bot(TOKEN);
106491
106841
  installTgPostLogger(bot);
106492
106842
  installRichMarkdownGuard(bot);
106493
106843
  installUpdateTap(bot, (line) => process.stderr.write(line));
@@ -106842,7 +107192,7 @@ async function startGateway() {
106842
107192
  return;
106843
107193
  }
106844
107194
  })();
106845
- const resolvedAgentDirForBootCard = agentDir ?? join60(homedir19(), ".switchroom", "agents", agentSlug);
107195
+ const resolvedAgentDirForBootCard = agentDir ?? join61(homedir20(), ".switchroom", "agents", agentSlug);
106846
107196
  const handle = await startBootCard(chatId, threadId, botApiForCard, {
106847
107197
  agentName: agentDisplayName,
106848
107198
  agentSlug,
@@ -106856,8 +107206,8 @@ async function startGateway() {
106856
107206
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
106857
107207
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
106858
107208
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
106859
- configSnapshotPath: join60(resolvedAgentDirForBootCard, ".config-snapshot.json"),
106860
- bootCardStatePath: join60(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
107209
+ configSnapshotPath: join61(resolvedAgentDirForBootCard, ".config-snapshot.json"),
107210
+ bootCardStatePath: join61(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
106861
107211
  floodStatePath: FLOOD_STATE_PATH,
106862
107212
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
106863
107213
  }, ackMsgId);
@@ -106888,10 +107238,10 @@ async function startGateway() {
106888
107238
  try {
106889
107239
  const smAgentDir = resolveAgentDirFromEnv();
106890
107240
  if (smAgentDir) {
106891
- const activePath = join60(smAgentDir, ".active-session-model");
106892
- if (existsSync54(activePath)) {
107241
+ const activePath = join61(smAgentDir, ".active-session-model");
107242
+ if (existsSync55(activePath)) {
106893
107243
  try {
106894
- const launched = readFileSync58(activePath, "utf8").trim();
107244
+ const launched = readFileSync59(activePath, "utf8").trim();
106895
107245
  const configured = (() => {
106896
107246
  const d = switchroomExecJson(["agent", "list"]);
106897
107247
  const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
@@ -106924,29 +107274,29 @@ async function startGateway() {
106924
107274
  deliverModelSwitchBootNotice({
106925
107275
  ...modelBootCardDeps,
106926
107276
  confirmation,
106927
- hasSessionModelAlert: existsSync54(join60(smAgentDir, ".session-model-alert"))
107277
+ hasSessionModelAlert: existsSync55(join61(smAgentDir, ".session-model-alert"))
106928
107278
  });
106929
107279
  }
106930
107280
  } catch {}
106931
107281
  }
106932
- const activeEffortPath = join60(smAgentDir, ".active-session-effort");
106933
- if (existsSync54(activeEffortPath)) {
107282
+ const activeEffortPath = join61(smAgentDir, ".active-session-effort");
107283
+ if (existsSync55(activeEffortPath)) {
106934
107284
  try {
106935
- const launchedEffort = readFileSync58(activeEffortPath, "utf8").trim();
107285
+ const launchedEffort = readFileSync59(activeEffortPath, "utf8").trim();
106936
107286
  const configuredEffort = getConfiguredEffortForPersist();
106937
107287
  sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
106938
107288
  } catch {}
106939
107289
  }
106940
- const alertPath = join60(smAgentDir, ".session-model-alert");
106941
- if (existsSync54(alertPath)) {
107290
+ const alertPath = join61(smAgentDir, ".session-model-alert");
107291
+ if (existsSync55(alertPath)) {
106942
107292
  let alertText = null;
106943
107293
  try {
106944
- alertText = readFileSync58(alertPath, "utf8").trim();
107294
+ alertText = readFileSync59(alertPath, "utf8").trim();
106945
107295
  } catch {
106946
107296
  alertText = null;
106947
107297
  }
106948
107298
  try {
106949
- unlinkSync26(alertPath);
107299
+ unlinkSync27(alertPath);
106950
107300
  } catch {}
106951
107301
  if (alertText && alertText.length > 0) {
106952
107302
  const operators = loadAccess().allowFrom;
@@ -107442,7 +107792,7 @@ async function startGateway() {
107442
107792
  await runnerHandle.task();
107443
107793
  return;
107444
107794
  } catch (err) {
107445
- if (err instanceof import_grammy15.GrammyError && err.error_code === 409) {
107795
+ if (err instanceof import_grammy16.GrammyError && err.error_code === 409) {
107446
107796
  const delay = Math.min(1000 * attempt, 15000);
107447
107797
  const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
107448
107798
  process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}