switchroom 0.19.13 → 0.19.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cli/switchroom.js +1 -1
  2. package/dist/host-control/main.js +4 -2
  3. package/package.json +1 -1
  4. package/telegram-plugin/bridge/bridge.ts +1 -1
  5. package/telegram-plugin/dist/bridge/bridge.js +1 -1
  6. package/telegram-plugin/dist/gateway/gateway.js +1027 -509
  7. package/telegram-plugin/dist/server.js +1 -1
  8. package/telegram-plugin/gateway/forward-origin.ts +6 -1
  9. package/telegram-plugin/gateway/gateway.ts +4 -0
  10. package/telegram-plugin/gateway/narrative-lane.ts +11 -0
  11. package/telegram-plugin/gateway/outbound-send-path.ts +9 -3
  12. package/telegram-plugin/gateway/outbox-sweep.ts +73 -5
  13. package/telegram-plugin/gateway/rich-message-handler.ts +235 -0
  14. package/telegram-plugin/gateway/stream-render.ts +107 -15
  15. package/telegram-plugin/gateway/unhandled-message.ts +14 -0
  16. package/telegram-plugin/hooks/narration-classify.d.mts +23 -0
  17. package/telegram-plugin/hooks/narration-classify.mjs +210 -0
  18. package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +33 -7
  19. package/telegram-plugin/hooks/silent-end-scan.mjs +171 -85
  20. package/telegram-plugin/narrative-flush.ts +35 -0
  21. package/telegram-plugin/outbox.ts +87 -0
  22. package/telegram-plugin/shown-ledger.ts +145 -0
  23. package/telegram-plugin/silent-end.ts +42 -0
  24. package/telegram-plugin/tests/backstop-exactly-once.test.ts +335 -0
  25. package/telegram-plugin/tests/catch-all-unhandled-message.test.ts +14 -0
  26. package/telegram-plugin/tests/forward-origin.test.ts +20 -0
  27. package/telegram-plugin/tests/forwarded-rich-message.test.ts +305 -0
  28. package/telegram-plugin/tests/gateway-handler-registration-wiring.test.ts +1 -0
  29. package/telegram-plugin/tests/narration-leak-3513.test.ts +352 -0
  30. package/telegram-plugin/tests/outbox-reply-then-recap-e2e.test.ts +600 -0
  31. package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +19 -11
  32. package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +42 -13
  33. package/telegram-plugin/tests/silent-end.test.ts +7 -1
  34. package/telegram-plugin/tests/turn-flush-safety.test.ts +35 -3
  35. package/telegram-plugin/turn-flush-safety.ts +66 -53
@@ -29091,8 +29091,8 @@ __export(exports_history, {
29091
29091
  _resetForTests: () => _resetForTests,
29092
29092
  MIN_PREFIX_MATCH_CHARS: () => MIN_PREFIX_MATCH_CHARS2
29093
29093
  });
29094
- import { chmodSync as chmodSync9, existsSync as existsSync26, mkdirSync as mkdirSync27 } from "fs";
29095
- import { join as join30 } from "path";
29094
+ import { chmodSync as chmodSync9, existsSync as existsSync27, mkdirSync as mkdirSync28 } from "fs";
29095
+ import { join as join31 } from "path";
29096
29096
  function loadDatabaseClass2() {
29097
29097
  if (DatabaseClass2 != null)
29098
29098
  return DatabaseClass2;
@@ -29123,8 +29123,8 @@ function initHistory2(stateDir, retentionDays = 30) {
29123
29123
  if (db2 != null)
29124
29124
  return;
29125
29125
  const Database = loadDatabaseClass2();
29126
- mkdirSync27(stateDir, { recursive: true, mode: 448 });
29127
- const path2 = join30(stateDir, "history.db");
29126
+ mkdirSync28(stateDir, { recursive: true, mode: 448 });
29127
+ const path2 = join31(stateDir, "history.db");
29128
29128
  dbPath2 = path2;
29129
29129
  db2 = new Database(path2, { create: true });
29130
29130
  db2.exec("PRAGMA journal_mode = WAL");
@@ -29190,7 +29190,7 @@ function initHistory2(stateDir, retentionDays = 30) {
29190
29190
  }
29191
29191
  for (const suffix of ["", "-shm", "-wal"]) {
29192
29192
  const f = path2 + suffix;
29193
- if (existsSync26(f)) {
29193
+ if (existsSync27(f)) {
29194
29194
  try {
29195
29195
  chmodSync9(f, 420);
29196
29196
  } catch {}
@@ -29242,7 +29242,7 @@ function checkpointWal2() {
29242
29242
  if (dbPath2) {
29243
29243
  for (const suffix of ["-shm", "-wal"]) {
29244
29244
  const f = dbPath2 + suffix;
29245
- if (existsSync26(f)) {
29245
+ if (existsSync27(f)) {
29246
29246
  try {
29247
29247
  chmodSync9(f, 420);
29248
29248
  } catch {}
@@ -29489,7 +29489,7 @@ var FLUSH_SUPPRESSION_WINDOW_MS = 2000;
29489
29489
  var init_turn_flush_suppression = () => {};
29490
29490
 
29491
29491
  // ../src/util/atomic.ts
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";
29492
+ import { closeSync as closeSync6, constants as constants2, fchmodSync, fchownSync, fsyncSync as fsyncSync2, openSync as openSync6, renameSync as renameSync13, rmSync as rmSync5, writeSync as writeSync4 } from "node:fs";
29493
29493
  var TMP_OPEN_FLAGS;
29494
29494
  var init_atomic = __esm(() => {
29495
29495
  TMP_OPEN_FLAGS = constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0);
@@ -32451,7 +32451,7 @@ var require_util = __commonJS((exports2) => {
32451
32451
  return path2;
32452
32452
  }
32453
32453
  exports2.normalize = normalize;
32454
- function join38(aRoot, aPath) {
32454
+ function join39(aRoot, aPath) {
32455
32455
  if (aRoot === "") {
32456
32456
  aRoot = ".";
32457
32457
  }
@@ -32483,7 +32483,7 @@ var require_util = __commonJS((exports2) => {
32483
32483
  }
32484
32484
  return joined;
32485
32485
  }
32486
- exports2.join = join38;
32486
+ exports2.join = join39;
32487
32487
  exports2.isAbsolute = function(aPath) {
32488
32488
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
32489
32489
  };
@@ -32656,7 +32656,7 @@ var require_util = __commonJS((exports2) => {
32656
32656
  parsed.path = parsed.path.substring(0, index2 + 1);
32657
32657
  }
32658
32658
  }
32659
- sourceURL = join38(urlGenerate(parsed), sourceURL);
32659
+ sourceURL = join39(urlGenerate(parsed), sourceURL);
32660
32660
  }
32661
32661
  return normalize(sourceURL);
32662
32662
  }
@@ -35261,19 +35261,19 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
35261
35261
  }
35262
35262
 
35263
35263
  // gateway/quota-cache.ts
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";
35264
+ import { existsSync as existsSync43, readFileSync as readFileSync44, writeFileSync as writeFileSync37, mkdirSync as mkdirSync38 } from "fs";
35265
+ import { join as join48, dirname as dirname15 } from "path";
35266
35266
  function defaultCachePath() {
35267
- return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join47(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35267
+ return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join48(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35268
35268
  }
35269
35269
  function readQuotaCache(opts = {}) {
35270
35270
  const path2 = opts.path ?? defaultCachePath();
35271
35271
  const now = opts.now ?? Date.now();
35272
- if (!existsSync42(path2))
35272
+ if (!existsSync43(path2))
35273
35273
  return null;
35274
35274
  let entry;
35275
35275
  try {
35276
- entry = JSON.parse(readFileSync43(path2, "utf8"));
35276
+ entry = JSON.parse(readFileSync44(path2, "utf8"));
35277
35277
  } catch {
35278
35278
  return null;
35279
35279
  }
@@ -35299,8 +35299,8 @@ function writeQuotaCache(result, opts = {}) {
35299
35299
  result
35300
35300
  };
35301
35301
  try {
35302
- mkdirSync37(dirname15(path2), { recursive: true });
35303
- writeFileSync36(path2, JSON.stringify(entry, null, 2), { mode: 384 });
35302
+ mkdirSync38(dirname15(path2), { recursive: true });
35303
+ writeFileSync37(path2, JSON.stringify(entry, null, 2), { mode: 384 });
35304
35304
  } catch {}
35305
35305
  }
35306
35306
  var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
@@ -35310,8 +35310,8 @@ var init_quota_cache = __esm(() => {
35310
35310
  });
35311
35311
 
35312
35312
  // gateway/boot-probes.ts
35313
- import { readFileSync as readFileSync44, readdirSync as readdirSync10, existsSync as existsSync43 } from "fs";
35314
- import { join as join48 } from "path";
35313
+ import { readFileSync as readFileSync45, readdirSync as readdirSync10, existsSync as existsSync44 } from "fs";
35314
+ import { join as join49 } from "path";
35315
35315
  import { execFile as execFileCb } from "child_process";
35316
35316
  import { promisify } from "util";
35317
35317
  async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
@@ -35353,11 +35353,11 @@ function mapPlan(billingType, hasExtra) {
35353
35353
  }
35354
35354
  async function probeAccount(agentDir) {
35355
35355
  return withTimeout("Account", (async () => {
35356
- const claudeDir = join48(agentDir, ".claude");
35357
- const claudeJsonPath = join48(claudeDir, ".claude.json");
35356
+ const claudeDir = join49(agentDir, ".claude");
35357
+ const claudeJsonPath = join49(claudeDir, ".claude.json");
35358
35358
  let cfg = {};
35359
35359
  try {
35360
- const raw = readFileSync44(claudeJsonPath, "utf8");
35360
+ const raw = readFileSync45(claudeJsonPath, "utf8");
35361
35361
  cfg = JSON.parse(raw);
35362
35362
  } catch {
35363
35363
  return { status: "fail", label: "Account", detail: "no .claude.json" };
@@ -35375,12 +35375,12 @@ async function probeAccount(agentDir) {
35375
35375
  let tokenStr = "";
35376
35376
  let status = "ok";
35377
35377
  for (const candidate of [
35378
- join48(claudeDir, ".oauth-token.meta.json"),
35379
- join48(claudeDir, "accounts", "default", ".oauth-token.meta.json")
35378
+ join49(claudeDir, ".oauth-token.meta.json"),
35379
+ join49(claudeDir, "accounts", "default", ".oauth-token.meta.json")
35380
35380
  ]) {
35381
- if (existsSync43(candidate)) {
35381
+ if (existsSync44(candidate)) {
35382
35382
  try {
35383
- const meta = JSON.parse(readFileSync44(candidate, "utf8"));
35383
+ const meta = JSON.parse(readFileSync45(candidate, "utf8"));
35384
35384
  if (meta.expiresAt) {
35385
35385
  tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
35386
35386
  const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
@@ -35555,9 +35555,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
35555
35555
  if (!cgroup)
35556
35556
  return null;
35557
35557
  const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
35558
- if (!existsSync43(procsPath))
35558
+ if (!existsSync44(procsPath))
35559
35559
  return null;
35560
- const pidsRaw = readFileSync44(procsPath, "utf-8");
35560
+ const pidsRaw = readFileSync45(procsPath, "utf-8");
35561
35561
  const pids = pidsRaw.split(`
35562
35562
  `).map((s) => s.trim()).filter(Boolean);
35563
35563
  if (pids.length === 0)
@@ -35570,7 +35570,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
35570
35570
  let rss = 0;
35571
35571
  let comm = "";
35572
35572
  try {
35573
- const status = readFileSync44(`/proc/${pid}/status`, "utf-8");
35573
+ const status = readFileSync45(`/proc/${pid}/status`, "utf-8");
35574
35574
  const rssLine = status.split(`
35575
35575
  `).find((l) => l.startsWith("VmRSS:"));
35576
35576
  if (rssLine) {
@@ -35582,7 +35582,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
35582
35582
  continue;
35583
35583
  }
35584
35584
  try {
35585
- comm = readFileSync44(`/proc/${pid}/comm`, "utf-8").trim();
35585
+ comm = readFileSync45(`/proc/${pid}/comm`, "utf-8").trim();
35586
35586
  } catch {}
35587
35587
  candidates.push({ pid, rss, comm });
35588
35588
  }
@@ -35762,9 +35762,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
35762
35762
  let claudeDirForProbe = null;
35763
35763
  for (const candidate of [
35764
35764
  claudeConfigDir,
35765
- join48(claudeConfigDir, "accounts", "default")
35765
+ join49(claudeConfigDir, "accounts", "default")
35766
35766
  ]) {
35767
- if (existsSync43(join48(candidate, ".oauth-token"))) {
35767
+ if (existsSync44(join49(candidate, ".oauth-token"))) {
35768
35768
  claudeDirForProbe = candidate;
35769
35769
  break;
35770
35770
  }
@@ -35829,7 +35829,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
35829
35829
  }
35830
35830
  function readContainerBootTimeMsForProbe() {
35831
35831
  try {
35832
- const stat1 = readFileSync44("/proc/1/stat", "utf8");
35832
+ const stat1 = readFileSync45("/proc/1/stat", "utf8");
35833
35833
  const lastParen = stat1.lastIndexOf(")");
35834
35834
  if (lastParen < 0)
35835
35835
  return null;
@@ -35837,7 +35837,7 @@ function readContainerBootTimeMsForProbe() {
35837
35837
  const starttimeTicks = Number(after[19]);
35838
35838
  if (!Number.isFinite(starttimeTicks))
35839
35839
  return null;
35840
- const procStat = readFileSync44("/proc/stat", "utf8");
35840
+ const procStat = readFileSync45("/proc/stat", "utf8");
35841
35841
  const btimeLine = procStat.split(`
35842
35842
  `).find((l) => l.startsWith("btime "));
35843
35843
  if (!btimeLine)
@@ -35935,7 +35935,7 @@ async function probeUds(label, socketPath, opts = {}) {
35935
35935
  }
35936
35936
  return withTimeout(label, (async () => {
35937
35937
  if (!opts.connectImpl) {
35938
- if (!existsSync43(socketPath)) {
35938
+ if (!existsSync44(socketPath)) {
35939
35939
  return {
35940
35940
  status: "fail",
35941
35941
  label,
@@ -35999,7 +35999,7 @@ async function probeSkills(agentDir, opts = {}) {
35999
35999
  return withTimeout("Skills", (async () => {
36000
36000
  const fs2 = opts.fs ?? realSkillsFs;
36001
36001
  const max = opts.maxNamesShown ?? 3;
36002
- const skillsDir = join48(agentDir, ".claude", "skills");
36002
+ const skillsDir = join49(agentDir, ".claude", "skills");
36003
36003
  if (!fs2.exists(skillsDir)) {
36004
36004
  return { status: "ok", label: "Skills", detail: "no skills dir" };
36005
36005
  }
@@ -36014,17 +36014,17 @@ async function probeSkills(agentDir, opts = {}) {
36014
36014
  }
36015
36015
  const dangling = [];
36016
36016
  for (const name of entries) {
36017
- const skillPath = join48(skillsDir, name);
36017
+ const skillPath = join49(skillsDir, name);
36018
36018
  if (!fs2.exists(skillPath)) {
36019
36019
  dangling.push(name);
36020
36020
  continue;
36021
36021
  }
36022
- const skillMd = join48(skillPath, "SKILL.md");
36022
+ const skillMd = join49(skillPath, "SKILL.md");
36023
36023
  if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
36024
36024
  continue;
36025
36025
  }
36026
36026
  }
36027
- const overlayDir = opts.overlaySkillsDir ?? join48(agentDir, "skills.d");
36027
+ const overlayDir = opts.overlaySkillsDir ?? join49(agentDir, "skills.d");
36028
36028
  const overlaySlugs = new Set;
36029
36029
  if (fs2.exists(overlayDir)) {
36030
36030
  let overlayEntries = [];
@@ -36066,8 +36066,8 @@ function renderBucketedSkills(switchroom, agent) {
36066
36066
  }
36067
36067
  async function probeConnections(agentDir, opts = {}) {
36068
36068
  return withTimeout("Connections", (async () => {
36069
- const path2 = join48(agentDir, ".claude", "connection-health.json");
36070
- const read = opts.readFileImpl ?? ((p) => readFileSync44(p, "utf8"));
36069
+ const path2 = join49(agentDir, ".claude", "connection-health.json");
36070
+ const read = opts.readFileImpl ?? ((p) => readFileSync45(p, "utf8"));
36071
36071
  let issues = [];
36072
36072
  try {
36073
36073
  const parsed = JSON.parse(read(path2));
@@ -36098,24 +36098,24 @@ var init_boot_probes = __esm(() => {
36098
36098
  execFile = promisify(execFileCb);
36099
36099
  realProcFs = {
36100
36100
  readdir: (p) => readdirSync10(p),
36101
- readFile: (p) => readFileSync44(p, "utf-8")
36101
+ readFile: (p) => readFileSync45(p, "utf-8")
36102
36102
  };
36103
36103
  realSchedulerFs = {
36104
- readFile: (p) => readFileSync44(p, "utf-8"),
36104
+ readFile: (p) => readFileSync45(p, "utf-8"),
36105
36105
  mtimeMs: (p) => {
36106
36106
  const { statSync: statSync14 } = __require("fs");
36107
36107
  return statSync14(p).mtimeMs;
36108
36108
  },
36109
- exists: (p) => existsSync43(p)
36109
+ exists: (p) => existsSync44(p)
36110
36110
  };
36111
36111
  realSkillsFs = {
36112
36112
  readdir: (p) => readdirSync10(p),
36113
- exists: (p) => existsSync43(p)
36113
+ exists: (p) => existsSync44(p)
36114
36114
  };
36115
36115
  });
36116
36116
 
36117
36117
  // gateway/boot-issue-cache.ts
36118
- import { existsSync as existsSync44, readFileSync as readFileSync45, writeFileSync as writeFileSync37, mkdirSync as mkdirSync38, renameSync as renameSync18 } from "fs";
36118
+ import { existsSync as existsSync45, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync39, renameSync as renameSync19 } from "fs";
36119
36119
  import { dirname as dirname16 } from "path";
36120
36120
  function fingerprintProbe(key, r) {
36121
36121
  if (r.status === "ok")
@@ -36195,11 +36195,11 @@ function diffProbes(probes, cache, opts = {}) {
36195
36195
  return out;
36196
36196
  }
36197
36197
  function loadCache(path2, now = Date.now) {
36198
- if (!existsSync44(path2))
36198
+ if (!existsSync45(path2))
36199
36199
  return { ...EMPTY_CACHE, probes: {} };
36200
36200
  let raw;
36201
36201
  try {
36202
- raw = readFileSync45(path2, "utf-8");
36202
+ raw = readFileSync46(path2, "utf-8");
36203
36203
  } catch {
36204
36204
  return { ...EMPTY_CACHE, probes: {} };
36205
36205
  }
@@ -36208,7 +36208,7 @@ function loadCache(path2, now = Date.now) {
36208
36208
  parsed = JSON.parse(raw);
36209
36209
  } catch {
36210
36210
  try {
36211
- renameSync18(path2, `${path2}.corrupt-${now()}`);
36211
+ renameSync19(path2, `${path2}.corrupt-${now()}`);
36212
36212
  } catch {}
36213
36213
  return { ...EMPTY_CACHE, probes: {} };
36214
36214
  }
@@ -36242,10 +36242,10 @@ function applyAndSave(path2, cache, diff) {
36242
36242
  }
36243
36243
  }
36244
36244
  try {
36245
- mkdirSync38(dirname16(path2), { recursive: true });
36245
+ mkdirSync39(dirname16(path2), { recursive: true });
36246
36246
  const tmp = `${path2}.tmp`;
36247
- writeFileSync37(tmp, JSON.stringify(next), { mode: 384 });
36248
- renameSync18(tmp, path2);
36247
+ writeFileSync38(tmp, JSON.stringify(next), { mode: 384 });
36248
+ renameSync19(tmp, path2);
36249
36249
  } catch {}
36250
36250
  return next;
36251
36251
  }
@@ -36257,14 +36257,14 @@ var init_boot_issue_cache = __esm(() => {
36257
36257
  });
36258
36258
 
36259
36259
  // gateway/config-snapshot.ts
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";
36260
+ import { createHash as createHash5 } from "crypto";
36261
+ import { existsSync as existsSync46, readFileSync as readFileSync47, writeFileSync as writeFileSync39, mkdirSync as mkdirSync40, renameSync as renameSync20 } from "fs";
36262
36262
  import { dirname as dirname17 } from "path";
36263
36263
  function hashStringArray(items) {
36264
36264
  if (!items || items.length === 0)
36265
36265
  return null;
36266
36266
  const sorted = [...items].sort();
36267
- const raw = createHash4("sha256").update(sorted.join("\x00")).digest("hex");
36267
+ const raw = createHash5("sha256").update(sorted.join("\x00")).digest("hex");
36268
36268
  return raw.slice(0, 12);
36269
36269
  }
36270
36270
  function normalizeModel(model) {
@@ -36323,11 +36323,11 @@ function renderConfigChangeDim(dim) {
36323
36323
  }
36324
36324
  }
36325
36325
  function loadSnapshot(path2, now = Date.now) {
36326
- if (!existsSync45(path2))
36326
+ if (!existsSync46(path2))
36327
36327
  return null;
36328
36328
  let raw;
36329
36329
  try {
36330
- raw = readFileSync46(path2, "utf-8");
36330
+ raw = readFileSync47(path2, "utf-8");
36331
36331
  } catch {
36332
36332
  return null;
36333
36333
  }
@@ -36336,7 +36336,7 @@ function loadSnapshot(path2, now = Date.now) {
36336
36336
  parsed = JSON.parse(raw);
36337
36337
  } catch {
36338
36338
  try {
36339
- renameSync19(path2, `${path2}.corrupt-${now()}`);
36339
+ renameSync20(path2, `${path2}.corrupt-${now()}`);
36340
36340
  } catch {}
36341
36341
  return null;
36342
36342
  }
@@ -36357,10 +36357,10 @@ function loadSnapshot(path2, now = Date.now) {
36357
36357
  }
36358
36358
  function persistSnapshot(path2, snapshot) {
36359
36359
  try {
36360
- mkdirSync39(dirname17(path2), { recursive: true });
36360
+ mkdirSync40(dirname17(path2), { recursive: true });
36361
36361
  const tmp = `${path2}.tmp`;
36362
- writeFileSync38(tmp, JSON.stringify(snapshot), { mode: 384 });
36363
- renameSync19(tmp, path2);
36362
+ writeFileSync39(tmp, JSON.stringify(snapshot), { mode: 384 });
36363
+ renameSync20(tmp, path2);
36364
36364
  } catch {}
36365
36365
  }
36366
36366
  var init_config_snapshot = __esm(() => {
@@ -36368,13 +36368,13 @@ var init_config_snapshot = __esm(() => {
36368
36368
  });
36369
36369
 
36370
36370
  // gateway/boot-card-msgid.ts
36371
- import { readFileSync as readFileSync47, writeFileSync as writeFileSync39 } from "node:fs";
36371
+ import { readFileSync as readFileSync48, writeFileSync as writeFileSync40 } from "node:fs";
36372
36372
  function bootCardChatKey(chatId, threadId) {
36373
36373
  return `${chatId}:${threadId ?? ""}`;
36374
36374
  }
36375
36375
  function readStore(path2) {
36376
36376
  try {
36377
- const parsed = JSON.parse(readFileSync47(path2, "utf8"));
36377
+ const parsed = JSON.parse(readFileSync48(path2, "utf8"));
36378
36378
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
36379
36379
  return parsed;
36380
36380
  }
@@ -36393,7 +36393,7 @@ function saveBootCardMsgId(path2, chatKey3, messageId) {
36393
36393
  if (store2[chatKey3] === messageId)
36394
36394
  return;
36395
36395
  store2[chatKey3] = messageId;
36396
- writeFileSync39(path2, JSON.stringify(store2), "utf8");
36396
+ writeFileSync40(path2, JSON.stringify(store2), "utf8");
36397
36397
  } catch {}
36398
36398
  }
36399
36399
  var init_boot_card_msgid = () => {};
@@ -36411,8 +36411,8 @@ __export(exports_boot_card, {
36411
36411
  readRoutingMode: () => readRoutingMode,
36412
36412
  containerBootStartMs: () => containerBootStartMs
36413
36413
  });
36414
- import { join as join49 } from "path";
36415
- import { readFileSync as readFileSync48, statSync as statSync14 } from "fs";
36414
+ import { join as join50 } from "path";
36415
+ import { readFileSync as readFileSync49, statSync as statSync14 } from "fs";
36416
36416
  function resolvePersonaName(slug, loadConfig3) {
36417
36417
  try {
36418
36418
  const config = loadConfig3 ? loadConfig3() : loadConfig();
@@ -36434,7 +36434,7 @@ function shouldSkipDuplicateBootCard(gate, site) {
36434
36434
  }
36435
36435
  return { skip: false };
36436
36436
  }
36437
- function containerBootStartMs(fsImpl = { readFileSync: readFileSync48 }) {
36437
+ function containerBootStartMs(fsImpl = { readFileSync: readFileSync49 }) {
36438
36438
  try {
36439
36439
  const btimeLine = fsImpl.readFileSync("/proc/stat", "utf-8").split(`
36440
36440
  `).find((l) => l.startsWith("btime "));
@@ -36455,8 +36455,8 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync48 }) {
36455
36455
  return null;
36456
36456
  }
36457
36457
  }
36458
- function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync48, statSync: statSync14 }) {
36459
- const path2 = join49(agentDir, ".routing-mode");
36458
+ function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync49, statSync: statSync14 }) {
36459
+ const path2 = join50(agentDir, ".routing-mode");
36460
36460
  try {
36461
36461
  const raw = fsImpl.readFileSync(path2, "utf-8");
36462
36462
  const line = raw.split(`
@@ -36563,7 +36563,7 @@ function renderBootCard(opts) {
36563
36563
  return stackCardLines(flatLines);
36564
36564
  }
36565
36565
  async function runAllProbes(opts) {
36566
- const claudeDir = join49(opts.agentDir, ".claude");
36566
+ const claudeDir = join50(opts.agentDir, ".claude");
36567
36567
  const probes = {};
36568
36568
  const slug = opts.agentSlug ?? opts.agentName;
36569
36569
  await Promise.allSettled([
@@ -37417,7 +37417,7 @@ __export(exports_tmux2, {
37417
37417
  captureAgentPane: () => captureAgentPane2
37418
37418
  });
37419
37419
  import { execFileSync as execFileSync8 } from "node:child_process";
37420
- import { chmodSync as chmodSync13, mkdirSync as mkdirSync47, readdirSync as readdirSync13, statSync as statSync19, unlinkSync as unlinkSync26, writeFileSync as writeFileSync48 } from "node:fs";
37420
+ import { chmodSync as chmodSync13, mkdirSync as mkdirSync48, readdirSync as readdirSync13, statSync as statSync19, unlinkSync as unlinkSync26, writeFileSync as writeFileSync49 } from "node:fs";
37421
37421
  import { resolve as resolve12 } from "node:path";
37422
37422
  function captureAgentPane2(opts) {
37423
37423
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -37429,7 +37429,7 @@ function captureAgentPane2(opts) {
37429
37429
  const reasonSlug = sanitizeReason2(reason);
37430
37430
  const outPath = resolve12(outDir, `${ts}-${reasonSlug}.txt`);
37431
37431
  try {
37432
- mkdirSync47(outDir, { recursive: true, mode: 448 });
37432
+ mkdirSync48(outDir, { recursive: true, mode: 448 });
37433
37433
  } catch (err) {
37434
37434
  const msg = `mkdir crash-reports failed: ${err.message}`;
37435
37435
  console.error(`[tmux-capture] ${agentName3}: ${msg}`);
@@ -37468,7 +37468,7 @@ function captureAgentPane2(opts) {
37468
37468
  ` + `
37469
37469
  `;
37470
37470
  try {
37471
- writeFileSync48(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
37471
+ writeFileSync49(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
37472
37472
  mode: 384
37473
37473
  });
37474
37474
  } catch (err) {
@@ -38348,7 +38348,7 @@ __export(exports_materialize_bot_token, {
38348
38348
  materializeBotToken: () => materializeBotToken,
38349
38349
  BotTokenMaterializeError: () => BotTokenMaterializeError
38350
38350
  });
38351
- import { existsSync as existsSync54 } from "node:fs";
38351
+ import { existsSync as existsSync55 } from "node:fs";
38352
38352
  function pickConfiguredToken(config, agentName3) {
38353
38353
  if (agentName3) {
38354
38354
  const agent = config.agents?.[agentName3];
@@ -38362,7 +38362,7 @@ function tryDirectVaultRead4(ref, config, passphrase) {
38362
38362
  if (!passphrase)
38363
38363
  return null;
38364
38364
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
38365
- if (!existsSync54(vaultPath))
38365
+ if (!existsSync55(vaultPath))
38366
38366
  return null;
38367
38367
  try {
38368
38368
  const secrets = openVault(passphrase, vaultPath);
@@ -38514,26 +38514,26 @@ var init_approvals_commands = __esm(() => {
38514
38514
  // gateway/gateway.ts
38515
38515
  var import_grammy16 = __toESM(require_mod(), 1);
38516
38516
  var import_runner3 = __toESM(require_mod3(), 1);
38517
- import { randomBytes as randomBytes10, createHash as createHash5 } from "crypto";
38517
+ import { randomBytes as randomBytes10, createHash as createHash6 } from "crypto";
38518
38518
  import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
38519
38519
  import {
38520
- readFileSync as readFileSync59,
38521
- writeFileSync as writeFileSync49,
38522
- mkdirSync as mkdirSync48,
38520
+ readFileSync as readFileSync60,
38521
+ writeFileSync as writeFileSync50,
38522
+ mkdirSync as mkdirSync49,
38523
38523
  readdirSync as readdirSync14,
38524
38524
  rmSync as rmSync6,
38525
38525
  statSync as statSync20,
38526
- renameSync as renameSync22,
38526
+ renameSync as renameSync23,
38527
38527
  realpathSync as realpathSync4,
38528
38528
  chmodSync as chmodSync14,
38529
38529
  openSync as openSync12,
38530
38530
  closeSync as closeSync12,
38531
- existsSync as existsSync55,
38531
+ existsSync as existsSync56,
38532
38532
  unlinkSync as unlinkSync27,
38533
- appendFileSync as appendFileSync8
38533
+ appendFileSync as appendFileSync9
38534
38534
  } from "fs";
38535
38535
  import { homedir as homedir20 } from "os";
38536
- import { join as join61, sep as sep4, basename as basename15 } from "path";
38536
+ import { join as join62, sep as sep4, basename as basename15 } from "path";
38537
38537
 
38538
38538
  // plugin-logger.ts
38539
38539
  import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
@@ -40819,6 +40819,141 @@ function forwardOriginDateIso(o) {
40819
40819
  return new Date(o.date * 1000).toISOString();
40820
40820
  }
40821
40821
 
40822
+ // gateway/rich-message-handler.ts
40823
+ var MAX_DEPTH = 32;
40824
+ function renderRichText(rt, depth = 0) {
40825
+ if (depth > MAX_DEPTH || rt == null)
40826
+ return "";
40827
+ if (typeof rt === "string")
40828
+ return rt;
40829
+ if (Array.isArray(rt))
40830
+ return rt.map((t) => renderRichText(t, depth + 1)).join("");
40831
+ if (typeof rt !== "object")
40832
+ return "";
40833
+ const node = rt;
40834
+ if (node.type === "custom_emoji")
40835
+ return node.alternative_text ?? "";
40836
+ if (node.type === "mathematical_expression") {
40837
+ return typeof node.expression === "string" ? node.expression : "";
40838
+ }
40839
+ if (node.type === "anchor")
40840
+ return "";
40841
+ if ("text" in node)
40842
+ return renderRichText(node.text, depth + 1);
40843
+ return "";
40844
+ }
40845
+ function renderCaption(caption, depth) {
40846
+ if (caption == null || typeof caption !== "object")
40847
+ return renderRichText(caption, depth);
40848
+ const c = caption;
40849
+ const text = renderRichText(c.text, depth);
40850
+ const credit = renderRichText(c.credit, depth);
40851
+ return [text, credit ? `\u2014 ${credit}` : ""].filter(Boolean).join(" ");
40852
+ }
40853
+ function renderBlocks(blocks, depth) {
40854
+ if (depth > MAX_DEPTH || !Array.isArray(blocks))
40855
+ return "";
40856
+ return blocks.map((b) => renderRichBlock(b, depth + 1)).filter((s) => s.length > 0).join(`
40857
+ `);
40858
+ }
40859
+ function renderRichBlock(block, depth = 0) {
40860
+ if (depth > MAX_DEPTH || block == null || typeof block !== "object")
40861
+ return "";
40862
+ const b = block;
40863
+ switch (b.type) {
40864
+ case "paragraph":
40865
+ case "heading":
40866
+ case "footer":
40867
+ return renderRichText(b.text, depth);
40868
+ case "pre": {
40869
+ const body = renderRichText(b.text, depth);
40870
+ return body.length > 0 ? `\`\`\`${b.language ?? ""}
40871
+ ${body}
40872
+ \`\`\`` : "";
40873
+ }
40874
+ case "divider":
40875
+ return "---";
40876
+ case "mathematical_expression":
40877
+ return typeof b.expression === "string" ? b.expression : "";
40878
+ case "anchor":
40879
+ return "";
40880
+ case "list": {
40881
+ if (!Array.isArray(b.items))
40882
+ return "";
40883
+ return b.items.map((item) => {
40884
+ const body = renderBlocks(item?.blocks, depth);
40885
+ const check = item?.has_checkbox ? item.is_checked ? "[x] " : "[ ] " : "";
40886
+ const label = typeof item?.label === "string" && item.label.length > 0 ? item.label : "-";
40887
+ return body.length > 0 ? `${label} ${check}${body}` : "";
40888
+ }).filter((s) => s.length > 0).join(`
40889
+ `);
40890
+ }
40891
+ case "blockquote": {
40892
+ const body = renderBlocks(b.blocks, depth);
40893
+ const credit = renderRichText(b.credit, depth);
40894
+ const quoted = body.split(`
40895
+ `).map((line) => `> ${line}`).join(`
40896
+ `);
40897
+ return [quoted, credit ? `> \u2014 ${credit}` : ""].filter((s) => s.length > 0).join(`
40898
+ `);
40899
+ }
40900
+ case "pullquote": {
40901
+ const body = renderRichText(b.text, depth);
40902
+ const credit = renderRichText(b.credit, depth);
40903
+ return [body, credit ? `\u2014 ${credit}` : ""].filter(Boolean).join(" ");
40904
+ }
40905
+ case "details": {
40906
+ const summary = renderRichText(b.summary, depth);
40907
+ const body = renderBlocks(b.blocks, depth);
40908
+ return [summary, body].filter((s) => s.length > 0).join(`
40909
+ `);
40910
+ }
40911
+ case "collage":
40912
+ case "slideshow": {
40913
+ const body = renderBlocks(b.blocks, depth);
40914
+ const caption = renderCaption(b.caption, depth);
40915
+ return [body, caption].filter((s) => s.length > 0).join(`
40916
+ `);
40917
+ }
40918
+ case "table": {
40919
+ if (!Array.isArray(b.cells))
40920
+ return "";
40921
+ const rows = b.cells.map((row) => Array.isArray(row) ? row.map((cell) => renderRichText(cell?.text, depth)).join(" | ") : "").filter((s) => s.length > 0).join(`
40922
+ `);
40923
+ const caption = renderRichText(b.caption, depth);
40924
+ return [caption, rows].filter((s) => s.length > 0).join(`
40925
+ `);
40926
+ }
40927
+ case "map":
40928
+ case "animation":
40929
+ case "audio":
40930
+ case "photo":
40931
+ case "video":
40932
+ case "voice_note": {
40933
+ const caption = renderCaption(b.caption, depth);
40934
+ const tag = `[${b.type}]`;
40935
+ return caption.length > 0 ? `${tag} ${caption}` : tag;
40936
+ }
40937
+ case "thinking":
40938
+ return "";
40939
+ default: {
40940
+ const text = renderRichText(b.text, depth);
40941
+ if (text.length > 0)
40942
+ return text;
40943
+ return renderBlocks(b.blocks, depth);
40944
+ }
40945
+ }
40946
+ }
40947
+ function extractRichMessageText(rich) {
40948
+ if (rich == null || typeof rich !== "object")
40949
+ return;
40950
+ const blocks = rich.blocks;
40951
+ const rendered = renderBlocks(blocks, 0).replace(/\n{3,}/g, `
40952
+
40953
+ `).trim();
40954
+ return rendered.length > 0 ? rendered : undefined;
40955
+ }
40956
+
40822
40957
  // gateway/unhandled-message.ts
40823
40958
  var MESSAGE_ENVELOPE_KEYS = new Set([
40824
40959
  "message_id",
@@ -40847,7 +40982,13 @@ var MESSAGE_ENVELOPE_KEYS = new Set([
40847
40982
  "show_caption_above_media",
40848
40983
  "entities",
40849
40984
  "caption_entities",
40850
- "paid_star_count"
40985
+ "paid_star_count",
40986
+ "forward_from",
40987
+ "forward_from_chat",
40988
+ "forward_from_message_id",
40989
+ "forward_signature",
40990
+ "forward_sender_name",
40991
+ "forward_date"
40851
40992
  ]);
40852
40993
  var SERVICE_NOISE_KEYS = new Set([
40853
40994
  "new_chat_members",
@@ -40892,7 +41033,7 @@ function planUnhandledMessage(msg) {
40892
41033
  return { action: "log-only", contentKeys };
40893
41034
  }
40894
41035
  const contentType = contentKeys[0] ?? "unknown";
40895
- const text = (typeof msg.text === "string" ? msg.text : undefined) ?? (typeof msg.caption === "string" ? msg.caption : undefined) ?? `(unhandled message content: ${contentType})`;
41036
+ const text = (typeof msg.text === "string" ? msg.text : undefined) ?? (typeof msg.caption === "string" ? msg.caption : undefined) ?? extractRichMessageText(msg.rich_message) ?? `(unhandled message content: ${contentType})`;
40896
41037
  return { action: "turn", text, contentKeys };
40897
41038
  }
40898
41039
  var TAP_MAX_LINES_PER_MINUTE = 300;
@@ -41091,6 +41232,153 @@ async function handlePassportDataMessage(ctx, deps) {
41091
41232
  await deps.handleRefusal(ctx, "passport_data", PASSPORT_REFUSAL_TEXT);
41092
41233
  }
41093
41234
 
41235
+ // gateway/rich-message-handler.ts
41236
+ var RICH_MESSAGE_EMPTY_TEXT = "(rich message with no extractable text)";
41237
+ var MAX_DEPTH2 = 32;
41238
+ function renderRichText2(rt, depth = 0) {
41239
+ if (depth > MAX_DEPTH2 || rt == null)
41240
+ return "";
41241
+ if (typeof rt === "string")
41242
+ return rt;
41243
+ if (Array.isArray(rt))
41244
+ return rt.map((t) => renderRichText2(t, depth + 1)).join("");
41245
+ if (typeof rt !== "object")
41246
+ return "";
41247
+ const node = rt;
41248
+ if (node.type === "custom_emoji")
41249
+ return node.alternative_text ?? "";
41250
+ if (node.type === "mathematical_expression") {
41251
+ return typeof node.expression === "string" ? node.expression : "";
41252
+ }
41253
+ if (node.type === "anchor")
41254
+ return "";
41255
+ if ("text" in node)
41256
+ return renderRichText2(node.text, depth + 1);
41257
+ return "";
41258
+ }
41259
+ function renderCaption2(caption, depth) {
41260
+ if (caption == null || typeof caption !== "object")
41261
+ return renderRichText2(caption, depth);
41262
+ const c = caption;
41263
+ const text = renderRichText2(c.text, depth);
41264
+ const credit = renderRichText2(c.credit, depth);
41265
+ return [text, credit ? `\u2014 ${credit}` : ""].filter(Boolean).join(" ");
41266
+ }
41267
+ function renderBlocks2(blocks, depth) {
41268
+ if (depth > MAX_DEPTH2 || !Array.isArray(blocks))
41269
+ return "";
41270
+ return blocks.map((b) => renderRichBlock2(b, depth + 1)).filter((s) => s.length > 0).join(`
41271
+ `);
41272
+ }
41273
+ function renderRichBlock2(block, depth = 0) {
41274
+ if (depth > MAX_DEPTH2 || block == null || typeof block !== "object")
41275
+ return "";
41276
+ const b = block;
41277
+ switch (b.type) {
41278
+ case "paragraph":
41279
+ case "heading":
41280
+ case "footer":
41281
+ return renderRichText2(b.text, depth);
41282
+ case "pre": {
41283
+ const body = renderRichText2(b.text, depth);
41284
+ return body.length > 0 ? `\`\`\`${b.language ?? ""}
41285
+ ${body}
41286
+ \`\`\`` : "";
41287
+ }
41288
+ case "divider":
41289
+ return "---";
41290
+ case "mathematical_expression":
41291
+ return typeof b.expression === "string" ? b.expression : "";
41292
+ case "anchor":
41293
+ return "";
41294
+ case "list": {
41295
+ if (!Array.isArray(b.items))
41296
+ return "";
41297
+ return b.items.map((item) => {
41298
+ const body = renderBlocks2(item?.blocks, depth);
41299
+ const check = item?.has_checkbox ? item.is_checked ? "[x] " : "[ ] " : "";
41300
+ const label = typeof item?.label === "string" && item.label.length > 0 ? item.label : "-";
41301
+ return body.length > 0 ? `${label} ${check}${body}` : "";
41302
+ }).filter((s) => s.length > 0).join(`
41303
+ `);
41304
+ }
41305
+ case "blockquote": {
41306
+ const body = renderBlocks2(b.blocks, depth);
41307
+ const credit = renderRichText2(b.credit, depth);
41308
+ const quoted = body.split(`
41309
+ `).map((line) => `> ${line}`).join(`
41310
+ `);
41311
+ return [quoted, credit ? `> \u2014 ${credit}` : ""].filter((s) => s.length > 0).join(`
41312
+ `);
41313
+ }
41314
+ case "pullquote": {
41315
+ const body = renderRichText2(b.text, depth);
41316
+ const credit = renderRichText2(b.credit, depth);
41317
+ return [body, credit ? `\u2014 ${credit}` : ""].filter(Boolean).join(" ");
41318
+ }
41319
+ case "details": {
41320
+ const summary = renderRichText2(b.summary, depth);
41321
+ const body = renderBlocks2(b.blocks, depth);
41322
+ return [summary, body].filter((s) => s.length > 0).join(`
41323
+ `);
41324
+ }
41325
+ case "collage":
41326
+ case "slideshow": {
41327
+ const body = renderBlocks2(b.blocks, depth);
41328
+ const caption = renderCaption2(b.caption, depth);
41329
+ return [body, caption].filter((s) => s.length > 0).join(`
41330
+ `);
41331
+ }
41332
+ case "table": {
41333
+ if (!Array.isArray(b.cells))
41334
+ return "";
41335
+ const rows = b.cells.map((row) => Array.isArray(row) ? row.map((cell) => renderRichText2(cell?.text, depth)).join(" | ") : "").filter((s) => s.length > 0).join(`
41336
+ `);
41337
+ const caption = renderRichText2(b.caption, depth);
41338
+ return [caption, rows].filter((s) => s.length > 0).join(`
41339
+ `);
41340
+ }
41341
+ case "map":
41342
+ case "animation":
41343
+ case "audio":
41344
+ case "photo":
41345
+ case "video":
41346
+ case "voice_note": {
41347
+ const caption = renderCaption2(b.caption, depth);
41348
+ const tag = `[${b.type}]`;
41349
+ return caption.length > 0 ? `${tag} ${caption}` : tag;
41350
+ }
41351
+ case "thinking":
41352
+ return "";
41353
+ default: {
41354
+ const text = renderRichText2(b.text, depth);
41355
+ if (text.length > 0)
41356
+ return text;
41357
+ return renderBlocks2(b.blocks, depth);
41358
+ }
41359
+ }
41360
+ }
41361
+ function extractRichMessageText2(rich) {
41362
+ if (rich == null || typeof rich !== "object")
41363
+ return;
41364
+ const blocks = rich.blocks;
41365
+ const rendered = renderBlocks2(blocks, 0).replace(/\n{3,}/g, `
41366
+
41367
+ `).trim();
41368
+ return rendered.length > 0 ? rendered : undefined;
41369
+ }
41370
+ async function handleRichMessageMessage(ctx, deps) {
41371
+ try {
41372
+ const text = extractRichMessageText2(ctx.message.rich_message) ?? RICH_MESSAGE_EMPTY_TEXT;
41373
+ deps.log(`telegram gateway: inbound rich_message from chat=${ctx.chat?.id ?? "?"} chars=${text.length}
41374
+ `);
41375
+ await deps.handleInbound(ctx, text, undefined);
41376
+ } catch (err) {
41377
+ deps.log(`telegram gateway: rich_message handler error: ${err.message}
41378
+ `);
41379
+ }
41380
+ }
41381
+
41094
41382
  // gateway/stop-command.ts
41095
41383
  function parseStopKeyword2(text) {
41096
41384
  return /^stop[.!]?$/i.test(text.trim());
@@ -42712,6 +43000,8 @@ function buildForwardOriginMeta(origins) {
42712
43000
  if (o.date != null) {
42713
43001
  out[`forwarded_date${suffix}`] = fmtLocalStamp(o.date * 1000, resolveEnvTimezone());
42714
43002
  }
43003
+ if (o.messageId != null)
43004
+ out[`forwarded_message_id${suffix}`] = String(o.messageId);
42715
43005
  });
42716
43006
  return out;
42717
43007
  }
@@ -61243,7 +61533,7 @@ function prefixLines(text4, prefix) {
61243
61533
  `);
61244
61534
  }
61245
61535
  function renderBlockquote(node2) {
61246
- const inner = renderBlocks(node2.children);
61536
+ const inner = renderBlocks3(node2.children);
61247
61537
  if (node2.expandable) {
61248
61538
  const lines = inner.split(`
61249
61539
  `);
@@ -61339,7 +61629,7 @@ function renderBlock(node2) {
61339
61629
  function renderBlocksJoined(blocks, sep2) {
61340
61630
  return blocks.map(renderBlock).join(sep2);
61341
61631
  }
61342
- function renderBlocks(blocks) {
61632
+ function renderBlocks3(blocks) {
61343
61633
  return renderBlocksJoined(blocks, `
61344
61634
 
61345
61635
  `);
@@ -74010,6 +74300,33 @@ function readDeliveredNonces(stateDir) {
74010
74300
  } catch {}
74011
74301
  return set;
74012
74302
  }
74303
+ function isBackstopDeliveredEntry(e) {
74304
+ if (e.deliverySource === "sweep" || e.deliverySource === "flush")
74305
+ return true;
74306
+ if (e.deliverySource === "reply-tool" && e.replyAlreadyDeliveredThisTurn === false)
74307
+ return true;
74308
+ return false;
74309
+ }
74310
+ function backstopAlreadyDelivered(nonce, stateDir) {
74311
+ if (nonce == null || nonce === "")
74312
+ return false;
74313
+ const path2 = join28(resolveOutboxDir(stateDir), JOURNAL_FILE);
74314
+ if (!existsSync24(path2))
74315
+ return false;
74316
+ try {
74317
+ for (const line of readFileSync24(path2, "utf8").split(`
74318
+ `)) {
74319
+ if (!line)
74320
+ continue;
74321
+ try {
74322
+ const e = JSON.parse(line);
74323
+ if (e.turnNonce === nonce && isBackstopDeliveredEntry(e))
74324
+ return true;
74325
+ } catch {}
74326
+ }
74327
+ } catch {}
74328
+ return false;
74329
+ }
74013
74330
  var JOURNAL_KEEP = 2000;
74014
74331
  var JOURNAL_ROTATE_AT = 4000;
74015
74332
  function appendDelivered(entry, stateDir) {
@@ -74053,10 +74370,13 @@ function decideOutboxSweep(input) {
74053
74370
  routable,
74054
74371
  routePrefix = "",
74055
74372
  quietMs = OUTBOX_QUIET_MS,
74056
- maxAgeMs = OUTBOX_MAX_AGE_MS
74373
+ maxAgeMs = OUTBOX_MAX_AGE_MS,
74374
+ shownLedgerHit = false
74057
74375
  } = input;
74058
74376
  if (deliveredNonces.has(record.turnNonce))
74059
74377
  return { action: "skip-journaled" };
74378
+ if (shownLedgerHit)
74379
+ return { action: "skip-ephemeral-shown" };
74060
74380
  const age = now - record.createdAt;
74061
74381
  if (age < quietMs)
74062
74382
  return { action: "skip-quiet" };
@@ -74089,6 +74409,149 @@ function extractTaskId(anchorContent) {
74089
74409
  return m ? m[1] : null;
74090
74410
  }
74091
74411
 
74412
+ // shown-ledger.ts
74413
+ import {
74414
+ existsSync as existsSync25,
74415
+ mkdirSync as mkdirSync26,
74416
+ readFileSync as readFileSync25,
74417
+ renameSync as renameSync9,
74418
+ writeFileSync as writeFileSync23,
74419
+ appendFileSync as appendFileSync5
74420
+ } from "node:fs";
74421
+ import { join as join29 } from "node:path";
74422
+
74423
+ // hooks/narration-classify.mjs
74424
+ import { createHash as createHash4 } from "node:crypto";
74425
+ var SUBSTANTIVE_MIN_CHARS = 200;
74426
+ var NARRATION_OPENER = /^(let me\b|lemme\b|i'?ll\b|i will\b|i am going to\b|i'?m going to\b|i'?m about to\b|going to\b|first,?\s+(?:let me|i'?ll|i will)\b|now,?\s+(?:let me|i'?ll|i will)\b|next,?\s+(?:let me|i'?ll|i will)\b|let'?s\b)/i;
74427
+ var NARRATION_TRAILER = /(?:\.{3}|\u2026|:)\s*$/;
74428
+ function isTrailingNarrationLine(block) {
74429
+ const t = typeof block === "string" ? block.trim() : "";
74430
+ if (t.length === 0 || t.length >= SUBSTANTIVE_MIN_CHARS)
74431
+ return false;
74432
+ if (t.includes(`
74433
+ `))
74434
+ return false;
74435
+ return NARRATION_TRAILER.test(t);
74436
+ }
74437
+ function isNarrationBlock(block) {
74438
+ const s = typeof block === "string" ? block : "";
74439
+ return NARRATION_OPENER.test(s.trimStart()) || isTrailingNarrationLine(s);
74440
+ }
74441
+ function isStructuralNarration(text4, followedByToolUse) {
74442
+ if (followedByToolUse !== true)
74443
+ return false;
74444
+ const t = typeof text4 === "string" ? text4.trim() : "";
74445
+ if (t.length === 0)
74446
+ return true;
74447
+ return isNarrationBlock(t) || t.length < SUBSTANTIVE_MIN_CHARS;
74448
+ }
74449
+ var EPHEMERAL_TOOLS = new Set([
74450
+ "react",
74451
+ "send_typing",
74452
+ "pin_message",
74453
+ "delete_message",
74454
+ "edit_message"
74455
+ ]);
74456
+ function isEphemeralTool(name) {
74457
+ if (typeof name !== "string")
74458
+ return false;
74459
+ const suffix = name.replace(/^mcp__[^_].*?telegram__/, "");
74460
+ return EPHEMERAL_TOOLS.has(suffix);
74461
+ }
74462
+ function selectBackstopDelivery(blocks) {
74463
+ const nonEmpty = (Array.isArray(blocks) ? blocks : []).map((b) => ({
74464
+ text: typeof b?.text === "string" ? b.text.trim() : "",
74465
+ followedByToolUse: b?.followedByToolUse
74466
+ })).filter((b) => b.text.length > 0);
74467
+ if (nonEmpty.length === 0)
74468
+ return null;
74469
+ const run3 = [];
74470
+ for (let i = nonEmpty.length - 1;i >= 0; i--) {
74471
+ if (nonEmpty[i].followedByToolUse === true)
74472
+ break;
74473
+ run3.unshift(nonEmpty[i].text);
74474
+ }
74475
+ if (run3.length > 0)
74476
+ return { text: run3.join(`
74477
+
74478
+ `) };
74479
+ const last = nonEmpty[nonEmpty.length - 1];
74480
+ if (last.text.length >= SUBSTANTIVE_MIN_CHARS)
74481
+ return { text: last.text };
74482
+ return null;
74483
+ }
74484
+ function ledgerHashHex(text4) {
74485
+ return createHash4("sha256").update(String(text4 ?? "").trim(), "utf8").digest("hex");
74486
+ }
74487
+
74488
+ // shown-ledger.ts
74489
+ var SHOWN_LEDGER_FILE = "shown-ledger.jsonl";
74490
+ var SHOWN_LEDGER_KEEP = 2000;
74491
+ var SHOWN_LEDGER_ROTATE_AT = 4000;
74492
+ function shownLedgerPath(stateDir) {
74493
+ return join29(resolveOutboxDir(stateDir), SHOWN_LEDGER_FILE);
74494
+ }
74495
+ function appendShownBlock(turnNonce, text4, stateDir, now = Date.now()) {
74496
+ if (turnNonce == null || turnNonce === "")
74497
+ return;
74498
+ const trimmed = typeof text4 === "string" ? text4.trim() : "";
74499
+ if (trimmed.length === 0)
74500
+ return;
74501
+ const dir = resolveOutboxDir(stateDir);
74502
+ const path2 = shownLedgerPath(stateDir);
74503
+ try {
74504
+ mkdirSync26(dir, { recursive: true });
74505
+ const entry = { turnNonce, hash: ledgerHashHex(trimmed), ts: now };
74506
+ appendFileSync5(path2, JSON.stringify(entry) + `
74507
+ `, { mode: 384 });
74508
+ compactIfLarge(path2);
74509
+ } catch {}
74510
+ }
74511
+ function readShownHashes(turnNonce, stateDir) {
74512
+ const set = new Set;
74513
+ if (turnNonce == null || turnNonce === "")
74514
+ return set;
74515
+ const path2 = shownLedgerPath(stateDir);
74516
+ if (!existsSync25(path2))
74517
+ return set;
74518
+ try {
74519
+ for (const line of readFileSync25(path2, "utf8").split(`
74520
+ `)) {
74521
+ if (!line)
74522
+ continue;
74523
+ try {
74524
+ const e = JSON.parse(line);
74525
+ if (e.turnNonce === turnNonce && typeof e.hash === "string")
74526
+ set.add(e.hash);
74527
+ } catch {}
74528
+ }
74529
+ } catch {}
74530
+ return set;
74531
+ }
74532
+ function isShownBlock(turnNonce, text4, stateDir) {
74533
+ if (turnNonce == null || turnNonce === "")
74534
+ return false;
74535
+ const trimmed = typeof text4 === "string" ? text4.trim() : "";
74536
+ if (trimmed.length === 0)
74537
+ return false;
74538
+ return readShownHashes(turnNonce, stateDir).has(ledgerHashHex(trimmed));
74539
+ }
74540
+ function compactIfLarge(path2) {
74541
+ try {
74542
+ const lines = readFileSync25(path2, "utf8").split(`
74543
+ `).filter((l) => l.length > 0);
74544
+ if (lines.length <= SHOWN_LEDGER_ROTATE_AT)
74545
+ return;
74546
+ const kept = lines.slice(lines.length - SHOWN_LEDGER_KEEP);
74547
+ const tmp = `${path2}.${process.pid}.compact`;
74548
+ writeFileSync23(tmp, kept.join(`
74549
+ `) + `
74550
+ `, { mode: 384 });
74551
+ renameSync9(tmp, path2);
74552
+ } catch {}
74553
+ }
74554
+
74092
74555
  // registry/subagents-schema.ts
74093
74556
  function countRunningBackgroundSubagents(db2) {
74094
74557
  const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
@@ -74206,7 +74669,14 @@ function journalExternalDelivery(args, stateDir, now = Date.now()) {
74206
74669
  const nonce = args.turnNonce;
74207
74670
  if (nonce == null || nonce === "")
74208
74671
  return;
74209
- appendDelivered({ turnNonce: nonce, textSha256: sha256Hex(args.text), tgMessageId: args.tgMessageId, ts: now }, stateDir);
74672
+ appendDelivered({
74673
+ turnNonce: nonce,
74674
+ textSha256: sha256Hex(args.text),
74675
+ tgMessageId: args.tgMessageId,
74676
+ ts: now,
74677
+ deliverySource: args.deliverySource ?? "reply-tool",
74678
+ ...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn }
74679
+ }, stateDir);
74210
74680
  clearOutboxRecord(nonce, stateDir);
74211
74681
  }
74212
74682
 
@@ -74599,8 +75069,8 @@ function monotonicNowMs2() {
74599
75069
  }
74600
75070
 
74601
75071
  // silent-end.ts
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";
75072
+ import { existsSync as existsSync26, readFileSync as readFileSync26, writeFileSync as writeFileSync24, unlinkSync as unlinkSync13, mkdirSync as mkdirSync27 } from "node:fs";
75073
+ import { dirname as dirname11, join as join30 } from "node:path";
74604
75074
  import { homedir as homedir11 } from "node:os";
74605
75075
  var SILENT_END_MAX_RETRIES2 = 2;
74606
75076
  var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
@@ -74615,10 +75085,10 @@ function resolveStateDir3(deps) {
74615
75085
  if (env != null && env !== "")
74616
75086
  return env;
74617
75087
  const home2 = process.env.HOME ?? homedir11();
74618
- return join29(home2, ".claude", "channels", "telegram");
75088
+ return join30(home2, ".claude", "channels", "telegram");
74619
75089
  }
74620
75090
  function resolveStatePath3(deps) {
74621
- return join29(resolveStateDir3(deps), "silent-end-pending.json");
75091
+ return join30(resolveStateDir3(deps), "silent-end-pending.json");
74622
75092
  }
74623
75093
  function emitLog2(deps, line) {
74624
75094
  if (deps?.log != null)
@@ -74630,8 +75100,8 @@ function writeSilentEndState2(args, deps) {
74630
75100
  const statePath = resolveStatePath3(deps);
74631
75101
  let retryCount = 0;
74632
75102
  try {
74633
- if (existsSync25(statePath)) {
74634
- const prev = JSON.parse(readFileSync25(statePath, "utf8"));
75103
+ if (existsSync26(statePath)) {
75104
+ const prev = JSON.parse(readFileSync26(statePath, "utf8"));
74635
75105
  if (prev.turnKey === args.turnKey && typeof prev.retryCount === "number") {
74636
75106
  retryCount = prev.retryCount;
74637
75107
  }
@@ -74647,8 +75117,8 @@ function writeSilentEndState2(args, deps) {
74647
75117
  timestamp: Date.now()
74648
75118
  };
74649
75119
  try {
74650
- mkdirSync26(dirname11(statePath), { recursive: true });
74651
- writeFileSync23(statePath, JSON.stringify(state3), "utf8");
75120
+ mkdirSync27(dirname11(statePath), { recursive: true });
75121
+ writeFileSync24(statePath, JSON.stringify(state3), "utf8");
74652
75122
  emitLog2(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
74653
75123
  `);
74654
75124
  } catch (err) {
@@ -74658,10 +75128,10 @@ function writeSilentEndState2(args, deps) {
74658
75128
  }
74659
75129
  function clearSilentEndState2(turnKey3, deps) {
74660
75130
  const statePath = resolveStatePath3(deps);
74661
- if (!existsSync25(statePath))
75131
+ if (!existsSync26(statePath))
74662
75132
  return;
74663
75133
  try {
74664
- const prev = JSON.parse(readFileSync25(statePath, "utf8"));
75134
+ const prev = JSON.parse(readFileSync26(statePath, "utf8"));
74665
75135
  if (prev.turnKey != null && prev.turnKey !== turnKey3)
74666
75136
  return;
74667
75137
  unlinkSync13(statePath);
@@ -74683,6 +75153,12 @@ function decideCapturedProseDelivery(args, deps) {
74683
75153
  const text4 = typeof state3.pendingText === "string" ? state3.pendingText : "";
74684
75154
  if (text4.trim().length < minChars)
74685
75155
  return { deliver: false, reason: "no-substantive-prose" };
75156
+ if (deps?.isBlockShown?.(state3.turnId, text4) === true) {
75157
+ return { deliver: false, reason: "ephemeral-shown" };
75158
+ }
75159
+ if (deps?.backstopDeliveredNonceHit?.(state3.turnId) === true) {
75160
+ return { deliver: false, reason: "already-delivered" };
75161
+ }
74686
75162
  return { deliver: true, text: text4, reason: "captured-prose" };
74687
75163
  }
74688
75164
  function settleCapturedProseDelivery(outcome, effects) {
@@ -74695,10 +75171,10 @@ function settleCapturedProseDelivery(outcome, effects) {
74695
75171
  }
74696
75172
  function readSilentEndState2(deps) {
74697
75173
  const statePath = resolveStatePath3(deps);
74698
- if (!existsSync25(statePath))
75174
+ if (!existsSync26(statePath))
74699
75175
  return null;
74700
75176
  try {
74701
- return JSON.parse(readFileSync25(statePath, "utf8"));
75177
+ return JSON.parse(readFileSync26(statePath, "utf8"));
74702
75178
  } catch {
74703
75179
  return null;
74704
75180
  }
@@ -75377,7 +75853,7 @@ ${url}`;
75377
75853
  }
75378
75854
  outboundDedup.record(chat_id, threadId, decision.mergedText, Date.now(), turn2?.registryKey ?? null);
75379
75855
  if (isFinalAnswerReply({ text: decision.mergedText, disableNotification: modelDisableNotification })) {
75380
- journalExternalDelivery({ turnNonce: turn2?.turnId ?? null, text: decision.mergedText, tgMessageId: decision.messageId });
75856
+ journalExternalDelivery({ turnNonce: turn2?.turnId ?? null, text: decision.mergedText, tgMessageId: decision.messageId, replyAlreadyDeliveredThisTurn: true });
75381
75857
  }
75382
75858
  silentAnchorEditDone = true;
75383
75859
  } catch (err) {
@@ -75655,7 +76131,7 @@ ${url}`;
75655
76131
  const t = getCurrentTurn();
75656
76132
  outboundDedup.record(chat_id, threadId, text4, Date.now(), t?.registryKey ?? null);
75657
76133
  if (shouldJournalReplySiteDelivery({ text: rawText, disableNotification: modelDisableNotification })) {
75658
- journalExternalDelivery({ turnNonce: t?.turnId ?? null, text: text4, tgMessageId: sentIds[sentIds.length - 1] });
76134
+ journalExternalDelivery({ turnNonce: t?.turnId ?? null, text: text4, tgMessageId: sentIds[sentIds.length - 1], replyAlreadyDeliveredThisTurn: true });
75659
76135
  }
75660
76136
  }
75661
76137
  return { content: [{ type: "text", text: result }] };
@@ -75709,7 +76185,7 @@ async function deliverCapturedProse(deps, args) {
75709
76185
  } catch {}
75710
76186
  }
75711
76187
  outboundDedup.record(chatId, threadId, text4, now, registryKey);
75712
- journalExternalDelivery({ turnNonce: originTurnId, text: text4, tgMessageId: sentIds[sentIds.length - 1] });
76188
+ journalExternalDelivery({ turnNonce: originTurnId, text: text4, tgMessageId: sentIds[sentIds.length - 1], replyAlreadyDeliveredThisTurn: false });
75713
76189
  process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
75714
76190
  `);
75715
76191
  outcome = "sent";
@@ -75818,34 +76294,6 @@ function endsWithSilentMarker(text4) {
75818
76294
  return false;
75819
76295
  return isSilentFlushMarker(lines[lines.length - 1]);
75820
76296
  }
75821
- var FLUSH_SUBSTANTIVE_MIN_CHARS = 200;
75822
- function selectFlushDeliveryText(blocks, followedByToolUse) {
75823
- const candidates = blocks.map((b, i) => ({ text: b.trim(), followedByToolUse: followedByToolUse?.[i] })).filter((c) => c.text.length > 0);
75824
- if (candidates.length === 0)
75825
- return "";
75826
- if (candidates.length === 1)
75827
- return candidates[0].text;
75828
- const answer = candidates[candidates.length - 1].text;
75829
- const preceding = candidates.slice(0, -1);
75830
- const allNarration = preceding.every((c) => c.followedByToolUse === true ? isNarrationBlock(c.text) || c.text.trim().length < FLUSH_SUBSTANTIVE_MIN_CHARS : c.followedByToolUse === false ? false : isNarrationBlock(c.text));
75831
- return allNarration ? answer : candidates.map((c) => c.text).join(`
75832
-
75833
- `);
75834
- }
75835
- var NARRATION_OPENER = /^(let me\b|lemme\b|i'?ll\b|i will\b|i am going to\b|i'?m going to\b|i'?m about to\b|going to\b|first,?\s+(?:let me|i'?ll|i will)\b|now,?\s+(?:let me|i'?ll|i will)\b|next,?\s+(?:let me|i'?ll|i will)\b|let'?s\b)/i;
75836
- var NARRATION_TRAILER = /(?:\.{3}|\u2026|:)\s*$/;
75837
- function isTrailingNarrationLine(block) {
75838
- const t = block.trim();
75839
- if (t.length === 0 || t.length >= FLUSH_SUBSTANTIVE_MIN_CHARS)
75840
- return false;
75841
- if (t.includes(`
75842
- `))
75843
- return false;
75844
- return NARRATION_TRAILER.test(t);
75845
- }
75846
- function isNarrationBlock(block) {
75847
- return NARRATION_OPENER.test(block.trimStart()) || isTrailingNarrationLine(block);
75848
- }
75849
76297
  function decideTurnFlush(input) {
75850
76298
  const flushEnabled = input.flushEnabled !== false;
75851
76299
  if (!flushEnabled)
@@ -75865,10 +76313,14 @@ function decideTurnFlush(input) {
75865
76313
  return { kind: "skip", reason: "silent-marker" };
75866
76314
  if (endsWithSilentMarker(joined))
75867
76315
  return { kind: "skip", reason: "silent-marker" };
75868
- return {
75869
- kind: "flush",
75870
- text: selectFlushDeliveryText(input.capturedText, input.capturedBlockMeta)
75871
- };
76316
+ const selected = selectBackstopDelivery(input.capturedText.map((text4, i) => ({
76317
+ text: text4,
76318
+ followedByToolUse: input.capturedBlockMeta?.[i]
76319
+ })));
76320
+ if (selected == null || selected.text.trim().length === 0) {
76321
+ return { kind: "skip", reason: "empty-text" };
76322
+ }
76323
+ return { kind: "flush", text: selected.text };
75872
76324
  }
75873
76325
 
75874
76326
  // answer-stream.ts
@@ -76258,20 +76710,20 @@ var INTERRUPTED_VIA = new Set([
76258
76710
  init_rich_send();
76259
76711
 
76260
76712
  // runtime-metrics.ts
76261
- import { mkdirSync as mkdirSync28, appendFileSync as appendFileSync5 } from "node:fs";
76262
- import { dirname as dirname12, join as join31 } from "node:path";
76713
+ import { mkdirSync as mkdirSync29, appendFileSync as appendFileSync6 } from "node:fs";
76714
+ import { dirname as dirname12, join as join32 } from "node:path";
76263
76715
  function resolveJsonlPath2() {
76264
76716
  const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
76265
76717
  if (override && override.trim() !== "")
76266
76718
  return override.trim();
76267
76719
  const base = process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent";
76268
- return join31(base, "runtime-metrics.jsonl");
76720
+ return join32(base, "runtime-metrics.jsonl");
76269
76721
  }
76270
76722
  function appendJsonl2(line) {
76271
76723
  const path2 = resolveJsonlPath2();
76272
76724
  try {
76273
- mkdirSync28(dirname12(path2), { recursive: true });
76274
- appendFileSync5(path2, line + `
76725
+ mkdirSync29(dirname12(path2), { recursive: true });
76726
+ appendFileSync6(path2, line + `
76275
76727
  `, "utf-8");
76276
76728
  } catch (err) {
76277
76729
  process.stderr.write(`runtime-metrics: jsonl write failed: ${err.message}
@@ -76761,28 +77213,28 @@ function detectStatusSurfaceDegraded(t) {
76761
77213
  // gateway/turn-active-marker.ts
76762
77214
  import {
76763
77215
  closeSync as closeSync5,
76764
- existsSync as existsSync28,
76765
- mkdirSync as mkdirSync29,
77216
+ existsSync as existsSync29,
77217
+ mkdirSync as mkdirSync30,
76766
77218
  openSync as openSync5,
76767
- readFileSync as readFileSync26,
77219
+ readFileSync as readFileSync27,
76768
77220
  statSync as statSync11,
76769
77221
  unlinkSync as unlinkSync14,
76770
77222
  utimesSync,
76771
- writeFileSync as writeFileSync24
77223
+ writeFileSync as writeFileSync25
76772
77224
  } from "node:fs";
76773
- import { join as join32 } from "node:path";
77225
+ import { join as join33 } from "node:path";
76774
77226
  var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
76775
77227
  var TURN_ACTIVE_HARD_TTL_MS = 10 * 60000;
76776
77228
  function writeTurnActiveMarker(stateDir, marker) {
76777
77229
  try {
76778
- mkdirSync29(stateDir, { recursive: true });
76779
- writeFileSync24(join32(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
77230
+ mkdirSync30(stateDir, { recursive: true });
77231
+ writeFileSync25(join33(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
76780
77232
  `, { mode: 384 });
76781
77233
  } catch {}
76782
77234
  }
76783
77235
  function touchTurnActiveMarker(stateDir) {
76784
- const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
76785
- if (!existsSync28(path2))
77236
+ const path2 = join33(stateDir, TURN_ACTIVE_MARKER_FILE);
77237
+ if (!existsSync29(path2))
76786
77238
  return;
76787
77239
  const now = new Date;
76788
77240
  try {
@@ -76796,11 +77248,11 @@ function touchTurnActiveMarker(stateDir) {
76796
77248
  }
76797
77249
  function removeTurnActiveMarker(stateDir) {
76798
77250
  try {
76799
- unlinkSync14(join32(stateDir, TURN_ACTIVE_MARKER_FILE));
77251
+ unlinkSync14(join33(stateDir, TURN_ACTIVE_MARKER_FILE));
76800
77252
  } catch {}
76801
77253
  }
76802
77254
  function readTurnActiveMarkerAgeMs(stateDir, now) {
76803
- const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
77255
+ const path2 = join33(stateDir, TURN_ACTIVE_MARKER_FILE);
76804
77256
  try {
76805
77257
  const st = statSync11(path2);
76806
77258
  return (now ?? Date.now()) - st.mtimeMs;
@@ -77222,7 +77674,10 @@ function handleSessionEvent(deps, ev) {
77222
77674
  const turn = getCurrentTurn();
77223
77675
  if (turn == null)
77224
77676
  return;
77225
- clearAnswerReadyFlushTimeout(turn);
77677
+ if (!isEphemeralTool(ev.toolName)) {
77678
+ turn.capturedBlockMeta.fill(true);
77679
+ clearAnswerReadyFlushTimeout(turn);
77680
+ }
77226
77681
  resolvePendingNarrativeOnTool(turn, ev.toolName, ev.input);
77227
77682
  turn.toolCallCount++;
77228
77683
  touchTurnActiveMarker(STATE_DIR);
@@ -77257,7 +77712,9 @@ function handleSessionEvent(deps, ev) {
77257
77712
  const turn = getCurrentTurn();
77258
77713
  if (turn == null)
77259
77714
  return;
77260
- clearAnswerReadyFlushTimeout(turn);
77715
+ if (!isEphemeralTool(ev.toolName)) {
77716
+ clearAnswerReadyFlushTimeout(turn);
77717
+ }
77261
77718
  resetOrphanedReplyTimeout();
77262
77719
  if (isTelegramSurfaceTool(ev.toolName))
77263
77720
  return;
@@ -77635,6 +78092,11 @@ function handleSessionEvent(deps, ev) {
77635
78092
  }
77636
78093
  } catch {}
77637
78094
  }
78095
+ if (backstopAlreadyDelivered(turn.turnId, STATE_DIR)) {
78096
+ process.stderr.write(`telegram gateway: turn-flush skipped \u2014 turn ${turn.turnId} already delivered by a prior backstop (durable journal)
78097
+ `);
78098
+ return;
78099
+ }
77638
78100
  if (!backstopLatchClaimed) {
77639
78101
  process.stderr.write(`telegram gateway: turn-flush skipped \u2014 turn ${turn.turnId} already claimed the delivery latch
77640
78102
  `);
@@ -77697,6 +78159,19 @@ function handleSessionEvent(deps, ev) {
77697
78159
  chunkCount,
77698
78160
  cardMessageId: backstopCardMessageId
77699
78161
  });
78162
+ if (delivered) {
78163
+ try {
78164
+ journalExternalDelivery({
78165
+ turnNonce: turn.turnId,
78166
+ text: capturedText,
78167
+ tgMessageId: sentIds.length > 0 ? sentIds[0] : undefined,
78168
+ deliverySource: "flush"
78169
+ }, STATE_DIR);
78170
+ } catch (err) {
78171
+ process.stderr.write(`telegram gateway: turn-flush delivered but journal write failed (non-fatal): ${err.message}
78172
+ `);
78173
+ }
78174
+ }
77700
78175
  if (OBLIGATION_LEDGER_ENABLED) {
77701
78176
  if (delivered) {
77702
78177
  obligationLedger.close(turn.turnId);
@@ -77757,6 +78232,9 @@ function handleSessionEvent(deps, ev) {
77757
78232
  turnKey: tKey,
77758
78233
  turnId: turn.turnId,
77759
78234
  minChars: proseMinChars
78235
+ }, {
78236
+ isBlockShown: (nonce, text4) => isShownBlock(nonce ?? null, text4),
78237
+ backstopDeliveredNonceHit: (nonce) => backstopAlreadyDelivered(nonce ?? "", STATE_DIR)
77760
78238
  }) : { deliver: false, reason: "no-state" };
77761
78239
  if (proseDecision.deliver && proseDecision.text != null) {
77762
78240
  process.stderr.write(`telegram gateway: captured-prose delivery engaged on first silent-end chat=${chatId} turnKey=${tKey} (#3227)
@@ -77896,6 +78374,7 @@ class NarrativeFlushController {
77896
78374
  this.scheduler.disarm();
77897
78375
  if (this.pending != null) {
77898
78376
  this.effects.show(this.pending);
78377
+ this.markShown(this.pending);
77899
78378
  }
77900
78379
  this.pending = text4;
77901
78380
  this.scheduler.arm(() => this.onTimerFire(), this.flushMs);
@@ -77920,6 +78399,7 @@ class NarrativeFlushController {
77920
78399
  if (replyText != null && isDraftOfReply(pending, replyText))
77921
78400
  return;
77922
78401
  this.effects.show(pending);
78402
+ this.markShown(pending);
77923
78403
  }
77924
78404
  flushAtTurnEnd(lastReplyText) {
77925
78405
  this.scheduler.disarm();
@@ -77938,6 +78418,13 @@ class NarrativeFlushController {
77938
78418
  this.pending = null;
77939
78419
  this.timerShown = null;
77940
78420
  }
78421
+ markShown(text4) {
78422
+ if (this.effects.markDurableNarration == null)
78423
+ return;
78424
+ if (!isStructuralNarration(text4, true))
78425
+ return;
78426
+ this.effects.markDurableNarration(text4);
78427
+ }
77941
78428
  maybeRetract(replyText) {
77942
78429
  const shown = this.timerShown;
77943
78430
  if (shown == null)
@@ -78089,7 +78576,12 @@ function createNarrativeLane(deps) {
78089
78576
  let handle = null;
78090
78577
  return new NarrativeFlushController({
78091
78578
  show: (text4) => showNarrativeStep(turn, text4),
78092
- retractShown: (text4) => retractNarrativeLine(turn, text4)
78579
+ retractShown: (text4) => retractNarrativeLine(turn, text4),
78580
+ markDurableNarration: (text4) => {
78581
+ if (turn.turnId == null)
78582
+ return;
78583
+ appendShownBlock(turn.turnId, text4);
78584
+ }
78093
78585
  }, {
78094
78586
  arm: (fn, ms) => {
78095
78587
  if (handle != null)
@@ -78687,19 +79179,19 @@ function resolveAgentDirFromEnv() {
78687
79179
  }
78688
79180
 
78689
79181
  // active-reactions.ts
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";
79182
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync26, renameSync as renameSync10, existsSync as existsSync30, unlinkSync as unlinkSync15 } from "node:fs";
79183
+ import { join as join34 } from "node:path";
78692
79184
  var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
78693
79185
  function reactionsPath(agentDir) {
78694
- return join33(agentDir, ACTIVE_REACTIONS_FILENAME);
79186
+ return join34(agentDir, ACTIVE_REACTIONS_FILENAME);
78695
79187
  }
78696
79188
  function readActiveReactions(agentDir) {
78697
79189
  const p = reactionsPath(agentDir);
78698
- if (!existsSync29(p))
79190
+ if (!existsSync30(p))
78699
79191
  return [];
78700
79192
  let raw;
78701
79193
  try {
78702
- raw = readFileSync27(p, "utf-8");
79194
+ raw = readFileSync28(p, "utf-8");
78703
79195
  } catch {
78704
79196
  return [];
78705
79197
  }
@@ -78731,9 +79223,9 @@ function writeActiveReactions(agentDir, reactions) {
78731
79223
  }
78732
79224
  const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
78733
79225
  try {
78734
- writeFileSync25(tmp, JSON.stringify(reactions) + `
79226
+ writeFileSync26(tmp, JSON.stringify(reactions) + `
78735
79227
  `, "utf-8");
78736
- renameSync9(tmp, p);
79228
+ renameSync10(tmp, p);
78737
79229
  } catch {}
78738
79230
  }
78739
79231
  function addActiveReaction(agentDir, reaction) {
@@ -78755,19 +79247,19 @@ function clearActiveReactions(agentDir) {
78755
79247
  }
78756
79248
 
78757
79249
  // active-reactions.ts
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";
79250
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync27, renameSync as renameSync11, existsSync as existsSync31, unlinkSync as unlinkSync16 } from "node:fs";
79251
+ import { join as join35 } from "node:path";
78760
79252
  var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
78761
79253
  function reactionsPath2(agentDir) {
78762
- return join34(agentDir, ACTIVE_REACTIONS_FILENAME2);
79254
+ return join35(agentDir, ACTIVE_REACTIONS_FILENAME2);
78763
79255
  }
78764
79256
  function readActiveReactions2(agentDir) {
78765
79257
  const p = reactionsPath2(agentDir);
78766
- if (!existsSync30(p))
79258
+ if (!existsSync31(p))
78767
79259
  return [];
78768
79260
  let raw;
78769
79261
  try {
78770
- raw = readFileSync28(p, "utf-8");
79262
+ raw = readFileSync29(p, "utf-8");
78771
79263
  } catch {
78772
79264
  return [];
78773
79265
  }
@@ -79635,17 +80127,17 @@ async function approvalRecord(args, opts) {
79635
80127
  }
79636
80128
 
79637
80129
  // quota-check.ts
79638
- import { readFileSync as readFileSync29, existsSync as existsSync31 } from "fs";
79639
- import { join as join35 } from "path";
80130
+ import { readFileSync as readFileSync30, existsSync as existsSync32 } from "fs";
80131
+ import { join as join36 } from "path";
79640
80132
  var OAUTH_BETA2 = "oauth-2025-04-20";
79641
80133
  var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
79642
80134
  var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
79643
80135
  function readOauthToken2(claudeConfigDir) {
79644
- const tokenFile = join35(claudeConfigDir, ".oauth-token");
79645
- if (!existsSync31(tokenFile))
80136
+ const tokenFile = join36(claudeConfigDir, ".oauth-token");
80137
+ if (!existsSync32(tokenFile))
79646
80138
  return null;
79647
80139
  try {
79648
- const raw = readFileSync29(tokenFile, "utf-8").trim();
80140
+ const raw = readFileSync30(tokenFile, "utf-8").trim();
79649
80141
  return raw.length > 0 ? raw : null;
79650
80142
  } catch {
79651
80143
  return null;
@@ -80981,15 +81473,15 @@ async function menuWithBannerStatic(deps, banner) {
80981
81473
  }
80982
81474
 
80983
81475
  // gateway/session-model-file.ts
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";
81476
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync28, renameSync as renameSync12, rmSync as rmSync4 } from "node:fs";
81477
+ import { join as join37 } from "node:path";
80986
81478
  var SESSION_MODEL_FILE = ".session-model";
80987
81479
  var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
80988
81480
  var SESSION_MODEL_BOOT_ATTEMPTS_FILE = ".session-model-boot-attempts";
80989
81481
  function atomicWrite(path2, content3) {
80990
81482
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
80991
- writeFileSync27(tmp, content3, "utf8");
80992
- renameSync11(tmp, path2);
81483
+ writeFileSync28(tmp, content3, "utf8");
81484
+ renameSync12(tmp, path2);
80993
81485
  }
80994
81486
  function serializeSessionModel(rec) {
80995
81487
  return `${JSON.stringify({
@@ -81004,28 +81496,28 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
81004
81496
  throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
81005
81497
  }
81006
81498
  try {
81007
- rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
81499
+ rmSync4(join37(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
81008
81500
  } catch {}
81009
- atomicWrite(join36(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
81501
+ atomicWrite(join37(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
81010
81502
  }
81011
81503
  function readSessionModelFileRaw(agentDir) {
81012
81504
  try {
81013
- return readFileSync30(join36(agentDir, SESSION_MODEL_FILE), "utf8");
81505
+ return readFileSync31(join37(agentDir, SESSION_MODEL_FILE), "utf8");
81014
81506
  } catch {
81015
81507
  return null;
81016
81508
  }
81017
81509
  }
81018
81510
  function clearSessionModelFile(agentDir) {
81019
81511
  try {
81020
- rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
81512
+ rmSync4(join37(agentDir, SESSION_MODEL_FILE), { force: true });
81021
81513
  } catch {}
81022
81514
  }
81023
81515
  function consumeSessionModelCarrierOnHealthyBoot(agentDir) {
81024
81516
  try {
81025
- rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
81517
+ rmSync4(join37(agentDir, SESSION_MODEL_FILE), { force: true });
81026
81518
  } catch {}
81027
81519
  try {
81028
- rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
81520
+ rmSync4(join37(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
81029
81521
  } catch {}
81030
81522
  }
81031
81523
  function restoreSessionModelFileRaw(agentDir, raw) {
@@ -81034,12 +81526,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
81034
81526
  return;
81035
81527
  }
81036
81528
  try {
81037
- atomicWrite(join36(agentDir, SESSION_MODEL_FILE), raw);
81529
+ atomicWrite(join37(agentDir, SESSION_MODEL_FILE), raw);
81038
81530
  } catch {}
81039
81531
  }
81040
81532
  function readConfiguredDefaultModel(agentDir) {
81041
81533
  try {
81042
- const v = readFileSync30(join36(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
81534
+ const v = readFileSync31(join37(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
81043
81535
  return v.length > 0 ? v : null;
81044
81536
  } catch {
81045
81537
  return null;
@@ -81051,12 +81543,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
81051
81543
  if (!EFFORT_LEVEL_RE.test(level)) {
81052
81544
  throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
81053
81545
  }
81054
- atomicWrite(join36(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
81546
+ atomicWrite(join37(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
81055
81547
  `);
81056
81548
  }
81057
81549
  function clearSessionEffortFile(agentDir) {
81058
81550
  try {
81059
- rmSync4(join36(agentDir, SESSION_EFFORT_FILE), { force: true });
81551
+ rmSync4(join37(agentDir, SESSION_EFFORT_FILE), { force: true });
81060
81552
  } catch {}
81061
81553
  }
81062
81554
  var PREMIUM_RECOVERY_FILE = ".premium-recovery";
@@ -81079,13 +81571,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
81079
81571
  if (clean.length === 0) {
81080
81572
  throw new Error("refusing to persist premium-recovery marker with no chats to notify");
81081
81573
  }
81082
- atomicWrite(join36(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
81574
+ atomicWrite(join37(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
81083
81575
  `);
81084
81576
  }
81085
81577
  function readPremiumRecoveryFile(agentDir) {
81086
81578
  let raw;
81087
81579
  try {
81088
- raw = readFileSync30(join36(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
81580
+ raw = readFileSync31(join37(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
81089
81581
  } catch {
81090
81582
  return null;
81091
81583
  }
@@ -81098,7 +81590,7 @@ function readPremiumRecoveryFile(agentDir) {
81098
81590
  }
81099
81591
  function clearPremiumRecoveryFile(agentDir) {
81100
81592
  try {
81101
- rmSync4(join36(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
81593
+ rmSync4(join37(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
81102
81594
  } catch {}
81103
81595
  }
81104
81596
 
@@ -81387,7 +81879,7 @@ async function discoverModels(agentName3, opts = {}) {
81387
81879
  }
81388
81880
 
81389
81881
  // ../src/agents/scaffold.ts
81390
- import { dirname as dirname14, isAbsolute, join as join39, relative, resolve as resolve8 } from "node:path";
81882
+ import { dirname as dirname14, isAbsolute, join as join40, relative, resolve as resolve8 } from "node:path";
81391
81883
  init_atomic();
81392
81884
 
81393
81885
  // ../src/agents/agent-uid.ts
@@ -81404,7 +81896,7 @@ init_merge();
81404
81896
  init_timezone();
81405
81897
 
81406
81898
  // ../src/cli/agent-config.ts
81407
- import { join as join37 } from "node:path";
81899
+ import { join as join38 } from "node:path";
81408
81900
  import { homedir as homedir12 } from "node:os";
81409
81901
 
81410
81902
  // ../src/cli/helpers.ts
@@ -81425,12 +81917,12 @@ var WEBKITE_VAULT_KEYS = new Set([
81425
81917
  init_overlay_loader();
81426
81918
 
81427
81919
  // ../src/cli/agent-config.ts
81428
- var AUDIT_ROOT = join37(homedir12(), ".switchroom", "audit");
81920
+ var AUDIT_ROOT = join38(homedir12(), ".switchroom", "audit");
81429
81921
 
81430
81922
  // ../src/agents/profiles.ts
81431
81923
  var import_handlebars = __toESM(require_lib(), 1);
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";
81924
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync29, existsSync as existsSync33, readdirSync as readdirSync7, statSync as statSync12, copyFileSync, mkdirSync as mkdirSync31, realpathSync as realpathSync2 } from "node:fs";
81925
+ import { resolve as resolve7, join as join39, sep as pathSep } from "node:path";
81434
81926
  function resolveProfilesRoot() {
81435
81927
  const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
81436
81928
  if (envOverride) {
@@ -81441,7 +81933,7 @@ function resolveProfilesRoot() {
81441
81933
  resolve7(import.meta.dirname, "profiles")
81442
81934
  ];
81443
81935
  for (const candidate of candidates) {
81444
- if (existsSync32(candidate)) {
81936
+ if (existsSync33(candidate)) {
81445
81937
  return candidate;
81446
81938
  }
81447
81939
  }
@@ -81457,9 +81949,9 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
81457
81949
  var SHARED_FRAGMENTS_DIR = resolve7(PROFILES_ROOT, "_shared");
81458
81950
  var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
81459
81951
  for (const name of SHARED_FRAGMENTS) {
81460
- const fragPath = join38(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
81461
- if (existsSync32(fragPath)) {
81462
- import_handlebars.default.registerPartial(name, readFileSync31(fragPath, "utf-8"));
81952
+ const fragPath = join39(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
81953
+ if (existsSync33(fragPath)) {
81954
+ import_handlebars.default.registerPartial(name, readFileSync32(fragPath, "utf-8"));
81463
81955
  }
81464
81956
  }
81465
81957
 
@@ -82066,7 +82558,7 @@ init_overlay_loader();
82066
82558
  init_merge();
82067
82559
  init_timezone();
82068
82560
  var import_yaml4 = __toESM(require_dist(), 1);
82069
- import { readFileSync as readFileSync32, existsSync as existsSync33 } from "node:fs";
82561
+ import { readFileSync as readFileSync33, existsSync as existsSync34 } from "node:fs";
82070
82562
  import { homedir as homedir13 } from "node:os";
82071
82563
  import { resolve as resolve9 } from "node:path";
82072
82564
 
@@ -82142,7 +82634,7 @@ function findConfigFile2(startDir) {
82142
82634
  resolve9(userDir, "clerk.yml")
82143
82635
  ].filter(Boolean);
82144
82636
  for (const path2 of searchPaths) {
82145
- if (existsSync33(path2)) {
82637
+ if (existsSync34(path2)) {
82146
82638
  return path2;
82147
82639
  }
82148
82640
  }
@@ -82150,12 +82642,12 @@ function findConfigFile2(startDir) {
82150
82642
  }
82151
82643
  function loadConfig2(configPath) {
82152
82644
  const filePath = configPath ?? findConfigFile2();
82153
- if (!existsSync33(filePath)) {
82645
+ if (!existsSync34(filePath)) {
82154
82646
  throw new ConfigError2(`Config file not found: ${filePath}`);
82155
82647
  }
82156
82648
  let raw;
82157
82649
  try {
82158
- raw = readFileSync32(filePath, "utf-8");
82650
+ raw = readFileSync33(filePath, "utf-8");
82159
82651
  } catch (err) {
82160
82652
  throw new ConfigError2(`Failed to read config file: ${filePath}`, [
82161
82653
  ` ${err.message}`
@@ -82685,15 +83177,15 @@ function topicForRecipient(args) {
82685
83177
  }
82686
83178
 
82687
83179
  // ../src/agents/perf.ts
82688
- import { existsSync as existsSync34, readFileSync as readFileSync33 } from "node:fs";
83180
+ import { existsSync as existsSync35, readFileSync as readFileSync34 } from "node:fs";
82689
83181
  function readTurnUsages(jsonlPath, lastN) {
82690
- if (!existsSync34(jsonlPath))
83182
+ if (!existsSync35(jsonlPath))
82691
83183
  return [];
82692
83184
  if (lastN <= 0)
82693
83185
  return [];
82694
83186
  let raw;
82695
83187
  try {
82696
- raw = readFileSync33(jsonlPath, "utf-8");
83188
+ raw = readFileSync34(jsonlPath, "utf-8");
82697
83189
  } catch {
82698
83190
  return [];
82699
83191
  }
@@ -82784,8 +83276,8 @@ function numField(obj, key) {
82784
83276
  }
82785
83277
 
82786
83278
  // gateway/context-occupancy.ts
82787
- import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync29 } from "node:fs";
82788
- import { join as join40 } from "node:path";
83279
+ import { mkdirSync as mkdirSync32, writeFileSync as writeFileSync30 } from "node:fs";
83280
+ import { join as join41 } from "node:path";
82789
83281
  var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
82790
83282
  var TIGHT_FRACTION = 0.8;
82791
83283
  function buildContextOccupancy(occupancy, cap, now) {
@@ -82808,9 +83300,9 @@ function buildContextOccupancy(occupancy, cap, now) {
82808
83300
  }
82809
83301
  function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
82810
83302
  try {
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) + `
83303
+ const path2 = join41(stateDir, CONTEXT_OCCUPANCY_FILENAME);
83304
+ (deps?.mkdir ?? ((p, o) => mkdirSync32(p, o)))(stateDir, { recursive: true });
83305
+ (deps?.writeFile ?? ((p, d) => writeFileSync30(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
82814
83306
  `);
82815
83307
  } catch {}
82816
83308
  }
@@ -82964,7 +83456,7 @@ function nextCompactNotify(state6, ev) {
82964
83456
  }
82965
83457
 
82966
83458
  // gateway/hostd-dispatch.ts
82967
- import { existsSync as existsSync35 } from "node:fs";
83459
+ import { existsSync as existsSync36 } from "node:fs";
82968
83460
  import { randomBytes as randomBytes7 } from "node:crypto";
82969
83461
  init_loader();
82970
83462
  var _hostdEnabled2;
@@ -82988,13 +83480,13 @@ function hostdSocketPath2(agentName3) {
82988
83480
  function hostdWillBeUsed2(agentName3) {
82989
83481
  if (!isHostdEnabled2())
82990
83482
  return false;
82991
- return existsSync35(hostdSocketPath2(agentName3));
83483
+ return existsSync36(hostdSocketPath2(agentName3));
82992
83484
  }
82993
83485
  async function tryHostdDispatch2(agentName3, req, timeoutMs = 5000) {
82994
83486
  if (!isHostdEnabled2())
82995
83487
  return "not-configured";
82996
83488
  const sockPath = hostdSocketPath2(agentName3);
82997
- if (!existsSync35(sockPath))
83489
+ if (!existsSync36(sockPath))
82998
83490
  return "not-configured";
82999
83491
  try {
83000
83492
  return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
@@ -83023,7 +83515,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
83023
83515
  if (!isHostdEnabled2())
83024
83516
  return "not-configured";
83025
83517
  const sockPath = hostdSocketPath2(agentName3);
83026
- if (!existsSync35(sockPath))
83518
+ if (!existsSync36(sockPath))
83027
83519
  return "not-configured";
83028
83520
  const now = opts.now ?? Date.now;
83029
83521
  const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
@@ -83142,7 +83634,7 @@ function createDmPinSweeper(deps) {
83142
83634
 
83143
83635
  // gateway/webhook-ingest-server.ts
83144
83636
  import net4 from "node:net";
83145
- import { chmodSync as chmodSync10, existsSync as existsSync36, unlinkSync as unlinkSync17 } from "node:fs";
83637
+ import { chmodSync as chmodSync10, existsSync as existsSync37, unlinkSync as unlinkSync17 } from "node:fs";
83146
83638
  var MAX_REQUEST_BYTES = 1024 * 1024;
83147
83639
  function fdOf(conn) {
83148
83640
  const handle = conn._handle;
@@ -83154,7 +83646,7 @@ function startWebhookIngestServer(opts) {
83154
83646
  const log = opts.log ?? ((s) => process.stderr.write(s));
83155
83647
  const allowed = new Set(opts.allowedUids);
83156
83648
  try {
83157
- if (existsSync36(opts.socketPath))
83649
+ if (existsSync37(opts.socketPath))
83158
83650
  unlinkSync17(opts.socketPath);
83159
83651
  } catch (err) {
83160
83652
  log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
@@ -83247,7 +83739,7 @@ function startWebhookIngestServer(opts) {
83247
83739
  server.close();
83248
83740
  } catch {}
83249
83741
  try {
83250
- if (existsSync36(opts.socketPath))
83742
+ if (existsSync37(opts.socketPath))
83251
83743
  unlinkSync17(opts.socketPath);
83252
83744
  } catch {}
83253
83745
  }
@@ -83255,20 +83747,20 @@ function startWebhookIngestServer(opts) {
83255
83747
  }
83256
83748
 
83257
83749
  // ../src/web/webhook-gateway-record.ts
83258
- import { appendFileSync as appendFileSync7, mkdirSync as mkdirSync34 } from "fs";
83259
- import { join as join43 } from "path";
83750
+ import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync35 } from "fs";
83751
+ import { join as join44 } from "path";
83260
83752
  import { homedir as homedir15 } from "os";
83261
83753
 
83262
83754
  // ../src/web/webhook-handler.ts
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";
83755
+ import { appendFileSync as appendFileSync7, existsSync as existsSync38, mkdirSync as mkdirSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
83756
+ import { join as join42 } from "path";
83265
83757
  var DEDUP_MAX = 1000;
83266
83758
  var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
83267
83759
  function loadDedupFile(path2) {
83268
83760
  try {
83269
- if (!existsSync37(path2))
83761
+ if (!existsSync38(path2))
83270
83762
  return {};
83271
- const raw = JSON.parse(readFileSync34(path2, "utf-8"));
83763
+ const raw = JSON.parse(readFileSync35(path2, "utf-8"));
83272
83764
  return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
83273
83765
  } catch {
83274
83766
  return {};
@@ -83282,7 +83774,7 @@ function saveDedupFile(path2, deliveries, now) {
83282
83774
  }
83283
83775
  const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
83284
83776
  const final = Object.fromEntries(sorted);
83285
- writeFileSync30(path2, JSON.stringify({ deliveries: final }), {
83777
+ writeFileSync31(path2, JSON.stringify({ deliveries: final }), {
83286
83778
  mode: 384
83287
83779
  });
83288
83780
  }
@@ -83290,8 +83782,8 @@ var agentDedupCache = new Map;
83290
83782
  function createFileDedupStore(resolveAgentDir) {
83291
83783
  return {
83292
83784
  check(agent, deliveryId, now) {
83293
- const telegramDir = join41(resolveAgentDir(agent), "telegram");
83294
- const filePath = join41(telegramDir, "webhook-dedup.json");
83785
+ const telegramDir = join42(resolveAgentDir(agent), "telegram");
83786
+ const filePath = join42(telegramDir, "webhook-dedup.json");
83295
83787
  if (!agentDedupCache.has(agent)) {
83296
83788
  agentDedupCache.set(agent, loadDedupFile(filePath));
83297
83789
  }
@@ -83301,7 +83793,7 @@ function createFileDedupStore(resolveAgentDir) {
83301
83793
  }
83302
83794
  deliveries[deliveryId] = now;
83303
83795
  try {
83304
- mkdirSync32(telegramDir, { recursive: true });
83796
+ mkdirSync33(telegramDir, { recursive: true });
83305
83797
  saveDedupFile(filePath, deliveries, now);
83306
83798
  } catch {}
83307
83799
  return;
@@ -83312,8 +83804,8 @@ var tokenBuckets = new Map;
83312
83804
  var throttleIssueWindow = new Map;
83313
83805
 
83314
83806
  // ../src/web/webhook-dispatch.ts
83315
- import { existsSync as existsSync38, mkdirSync as mkdirSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
83316
- import { join as join42 } from "path";
83807
+ import { existsSync as existsSync39, mkdirSync as mkdirSync34, readFileSync as readFileSync36, writeFileSync as writeFileSync32 } from "fs";
83808
+ import { join as join43 } from "path";
83317
83809
  import { homedir as homedir14 } from "os";
83318
83810
 
83319
83811
  // ../src/agent-scheduler/ipc-client.ts
@@ -83612,9 +84104,9 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
83612
84104
  }
83613
84105
  function loadCooldownFile(path2) {
83614
84106
  try {
83615
- if (!existsSync38(path2))
84107
+ if (!existsSync39(path2))
83616
84108
  return {};
83617
- const raw = JSON.parse(readFileSync35(path2, "utf-8"));
84109
+ const raw = JSON.parse(readFileSync36(path2, "utf-8"));
83618
84110
  return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
83619
84111
  } catch {
83620
84112
  return {};
@@ -83622,7 +84114,7 @@ function loadCooldownFile(path2) {
83622
84114
  }
83623
84115
  function saveCooldownFile(path2, dispatches) {
83624
84116
  try {
83625
- writeFileSync31(path2, JSON.stringify({ dispatches }), {
84117
+ writeFileSync32(path2, JSON.stringify({ dispatches }), {
83626
84118
  mode: 384
83627
84119
  });
83628
84120
  } catch {}
@@ -83633,8 +84125,8 @@ function createFileCooldownStore(resolveAgentDir) {
83633
84125
  isCoolingDown(agent, key, cooldownMs, now) {
83634
84126
  if (cooldownMs <= 0)
83635
84127
  return false;
83636
- const telegramDir = join42(resolveAgentDir(agent), "telegram");
83637
- const filePath = join42(telegramDir, "webhook-cooldown.json");
84128
+ const telegramDir = join43(resolveAgentDir(agent), "telegram");
84129
+ const filePath = join43(telegramDir, "webhook-cooldown.json");
83638
84130
  if (!cache.has(agent)) {
83639
84131
  cache.set(agent, loadCooldownFile(filePath));
83640
84132
  }
@@ -83645,7 +84137,7 @@ function createFileCooldownStore(resolveAgentDir) {
83645
84137
  }
83646
84138
  dispatches[key] = now;
83647
84139
  try {
83648
- mkdirSync33(telegramDir, { recursive: true });
84140
+ mkdirSync34(telegramDir, { recursive: true });
83649
84141
  saveCooldownFile(filePath, dispatches);
83650
84142
  } catch {}
83651
84143
  return false;
@@ -83691,9 +84183,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
83691
84183
  }
83692
84184
  function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
83693
84185
  const log = deps.log ?? ((s) => process.stderr.write(s));
83694
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
84186
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join43(homedir14(), ".switchroom", "agents", a));
83695
84187
  const now = (deps.now ?? Date.now)();
83696
- const socketPath = join42(resolveAgentDir(agent), "telegram", "gateway.sock");
84188
+ const socketPath = join43(resolveAgentDir(agent), "telegram", "gateway.sock");
83697
84189
  const inbound = {
83698
84190
  type: "inbound",
83699
84191
  chatId: ctx.chatId,
@@ -83765,7 +84257,7 @@ function evaluateDispatch(args, deps = {}) {
83765
84257
  const log = deps.log ?? ((s) => process.stderr.write(s));
83766
84258
  const now = (deps.now ?? Date.now)();
83767
84259
  const nowDate = deps.nowDate ?? (() => new Date(now));
83768
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
84260
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join43(homedir14(), ".switchroom", "agents", a));
83769
84261
  const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
83770
84262
  if (!DISPATCH_SOURCES.includes(args.source))
83771
84263
  return 0;
@@ -83843,10 +84335,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
83843
84335
  function recordWebhookEvent(rec, deps = {}) {
83844
84336
  const log = deps.log ?? ((s) => process.stderr.write(s));
83845
84337
  const now = rec.ts || (deps.now ?? Date.now)();
83846
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join43(homedir15(), ".switchroom", "agents", a));
84338
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join44(homedir15(), ".switchroom", "agents", a));
83847
84339
  const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
83848
84340
  const agent = rec.agent;
83849
- const telegramDir = join43(resolveAgentDir(agent), "telegram");
84341
+ const telegramDir = join44(resolveAgentDir(agent), "telegram");
83850
84342
  if (rec.source === "github" && rec.delivery_id) {
83851
84343
  const originalTs = dedupStore.check(agent, rec.delivery_id, now);
83852
84344
  if (originalTs !== undefined) {
@@ -83855,9 +84347,9 @@ function recordWebhookEvent(rec, deps = {}) {
83855
84347
  return { status: "deduped", ts: originalTs };
83856
84348
  }
83857
84349
  }
83858
- const logPath = join43(telegramDir, "webhook-events.jsonl");
84350
+ const logPath = join44(telegramDir, "webhook-events.jsonl");
83859
84351
  try {
83860
- mkdirSync34(telegramDir, { recursive: true });
84352
+ mkdirSync35(telegramDir, { recursive: true });
83861
84353
  const record = {
83862
84354
  ts: now,
83863
84355
  source: rec.source,
@@ -83865,7 +84357,7 @@ function recordWebhookEvent(rec, deps = {}) {
83865
84357
  rendered_text: rec.rendered_text,
83866
84358
  payload: rec.payload
83867
84359
  };
83868
- appendFileSync7(logPath, JSON.stringify(record) + `
84360
+ appendFileSync8(logPath, JSON.stringify(record) + `
83869
84361
  `, { mode: 384 });
83870
84362
  } catch (err) {
83871
84363
  log(`webhook-gateway: agent='${agent}' source='${rec.source}' write failed: ${err.message}
@@ -83967,7 +84459,7 @@ function recordWebhookEvent(rec, deps = {}) {
83967
84459
 
83968
84460
  // gateway/ipc-server.ts
83969
84461
  init_format();
83970
- import { renameSync as renameSync13, unlinkSync as unlinkSync18, chmodSync as chmodSync11 } from "fs";
84462
+ import { renameSync as renameSync14, unlinkSync as unlinkSync18, chmodSync as chmodSync11 } from "fs";
83971
84463
  var MAX_BUFFER_SIZE = 1024 * 1024;
83972
84464
  var VALID_OPERATOR_KINDS = new Set([
83973
84465
  "credentials-expired",
@@ -84176,7 +84668,7 @@ function createIpcServer(options) {
84176
84668
  heartbeatTimeoutMs = 30000
84177
84669
  } = options;
84178
84670
  try {
84179
- renameSync13(socketPath, socketPath + ".bak");
84671
+ renameSync14(socketPath, socketPath + ".bak");
84180
84672
  } catch {}
84181
84673
  try {
84182
84674
  unlinkSync18(socketPath + ".bak");
@@ -84566,7 +85058,7 @@ function createIpcServer(options) {
84566
85058
  clientBySocketId.clear();
84567
85059
  server.stop(true);
84568
85060
  try {
84569
- renameSync13(socketPath, socketPath + ".bak");
85061
+ renameSync14(socketPath, socketPath + ".bak");
84570
85062
  } catch {}
84571
85063
  }
84572
85064
  };
@@ -88501,27 +88993,27 @@ function skillProposalKeyboard(id) {
88501
88993
  // ../src/self-improve/skill-proposals.ts
88502
88994
  import {
88503
88995
  closeSync as closeSync7,
88504
- existsSync as existsSync39,
88505
- mkdirSync as mkdirSync35,
88996
+ existsSync as existsSync40,
88997
+ mkdirSync as mkdirSync36,
88506
88998
  openSync as openSync7,
88507
- readFileSync as readFileSync36,
88999
+ readFileSync as readFileSync37,
88508
89000
  writeSync as writeSync5
88509
89001
  } from "node:fs";
88510
- import { join as join44 } from "node:path";
89002
+ import { join as join45 } from "node:path";
88511
89003
  import { randomUUID as randomUUID5 } from "node:crypto";
88512
89004
  var PROPOSALS_FILE2 = "skill-proposals.jsonl";
88513
89005
  var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
88514
89006
  var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
88515
89007
  var PROPOSAL_SIM_THRESHOLD = 0.5;
88516
89008
  function proposalsPath2(stateDir) {
88517
- return join44(stateDir, PROPOSALS_FILE2);
89009
+ return join45(stateDir, PROPOSALS_FILE2);
88518
89010
  }
88519
89011
  function rejectedPath2(stateDir) {
88520
- return join44(stateDir, REJECTED_FILE2);
89012
+ return join45(stateDir, REJECTED_FILE2);
88521
89013
  }
88522
89014
  function ensureDir3(stateDir) {
88523
- if (!existsSync39(stateDir)) {
88524
- mkdirSync35(stateDir, { recursive: true, mode: 493 });
89015
+ if (!existsSync40(stateDir)) {
89016
+ mkdirSync36(stateDir, { recursive: true, mode: 493 });
88525
89017
  }
88526
89018
  }
88527
89019
  function appendLine2(path2, obj) {
@@ -88534,11 +89026,11 @@ function appendLine2(path2, obj) {
88534
89026
  }
88535
89027
  }
88536
89028
  function readLines2(path2, isValid2) {
88537
- if (!existsSync39(path2))
89029
+ if (!existsSync40(path2))
88538
89030
  return [];
88539
89031
  let raw;
88540
89032
  try {
88541
- raw = readFileSync36(path2, "utf-8");
89033
+ raw = readFileSync37(path2, "utf-8");
88542
89034
  } catch {
88543
89035
  return [];
88544
89036
  }
@@ -89135,11 +89627,11 @@ function escapeBody2(s) {
89135
89627
  }
89136
89628
 
89137
89629
  // gateway/pid-file.ts
89138
- import { writeFileSync as writeFileSync32, readFileSync as readFileSync37, unlinkSync as unlinkSync19, renameSync as renameSync14 } from "node:fs";
89630
+ import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as unlinkSync19, renameSync as renameSync15 } from "node:fs";
89139
89631
  function writePidFile(path2, record) {
89140
89632
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
89141
- writeFileSync32(tmp, JSON.stringify(record), "utf-8");
89142
- renameSync14(tmp, path2);
89633
+ writeFileSync33(tmp, JSON.stringify(record), "utf-8");
89634
+ renameSync15(tmp, path2);
89143
89635
  }
89144
89636
  function clearPidFile(path2) {
89145
89637
  try {
@@ -89154,10 +89646,10 @@ import {
89154
89646
  writeFile as writeFileAsync,
89155
89647
  readFile as readFileAsync
89156
89648
  } from "node:fs/promises";
89157
- import { readFileSync as readFileSync38 } from "node:fs";
89649
+ import { readFileSync as readFileSync39 } from "node:fs";
89158
89650
  function readCurrentBootId() {
89159
89651
  try {
89160
- const stat = readFileSync38("/proc/1/stat", "utf-8");
89652
+ const stat = readFileSync39("/proc/1/stat", "utf-8");
89161
89653
  const lastParen = stat.lastIndexOf(")");
89162
89654
  if (lastParen < 0)
89163
89655
  return null;
@@ -89360,15 +89852,15 @@ function safeCount(fn) {
89360
89852
  }
89361
89853
 
89362
89854
  // gateway/session-marker.ts
89363
- import { writeFileSync as writeFileSync33, readFileSync as readFileSync39, renameSync as renameSync15, unlinkSync as unlinkSync20 } from "node:fs";
89855
+ import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync16, unlinkSync as unlinkSync20 } from "node:fs";
89364
89856
  function writeSessionMarker(path2, marker) {
89365
89857
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
89366
- writeFileSync33(tmp, JSON.stringify(marker), "utf-8");
89367
- renameSync15(tmp, path2);
89858
+ writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
89859
+ renameSync16(tmp, path2);
89368
89860
  }
89369
89861
  function readSessionMarker(path2) {
89370
89862
  try {
89371
- const raw = readFileSync39(path2, "utf-8");
89863
+ const raw = readFileSync40(path2, "utf-8");
89372
89864
  const parsed = JSON.parse(raw);
89373
89865
  if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
89374
89866
  return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
@@ -89390,16 +89882,16 @@ function shouldFireRestartBanner(input) {
89390
89882
  }
89391
89883
 
89392
89884
  // gateway/clean-shutdown-marker.ts
89393
- import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync16, unlinkSync as unlinkSync21 } from "node:fs";
89885
+ import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync17, unlinkSync as unlinkSync21 } from "node:fs";
89394
89886
  var DEFAULT_MAX_AGE_MS = 60000;
89395
89887
  function writeCleanShutdownMarker(path2, marker) {
89396
89888
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
89397
- writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
89398
- renameSync16(tmp, path2);
89889
+ writeFileSync35(tmp, JSON.stringify(marker), "utf-8");
89890
+ renameSync17(tmp, path2);
89399
89891
  }
89400
89892
  function readCleanShutdownMarker(path2) {
89401
89893
  try {
89402
- const raw = readFileSync40(path2, "utf-8");
89894
+ const raw = readFileSync41(path2, "utf-8");
89403
89895
  const parsed = JSON.parse(raw);
89404
89896
  if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
89405
89897
  const out = { ts: parsed.ts, signal: parsed.signal };
@@ -89771,16 +90263,16 @@ function classifyAdminGate(text4, myAgentName) {
89771
90263
 
89772
90264
  // subagent-watcher.ts
89773
90265
  import {
89774
- existsSync as existsSync40,
90266
+ existsSync as existsSync41,
89775
90267
  openSync as openSync8,
89776
90268
  readSync as readSync2,
89777
90269
  statSync as statSync13,
89778
90270
  closeSync as closeSync8,
89779
90271
  watch,
89780
90272
  readdirSync as readdirSync8,
89781
- readFileSync as readFileSync41
90273
+ readFileSync as readFileSync42
89782
90274
  } from "fs";
89783
- import { join as join45 } from "path";
90275
+ import { join as join46 } from "path";
89784
90276
 
89785
90277
  // session-tail.ts
89786
90278
  function sanitizeCwdToProjectName2(cwd) {
@@ -90130,7 +90622,7 @@ function backfillJsonlAgentId(db3, jsonlPath, agentId, log) {
90130
90622
  const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
90131
90623
  let meta;
90132
90624
  try {
90133
- const raw = readFileSync41(metaPath, "utf8");
90625
+ const raw = readFileSync42(metaPath, "utf8");
90134
90626
  meta = JSON.parse(raw);
90135
90627
  } catch {
90136
90628
  log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
@@ -90585,7 +91077,7 @@ function startSubagentWatcher(config) {
90585
91077
  clearTimeout(ref.ref);
90586
91078
  });
90587
91079
  const fs2 = config.fs ?? {
90588
- existsSync: existsSync40,
91080
+ existsSync: existsSync41,
90589
91081
  readdirSync: readdirSync8,
90590
91082
  statSync: statSync13,
90591
91083
  openSync: openSync8,
@@ -91103,8 +91595,8 @@ function startSubagentWatcher(config) {
91103
91595
  if (stopped)
91104
91596
  return;
91105
91597
  pruneVanishedDirWatchers();
91106
- const claudeHome = join45(agentDir, ".claude");
91107
- const projectsRoot = join45(claudeHome, "projects");
91598
+ const claudeHome = join46(agentDir, ".claude");
91599
+ const projectsRoot = join46(claudeHome, "projects");
91108
91600
  if (!fs2.existsSync(projectsRoot))
91109
91601
  return;
91110
91602
  let projectDirs;
@@ -91138,7 +91630,7 @@ function startSubagentWatcher(config) {
91138
91630
  continue;
91139
91631
  }
91140
91632
  warnedForeignSlugs.delete(pDir);
91141
- const projectPath = join45(projectsRoot, pDir);
91633
+ const projectPath = join46(projectsRoot, pDir);
91142
91634
  let sessionDirs;
91143
91635
  try {
91144
91636
  sessionDirs = fs2.readdirSync(projectPath);
@@ -91148,7 +91640,7 @@ function startSubagentWatcher(config) {
91148
91640
  for (const sDir of sessionDirs) {
91149
91641
  if (sDir.endsWith(".jsonl"))
91150
91642
  continue;
91151
- const subagentsPath = join45(projectPath, sDir, "subagents");
91643
+ const subagentsPath = join46(projectPath, sDir, "subagents");
91152
91644
  if (!fs2.existsSync(subagentsPath))
91153
91645
  continue;
91154
91646
  const watchAndScan = (dirPath) => {
@@ -91157,7 +91649,7 @@ function startSubagentWatcher(config) {
91157
91649
  const w = fs2.watch(dirPath, (_event, filename) => {
91158
91650
  if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
91159
91651
  return;
91160
- const filePath = join45(dirPath, filename.toString());
91652
+ const filePath = join46(dirPath, filename.toString());
91161
91653
  if (!knownFiles.has(filePath)) {
91162
91654
  scanSubagentsDir(dirPath);
91163
91655
  }
@@ -91171,7 +91663,7 @@ function startSubagentWatcher(config) {
91171
91663
  scanSubagentsDir(dirPath);
91172
91664
  };
91173
91665
  watchAndScan(subagentsPath);
91174
- const workflowsPath = join45(subagentsPath, "workflows");
91666
+ const workflowsPath = join46(subagentsPath, "workflows");
91175
91667
  if (fs2.existsSync(workflowsPath)) {
91176
91668
  let wfDirs;
91177
91669
  try {
@@ -91181,7 +91673,7 @@ function startSubagentWatcher(config) {
91181
91673
  }
91182
91674
  for (const wfDir of wfDirs) {
91183
91675
  try {
91184
- const wfPath = join45(workflowsPath, wfDir);
91676
+ const wfPath = join46(workflowsPath, wfDir);
91185
91677
  if (!fs2.statSync(wfPath).isDirectory())
91186
91678
  continue;
91187
91679
  watchAndScan(wfPath);
@@ -91201,7 +91693,7 @@ function startSubagentWatcher(config) {
91201
91693
  for (const e of entries) {
91202
91694
  if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
91203
91695
  continue;
91204
- const filePath = join45(subagentsPath, e);
91696
+ const filePath = join46(subagentsPath, e);
91205
91697
  if (knownFiles.has(filePath))
91206
91698
  continue;
91207
91699
  const agentId = e.slice("agent-".length, -".jsonl".length);
@@ -91329,37 +91821,37 @@ function startSubagentWatcher(config) {
91329
91821
 
91330
91822
  // ../src/worktree/registry.ts
91331
91823
  import {
91332
- mkdirSync as mkdirSync36,
91333
- writeFileSync as writeFileSync35,
91334
- readFileSync as readFileSync42,
91824
+ mkdirSync as mkdirSync37,
91825
+ writeFileSync as writeFileSync36,
91826
+ readFileSync as readFileSync43,
91335
91827
  readdirSync as readdirSync9,
91336
91828
  unlinkSync as unlinkSync22,
91337
- existsSync as existsSync41,
91338
- renameSync as renameSync17
91829
+ existsSync as existsSync42,
91830
+ renameSync as renameSync18
91339
91831
  } from "node:fs";
91340
- import { join as join46, resolve as resolve10 } from "node:path";
91832
+ import { join as join47, resolve as resolve10 } from "node:path";
91341
91833
  import { homedir as homedir16 } from "node:os";
91342
91834
  function registryDir() {
91343
- return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join46(homedir16(), ".switchroom", "worktrees"));
91835
+ return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join47(homedir16(), ".switchroom", "worktrees"));
91344
91836
  }
91345
91837
  function recordPath(id) {
91346
- return join46(registryDir(), `${id}.json`);
91838
+ return join47(registryDir(), `${id}.json`);
91347
91839
  }
91348
91840
  function ensureDir4() {
91349
- mkdirSync36(registryDir(), { recursive: true });
91841
+ mkdirSync37(registryDir(), { recursive: true });
91350
91842
  }
91351
91843
  function writeRecord(record) {
91352
91844
  ensureDir4();
91353
91845
  const target = recordPath(record.id);
91354
91846
  const tmp = `${target}.tmp${process.pid}`;
91355
- writeFileSync35(tmp, JSON.stringify(record, null, 2) + `
91847
+ writeFileSync36(tmp, JSON.stringify(record, null, 2) + `
91356
91848
  `, { mode: 384 });
91357
- renameSync17(tmp, target);
91849
+ renameSync18(tmp, target);
91358
91850
  }
91359
91851
  function readRecord(id) {
91360
91852
  const path2 = recordPath(id);
91361
91853
  try {
91362
- const raw = readFileSync42(path2, "utf8");
91854
+ const raw = readFileSync43(path2, "utf8");
91363
91855
  return JSON.parse(raw);
91364
91856
  } catch {
91365
91857
  return null;
@@ -91389,7 +91881,7 @@ function touchHeartbeat(id, onAfterRead) {
91389
91881
  writeRecord({ ...rec, heartbeatAt: new Date().toISOString() });
91390
91882
  }
91391
91883
  function recordExists(id) {
91392
- return existsSync41(recordPath(id));
91884
+ return existsSync42(recordPath(id));
91393
91885
  }
91394
91886
 
91395
91887
  // worktree-watch-cwds.ts
@@ -91521,15 +92013,15 @@ function determineRestartReason(opts) {
91521
92013
  init_boot_card();
91522
92014
 
91523
92015
  // gateway/update-announce.ts
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";
92016
+ import { existsSync as existsSync47, mkdirSync as mkdirSync41, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync50 } from "node:fs";
92017
+ import { join as join52 } from "node:path";
91526
92018
  import { homedir as homedir18 } from "node:os";
91527
92019
 
91528
92020
  // ../src/host-control/audit-reader.ts
91529
92021
  import { homedir as homedir17 } from "node:os";
91530
- import { join as join50 } from "node:path";
92022
+ import { join as join51 } from "node:path";
91531
92023
  function defaultAuditLogPath(home2 = homedir17()) {
91532
- return join50(home2, ".switchroom", "host-control-audit.log");
92024
+ return join51(home2, ".switchroom", "host-control-audit.log");
91533
92025
  }
91534
92026
  function parseAuditLine(line) {
91535
92027
  const trimmed = line.trim();
@@ -91652,8 +92144,8 @@ function readAndFilter(raw, filters, limit) {
91652
92144
  var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
91653
92145
  function readLastTerminalUpdateAudit(opts = {}) {
91654
92146
  const path2 = opts.auditLogPath ?? defaultAuditLogPath();
91655
- const exists = opts.exists ?? existsSync46;
91656
- const readFile = opts.readFile ?? ((p) => readFileSync49(p, "utf-8"));
92147
+ const exists = opts.exists ?? existsSync47;
92148
+ const readFile = opts.readFile ?? ((p) => readFileSync50(p, "utf-8"));
91657
92149
  if (!exists(path2))
91658
92150
  return null;
91659
92151
  let raw;
@@ -91714,15 +92206,15 @@ function renderUpdateOutcomeLine(entry) {
91714
92206
  `);
91715
92207
  }
91716
92208
  function claimUpdateAnnouncement(requestId, opts = {}) {
91717
- const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join51(homedir18(), ".switchroom");
91718
- const dir = join51(stateDir, "update-announced");
92209
+ const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join52(homedir18(), ".switchroom");
92210
+ const dir = join52(stateDir, "update-announced");
91719
92211
  try {
91720
- mkdirSync40(dir, { recursive: true });
92212
+ mkdirSync41(dir, { recursive: true });
91721
92213
  } catch {
91722
92214
  return false;
91723
92215
  }
91724
92216
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
91725
- const path2 = join51(dir, safeId);
92217
+ const path2 = join52(dir, safeId);
91726
92218
  try {
91727
92219
  const fd = openSync9(path2, "wx");
91728
92220
  closeSync9(fd);
@@ -91742,7 +92234,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
91742
92234
 
91743
92235
  // issues-card.ts
91744
92236
  init_card_format();
91745
- import { readFileSync as readFileSync50, writeFileSync as writeFileSync40 } from "node:fs";
92237
+ import { readFileSync as readFileSync51, writeFileSync as writeFileSync41 } from "node:fs";
91746
92238
  var SEVERITY_EMOJI = {
91747
92239
  info: "\u2139\ufe0f",
91748
92240
  warn: "\u26a0\ufe0f",
@@ -91834,7 +92326,7 @@ function extractRetryAfterSecs2(err) {
91834
92326
  var COOLDOWN_JITTER_MS2 = 500;
91835
92327
  function readPersistedMessageId(path2, log) {
91836
92328
  try {
91837
- const raw = readFileSync50(path2, "utf8");
92329
+ const raw = readFileSync51(path2, "utf8");
91838
92330
  const parsed = JSON.parse(raw);
91839
92331
  const v = parsed.messageId;
91840
92332
  if (typeof v === "number" && Number.isInteger(v) && v > 0)
@@ -91850,7 +92342,7 @@ function readPersistedMessageId(path2, log) {
91850
92342
  }
91851
92343
  function writePersistedMessageId(path2, messageId, log) {
91852
92344
  try {
91853
- writeFileSync40(path2, JSON.stringify({ messageId }) + `
92345
+ writeFileSync41(path2, JSON.stringify({ messageId }) + `
91854
92346
  `, { mode: 384 });
91855
92347
  } catch (err) {
91856
92348
  log(`issues-card: persist write failed (${err.message})`);
@@ -91943,24 +92435,24 @@ function createIssuesCardHandle(opts) {
91943
92435
  }
91944
92436
 
91945
92437
  // issues-watcher.ts
91946
- import { existsSync as existsSync48, statSync as statSync16 } from "node:fs";
91947
- import { join as join53 } from "node:path";
92438
+ import { existsSync as existsSync49, statSync as statSync16 } from "node:fs";
92439
+ import { join as join54 } from "node:path";
91948
92440
 
91949
92441
  // ../src/issues/store.ts
91950
92442
  import {
91951
92443
  closeSync as closeSync10,
91952
- existsSync as existsSync47,
91953
- mkdirSync as mkdirSync41,
92444
+ existsSync as existsSync48,
92445
+ mkdirSync as mkdirSync42,
91954
92446
  openSync as openSync10,
91955
92447
  readdirSync as readdirSync11,
91956
- readFileSync as readFileSync51,
91957
- renameSync as renameSync20,
92448
+ readFileSync as readFileSync52,
92449
+ renameSync as renameSync21,
91958
92450
  statSync as statSync15,
91959
92451
  unlinkSync as unlinkSync23,
91960
- writeFileSync as writeFileSync41,
92452
+ writeFileSync as writeFileSync42,
91961
92453
  writeSync as writeSync6
91962
92454
  } from "node:fs";
91963
- import { join as join52 } from "node:path";
92455
+ import { join as join53 } from "node:path";
91964
92456
  import { randomBytes as randomBytes8 } from "node:crypto";
91965
92457
  import { execSync } from "node:child_process";
91966
92458
 
@@ -91979,12 +92471,12 @@ init_redact();
91979
92471
  var ISSUES_FILE = "issues.jsonl";
91980
92472
  var ISSUES_LOCK = "issues.lock";
91981
92473
  function readAll(stateDir) {
91982
- const path2 = join52(stateDir, ISSUES_FILE);
91983
- if (!existsSync47(path2))
92474
+ const path2 = join53(stateDir, ISSUES_FILE);
92475
+ if (!existsSync48(path2))
91984
92476
  return [];
91985
92477
  let raw;
91986
92478
  try {
91987
- raw = readFileSync51(path2, "utf-8");
92479
+ raw = readFileSync52(path2, "utf-8");
91988
92480
  } catch {
91989
92481
  return [];
91990
92482
  }
@@ -92016,7 +92508,7 @@ function list2(stateDir, opts = {}) {
92016
92508
  });
92017
92509
  }
92018
92510
  function resolve11(stateDir, fingerprint, nowFn = Date.now) {
92019
- if (!existsSync47(join52(stateDir, ISSUES_FILE)))
92511
+ if (!existsSync48(join53(stateDir, ISSUES_FILE)))
92020
92512
  return 0;
92021
92513
  return withLock(stateDir, () => {
92022
92514
  const all2 = readAll(stateDir);
@@ -92034,14 +92526,14 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
92034
92526
  });
92035
92527
  }
92036
92528
  function writeAll(stateDir, events) {
92037
- const path2 = join52(stateDir, ISSUES_FILE);
92529
+ const path2 = join53(stateDir, ISSUES_FILE);
92038
92530
  sweepOrphanTmpFiles(stateDir);
92039
92531
  const tmp = `${path2}.tmp-${process.pid}-${randomBytes8(4).toString("hex")}`;
92040
92532
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
92041
92533
  `) + `
92042
92534
  `;
92043
- writeFileSync41(tmp, body, "utf-8");
92044
- renameSync20(tmp, path2);
92535
+ writeFileSync42(tmp, body, "utf-8");
92536
+ renameSync21(tmp, path2);
92045
92537
  }
92046
92538
  var ORPHAN_TMP_TTL_MS = 60000;
92047
92539
  var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
@@ -92056,7 +92548,7 @@ function sweepOrphanTmpFiles(stateDir) {
92056
92548
  for (const entry of entries) {
92057
92549
  if (!entry.startsWith(TMP_PREFIX))
92058
92550
  continue;
92059
- const tmpPath2 = join52(stateDir, entry);
92551
+ const tmpPath2 = join53(stateDir, entry);
92060
92552
  try {
92061
92553
  const stat = statSync15(tmpPath2);
92062
92554
  if (stat.mtimeMs < cutoff) {
@@ -92068,7 +92560,7 @@ function sweepOrphanTmpFiles(stateDir) {
92068
92560
  var LOCK_RETRY_MS = 25;
92069
92561
  var LOCK_TIMEOUT_MS = 1e4;
92070
92562
  function withLock(stateDir, fn) {
92071
- const lockPath = join52(stateDir, ISSUES_LOCK);
92563
+ const lockPath = join53(stateDir, ISSUES_LOCK);
92072
92564
  const startedAt = Date.now();
92073
92565
  let fd = null;
92074
92566
  while (fd === null) {
@@ -92103,7 +92595,7 @@ function withLock(stateDir, fn) {
92103
92595
  function tryStealStaleLock(lockPath) {
92104
92596
  let pidStr;
92105
92597
  try {
92106
- pidStr = readFileSync51(lockPath, "utf-8").trim();
92598
+ pidStr = readFileSync52(lockPath, "utf-8").trim();
92107
92599
  } catch {
92108
92600
  return true;
92109
92601
  }
@@ -92153,7 +92645,7 @@ function isIssueEvent(v) {
92153
92645
  // issues-watcher.ts
92154
92646
  var DEFAULT_POLL_INTERVAL_MS2 = 2000;
92155
92647
  function startIssuesWatcher(opts) {
92156
- const path2 = join53(opts.stateDir, ISSUES_FILE);
92648
+ const path2 = join54(opts.stateDir, ISSUES_FILE);
92157
92649
  const log = opts.log ?? (() => {});
92158
92650
  const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
92159
92651
  const setIntervalFn = opts.setInterval ?? setInterval;
@@ -92201,7 +92693,7 @@ function startIssuesWatcher(opts) {
92201
92693
  };
92202
92694
  }
92203
92695
  function defaultSignatureProvider(path2) {
92204
- if (!existsSync48(path2))
92696
+ if (!existsSync49(path2))
92205
92697
  return null;
92206
92698
  try {
92207
92699
  const stat = statSync16(path2);
@@ -92912,8 +93404,8 @@ function readBashCommand(inputPreview) {
92912
93404
  }
92913
93405
 
92914
93406
  // gateway/scoped-grant-store.ts
92915
- import { readFileSync as readFileSync52, writeFileSync as writeFileSync42 } from "node:fs";
92916
- import { join as join54 } from "node:path";
93407
+ import { readFileSync as readFileSync53, writeFileSync as writeFileSync43 } from "node:fs";
93408
+ import { join as join55 } from "node:path";
92917
93409
 
92918
93410
  // scoped-approval.ts
92919
93411
  var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
@@ -92955,11 +93447,11 @@ function scopedGrantPersistEnabled(env = process.env) {
92955
93447
  return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
92956
93448
  }
92957
93449
  function createScopedGrantStore(stateDir, env = process.env) {
92958
- const filePath = join54(stateDir, "scoped-grants.json");
93450
+ const filePath = join55(stateDir, "scoped-grants.json");
92959
93451
  const enabled7 = scopedGrantPersistEnabled(env);
92960
93452
  function read() {
92961
93453
  try {
92962
- const raw = readFileSync52(filePath, "utf-8");
93454
+ const raw = readFileSync53(filePath, "utf-8");
92963
93455
  const parsed = JSON.parse(raw);
92964
93456
  return Array.isArray(parsed) ? parsed : [];
92965
93457
  } catch {
@@ -92977,7 +93469,7 @@ function createScopedGrantStore(stateDir, env = process.env) {
92977
93469
  if (!enabled7)
92978
93470
  return;
92979
93471
  try {
92980
- writeFileSync42(filePath, JSON.stringify(serializeScopedGrants(store2)), {
93472
+ writeFileSync43(filePath, JSON.stringify(serializeScopedGrants(store2)), {
92981
93473
  encoding: "utf-8",
92982
93474
  mode: 384
92983
93475
  });
@@ -93328,8 +93820,8 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
93328
93820
 
93329
93821
  // credits-watch.ts
93330
93822
  init_card_format();
93331
- import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync49, mkdirSync as mkdirSync42 } from "fs";
93332
- import { join as join55 } from "path";
93823
+ import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync50, mkdirSync as mkdirSync43 } from "fs";
93824
+ import { join as join56 } from "path";
93333
93825
  var STATE_FILE = "credits-watch.json";
93334
93826
  var DEFAULT_CREDIT_FATAL_REASONS = new Set;
93335
93827
  var KNOWN_CREDIT_REASONS = [
@@ -93351,12 +93843,12 @@ function emptyCreditState() {
93351
93843
  return { lastNotifiedReason: null, lastNotifiedAt: 0 };
93352
93844
  }
93353
93845
  function readClaudeJsonOverage(claudeConfigDir) {
93354
- const path2 = join55(claudeConfigDir, ".claude.json");
93355
- if (!existsSync49(path2))
93846
+ const path2 = join56(claudeConfigDir, ".claude.json");
93847
+ if (!existsSync50(path2))
93356
93848
  return null;
93357
93849
  let raw;
93358
93850
  try {
93359
- raw = readFileSync53(path2, "utf-8");
93851
+ raw = readFileSync54(path2, "utf-8");
93360
93852
  } catch {
93361
93853
  return null;
93362
93854
  }
@@ -93434,11 +93926,11 @@ function humanizeReason(reason) {
93434
93926
  }
93435
93927
  }
93436
93928
  function loadCreditState(stateDir) {
93437
- const path2 = join55(stateDir, STATE_FILE);
93438
- if (!existsSync49(path2))
93929
+ const path2 = join56(stateDir, STATE_FILE);
93930
+ if (!existsSync50(path2))
93439
93931
  return emptyCreditState();
93440
93932
  try {
93441
- const raw = readFileSync53(path2, "utf-8");
93933
+ const raw = readFileSync54(path2, "utf-8");
93442
93934
  const parsed = JSON.parse(raw);
93443
93935
  if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
93444
93936
  return {
@@ -93450,17 +93942,17 @@ function loadCreditState(stateDir) {
93450
93942
  return emptyCreditState();
93451
93943
  }
93452
93944
  function saveCreditState(stateDir, state7) {
93453
- mkdirSync42(stateDir, { recursive: true });
93454
- const path2 = join55(stateDir, STATE_FILE);
93455
- writeFileSync43(path2, JSON.stringify(state7, null, 2) + `
93945
+ mkdirSync43(stateDir, { recursive: true });
93946
+ const path2 = join56(stateDir, STATE_FILE);
93947
+ writeFileSync44(path2, JSON.stringify(state7, null, 2) + `
93456
93948
  `, { mode: 384 });
93457
93949
  }
93458
93950
 
93459
93951
  // quota-watch.ts
93460
93952
  init_auth_snapshot_format();
93461
93953
  init_card_format();
93462
- import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync50, mkdirSync as mkdirSync43 } from "fs";
93463
- import { join as join56 } from "path";
93954
+ import { readFileSync as readFileSync55, writeFileSync as writeFileSync45, existsSync as existsSync51, mkdirSync as mkdirSync44 } from "fs";
93955
+ import { join as join57 } from "path";
93464
93956
  var STATE_FILE2 = "quota-watch.json";
93465
93957
  function emptyQuotaWatchState() {
93466
93958
  return {};
@@ -93698,11 +94190,11 @@ function buildRecoveryMessage(agentName3, snap) {
93698
94190
  `);
93699
94191
  }
93700
94192
  function loadQuotaWatchState(stateDir) {
93701
- const path2 = join56(stateDir, STATE_FILE2);
93702
- if (!existsSync50(path2))
94193
+ const path2 = join57(stateDir, STATE_FILE2);
94194
+ if (!existsSync51(path2))
93703
94195
  return emptyQuotaWatchState();
93704
94196
  try {
93705
- const raw = readFileSync54(path2, "utf-8");
94197
+ const raw = readFileSync55(path2, "utf-8");
93706
94198
  const parsed = JSON.parse(raw);
93707
94199
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
93708
94200
  return emptyQuotaWatchState();
@@ -93719,9 +94211,9 @@ function loadQuotaWatchState(stateDir) {
93719
94211
  }
93720
94212
  }
93721
94213
  function saveQuotaWatchState(stateDir, state7) {
93722
- mkdirSync43(stateDir, { recursive: true });
93723
- const path2 = join56(stateDir, STATE_FILE2);
93724
- writeFileSync44(path2, JSON.stringify(state7, null, 2) + `
94214
+ mkdirSync44(stateDir, { recursive: true });
94215
+ const path2 = join57(stateDir, STATE_FILE2);
94216
+ writeFileSync45(path2, JSON.stringify(state7, null, 2) + `
93725
94217
  `, { mode: 384 });
93726
94218
  }
93727
94219
  function patchQuotaWatchState(current, accountLabel, accountState) {
@@ -93769,27 +94261,27 @@ init_auth_snapshot_format2();
93769
94261
  // gateway/turn-active-marker.ts
93770
94262
  import {
93771
94263
  closeSync as closeSync11,
93772
- existsSync as existsSync51,
93773
- mkdirSync as mkdirSync44,
94264
+ existsSync as existsSync52,
94265
+ mkdirSync as mkdirSync45,
93774
94266
  openSync as openSync11,
93775
- readFileSync as readFileSync55,
94267
+ readFileSync as readFileSync56,
93776
94268
  statSync as statSync17,
93777
94269
  unlinkSync as unlinkSync24,
93778
94270
  utimesSync as utimesSync2,
93779
- writeFileSync as writeFileSync45
94271
+ writeFileSync as writeFileSync46
93780
94272
  } from "node:fs";
93781
- import { join as join57 } from "node:path";
94273
+ import { join as join58 } from "node:path";
93782
94274
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
93783
94275
  var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
93784
94276
  var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
93785
94277
  function removeTurnActiveMarker2(stateDir) {
93786
94278
  try {
93787
- unlinkSync24(join57(stateDir, TURN_ACTIVE_MARKER_FILE2));
94279
+ unlinkSync24(join58(stateDir, TURN_ACTIVE_MARKER_FILE2));
93788
94280
  } catch {}
93789
94281
  }
93790
94282
  function sweepStaleTurnActiveMarker(stateDir, opts) {
93791
- const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
93792
- if (!existsSync51(path2))
94283
+ const path2 = join58(stateDir, TURN_ACTIVE_MARKER_FILE2);
94284
+ if (!existsSync52(path2))
93793
94285
  return false;
93794
94286
  const now = opts.now ?? Date.now();
93795
94287
  try {
@@ -93801,7 +94293,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
93801
94293
  return false;
93802
94294
  let payload = null;
93803
94295
  try {
93804
- payload = readFileSync55(path2, "utf8");
94296
+ payload = readFileSync56(path2, "utf8");
93805
94297
  } catch {}
93806
94298
  unlinkSync24(path2);
93807
94299
  if (opts.onRemove) {
@@ -93819,7 +94311,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
93819
94311
  }
93820
94312
  }
93821
94313
  function readTurnActiveMarkerAgeMs2(stateDir, now) {
93822
- const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
94314
+ const path2 = join58(stateDir, TURN_ACTIVE_MARKER_FILE2);
93823
94315
  try {
93824
94316
  const st = statSync17(path2);
93825
94317
  return (now ?? Date.now()) - st.mtimeMs;
@@ -93832,19 +94324,19 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
93832
94324
  }
93833
94325
 
93834
94326
  // gateway/gateway-heartbeat.ts
93835
- import { mkdirSync as mkdirSync45, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
93836
- import { join as join58 } from "node:path";
94327
+ import { mkdirSync as mkdirSync46, utimesSync as utimesSync3, writeFileSync as writeFileSync47 } from "node:fs";
94328
+ import { join as join59 } from "node:path";
93837
94329
  var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
93838
94330
  var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
93839
94331
  function touchGatewayHeartbeat(stateDir) {
93840
- const path2 = join58(stateDir, GATEWAY_HEARTBEAT_FILE);
94332
+ const path2 = join59(stateDir, GATEWAY_HEARTBEAT_FILE);
93841
94333
  const now = new Date;
93842
94334
  try {
93843
94335
  utimesSync3(path2, now, now);
93844
94336
  } catch {
93845
94337
  try {
93846
- mkdirSync45(stateDir, { recursive: true });
93847
- writeFileSync46(path2, `${Date.now()}
94338
+ mkdirSync46(stateDir, { recursive: true });
94339
+ writeFileSync47(path2, `${Date.now()}
93848
94340
  `, { mode: 384 });
93849
94341
  } catch {}
93850
94342
  }
@@ -93887,14 +94379,32 @@ async function sweepOutbox(deps) {
93887
94379
  textAlreadyDelivered: resolved != null && deps.textAlreadyDelivered(resolved.chatId, resolved.threadId, record2.text),
93888
94380
  routable: resolved != null,
93889
94381
  routePrefix,
93890
- quietMs: deps.quietMs ?? OUTBOX_QUIET_MS
94382
+ quietMs: deps.quietMs ?? OUTBOX_QUIET_MS,
94383
+ shownLedgerHit: isShownBlock(record2.turnNonce, record2.text, deps.stateDir)
93891
94384
  });
93892
94385
  if (decision.action !== "send" && decision.action !== "send-delayed") {
93893
94386
  summary.skipped++;
93894
- if (decision.action === "skip-journaled") {
94387
+ if (decision.action === "skip-ephemeral-shown") {
94388
+ appendDelivered({
94389
+ turnNonce: record2.turnNonce,
94390
+ textSha256: record2.textSha256,
94391
+ ts: now,
94392
+ deliverySource: "sweep",
94393
+ replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
94394
+ }, deps.stateDir);
94395
+ clearOutboxRecord(record2.turnNonce, deps.stateDir);
94396
+ log(`outbox-sweep: suppressed ephemeral-shown nonce=${record2.turnNonce}
94397
+ `);
94398
+ } else if (decision.action === "skip-journaled") {
93895
94399
  clearOutboxRecord(record2.turnNonce, deps.stateDir);
93896
94400
  } else if (decision.action === "skip-dedup") {
93897
- appendDelivered({ turnNonce: record2.turnNonce, textSha256: record2.textSha256, ts: now }, deps.stateDir);
94401
+ appendDelivered({
94402
+ turnNonce: record2.turnNonce,
94403
+ textSha256: record2.textSha256,
94404
+ ts: now,
94405
+ deliverySource: "sweep",
94406
+ replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
94407
+ }, deps.stateDir);
93898
94408
  clearOutboxRecord(record2.turnNonce, deps.stateDir);
93899
94409
  }
93900
94410
  continue;
@@ -93907,7 +94417,14 @@ async function sweepOutbox(deps) {
93907
94417
  const resolvedChat = resolved;
93908
94418
  try {
93909
94419
  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);
94420
+ appendDelivered({
94421
+ turnNonce: record2.turnNonce,
94422
+ textSha256: record2.textSha256,
94423
+ tgMessageId: messageId,
94424
+ ts: now,
94425
+ deliverySource: "sweep",
94426
+ replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
94427
+ }, deps.stateDir);
93911
94428
  removeClaimed(record2.turnNonce, deps.stateDir);
93912
94429
  summary.delivered++;
93913
94430
  log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
@@ -93966,10 +94483,10 @@ function startOutboxSweep(deps) {
93966
94483
  }
93967
94484
 
93968
94485
  // ../src/build-info.ts
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;
94486
+ var VERSION = "0.19.15";
94487
+ var COMMIT_SHA = "20691874";
94488
+ var COMMIT_DATE = "2026-07-24T12:46:35+10:00";
94489
+ var LATEST_PR = 3518;
93973
94490
  var COMMITS_AHEAD_OF_TAG = 0;
93974
94491
 
93975
94492
  // gateway/boot-version.ts
@@ -94047,11 +94564,11 @@ init_peercred();
94047
94564
  import * as net5 from "node:net";
94048
94565
  import * as fs2 from "node:fs";
94049
94566
  import { homedir as homedir19 } from "node:os";
94050
- import { join as join59 } from "node:path";
94567
+ import { join as join60 } from "node:path";
94051
94568
  var DEFAULT_TIMEOUT_MS4 = 2000;
94052
94569
  var UNLOCK_TIMEOUT_MS = 30000;
94053
- var LEGACY_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "vault-broker.sock");
94054
- var OPERATOR_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "broker-operator", "sock");
94570
+ var LEGACY_SOCKET_PATH2 = join60(homedir19(), ".switchroom", "vault-broker.sock");
94571
+ var OPERATOR_SOCKET_PATH2 = join60(homedir19(), ".switchroom", "broker-operator", "sock");
94055
94572
  function defaultBrokerSocketPath2() {
94056
94573
  if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
94057
94574
  return OPERATOR_SOCKET_PATH2;
@@ -94933,8 +95450,8 @@ function resolveVaultApprovalPosture(broker) {
94933
95450
  }
94934
95451
 
94935
95452
  // registry/turns-schema.ts
94936
- import { chmodSync as chmodSync12, mkdirSync as mkdirSync46 } from "fs";
94937
- import { join as join60 } from "path";
95453
+ import { chmodSync as chmodSync12, mkdirSync as mkdirSync47 } from "fs";
95454
+ import { join as join61 } from "path";
94938
95455
  var DatabaseClass3 = null;
94939
95456
  function loadDatabaseClass3() {
94940
95457
  if (DatabaseClass3 != null)
@@ -95006,9 +95523,9 @@ function applySchema(db3) {
95006
95523
  }
95007
95524
  function openTurnsDb(agentDir) {
95008
95525
  const Database = loadDatabaseClass3();
95009
- const dir = join60(agentDir, "telegram");
95010
- mkdirSync46(dir, { recursive: true, mode: 448 });
95011
- const path2 = join60(dir, "registry.db");
95526
+ const dir = join61(agentDir, "telegram");
95527
+ mkdirSync47(dir, { recursive: true, mode: 448 });
95528
+ const path2 = join61(dir, "registry.db");
95012
95529
  const db3 = new Database(path2, { create: true });
95013
95530
  applySchema(db3);
95014
95531
  try {
@@ -95352,7 +95869,7 @@ function selectResumeBuilder(endedVia, opts) {
95352
95869
  }
95353
95870
 
95354
95871
  // gateway/bridge-dead-watchdog.ts
95355
- import { readFileSync as readFileSync57, writeFileSync as writeFileSync47, renameSync as renameSync21, unlinkSync as unlinkSync25 } from "node:fs";
95872
+ import { readFileSync as readFileSync58, writeFileSync as writeFileSync48, renameSync as renameSync22, unlinkSync as unlinkSync25 } from "node:fs";
95356
95873
 
95357
95874
  // gateway/cron-session.ts
95358
95875
  var CRON_IDENTITY_SUFFIX2 = "-cron";
@@ -95385,7 +95902,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
95385
95902
  const nowMs3 = opts.nowMs ?? Date.now();
95386
95903
  const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
95387
95904
  const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
95388
- const readFile = opts.readFile ?? ((p) => readFileSync57(p, "utf8"));
95905
+ const readFile = opts.readFile ?? ((p) => readFileSync58(p, "utf8"));
95389
95906
  let raw;
95390
95907
  try {
95391
95908
  raw = readFile(path2);
@@ -95406,13 +95923,13 @@ function readFreshCrashLogTail(path2, opts = {}) {
95406
95923
  }
95407
95924
  function writeBridgeDeadEscalationMarker(path2, marker) {
95408
95925
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
95409
- writeFileSync47(tmp, JSON.stringify(marker), "utf8");
95410
- renameSync21(tmp, path2);
95926
+ writeFileSync48(tmp, JSON.stringify(marker), "utf8");
95927
+ renameSync22(tmp, path2);
95411
95928
  }
95412
95929
  function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
95413
95930
  let marker = null;
95414
95931
  try {
95415
- const parsed = JSON.parse(readFileSync57(path2, "utf8"));
95932
+ const parsed = JSON.parse(readFileSync58(path2, "utf8"));
95416
95933
  if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
95417
95934
  const age = nowMs3 - parsed.ts;
95418
95935
  if (age >= 0 && age < maxAgeMs) {
@@ -95565,7 +96082,7 @@ function createBridgeDeadWatchdog(opts) {
95565
96082
  }
95566
96083
 
95567
96084
  // gateway/boot-probes.ts
95568
- import { readFileSync as readFileSync58, readdirSync as readdirSync12, existsSync as existsSync53 } from "fs";
96085
+ import { readFileSync as readFileSync59, readdirSync as readdirSync12, existsSync as existsSync54 } from "fs";
95569
96086
  init_quota_cache();
95570
96087
  init_quota_check();
95571
96088
  import { execFile as execFileCb2 } from "child_process";
@@ -95573,7 +96090,7 @@ import { promisify as promisify2 } from "util";
95573
96090
  var execFile2 = promisify2(execFileCb2);
95574
96091
  var realProcFs2 = {
95575
96092
  readdir: (p) => readdirSync12(p),
95576
- readFile: (p) => readFileSync58(p, "utf-8")
96093
+ readFile: (p) => readFileSync59(p, "utf-8")
95577
96094
  };
95578
96095
  function findAgentProcessInContainer2(fs3 = realProcFs2) {
95579
96096
  let entries;
@@ -95823,7 +96340,7 @@ if (isGatewayMain) {
95823
96340
  shutdownAnalytics();
95824
96341
  });
95825
96342
  }
95826
- var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join61(homedir20(), ".claude", "channels", "telegram");
96343
+ var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join62(homedir20(), ".claude", "channels", "telegram");
95827
96344
  var permCardStore = createPermissionCardStore(STATE_DIR);
95828
96345
  var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
95829
96346
  var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
@@ -95862,7 +96379,7 @@ function alwaysAllowDrainDeps() {
95862
96379
  return {
95863
96380
  readConfigText: () => {
95864
96381
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
95865
- return readFileSync59(cfgPath, "utf8");
96382
+ return readFileSync60(cfgPath, "utf8");
95866
96383
  },
95867
96384
  resolveAllowList: (_configText, agentName3) => {
95868
96385
  const cfg = loadConfig2();
@@ -95923,11 +96440,11 @@ function scheduleAlwaysAllowPersistDrain() {
95923
96440
  }, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
95924
96441
  timer3.unref?.();
95925
96442
  }
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");
96443
+ var ACCESS_FILE = join62(STATE_DIR, "access.json");
96444
+ var APPROVED_DIR = join62(STATE_DIR, "approved");
96445
+ var ENV_FILE = join62(STATE_DIR, ".env");
96446
+ var INBOX_DIR = join62(STATE_DIR, "inbox");
96447
+ var PEOPLE_FILE = join62(STATE_DIR, "people.json");
95931
96448
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
95932
96449
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
95933
96450
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -95992,7 +96509,7 @@ function formatBootVersion() {
95992
96509
  }
95993
96510
  try {
95994
96511
  chmodSync14(ENV_FILE, 384);
95995
- for (const line of readFileSync59(ENV_FILE, "utf8").split(`
96512
+ for (const line of readFileSync60(ENV_FILE, "utf8").split(`
95996
96513
  `)) {
95997
96514
  const m = line.match(/^(\w+)=(.*)$/);
95998
96515
  if (m && process.env[m[1]] === undefined)
@@ -96013,7 +96530,7 @@ var bot;
96013
96530
  var lastGetUpdatesHeartbeatMs = Date.now();
96014
96531
  var GRAMMY_VERSION = (() => {
96015
96532
  try {
96016
- const raw = readFileSync59(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
96533
+ const raw = readFileSync60(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
96017
96534
  return JSON.parse(raw).version ?? "unknown";
96018
96535
  } catch {
96019
96536
  return "unknown";
@@ -96076,7 +96593,7 @@ function assertSendable(f) {
96076
96593
  } catch {
96077
96594
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
96078
96595
  }
96079
- const inbox = join61(stateReal, "inbox");
96596
+ const inbox = join62(stateReal, "inbox");
96080
96597
  if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
96081
96598
  throw new Error(`refusing to send channel state: ${f}`);
96082
96599
  }
@@ -96095,7 +96612,7 @@ function assertSendable(f) {
96095
96612
  }
96096
96613
  function readAccessFile() {
96097
96614
  try {
96098
- const raw = readFileSync59(ACCESS_FILE, "utf8");
96615
+ const raw = readFileSync60(ACCESS_FILE, "utf8");
96099
96616
  const parsed = JSON.parse(raw);
96100
96617
  const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
96101
96618
  const groups = {};
@@ -96135,7 +96652,7 @@ function readAccessFile() {
96135
96652
  if (err.code === "ENOENT")
96136
96653
  return defaultAccess();
96137
96654
  try {
96138
- renameSync22(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
96655
+ renameSync23(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
96139
96656
  } catch {}
96140
96657
  process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
96141
96658
  `);
@@ -96157,7 +96674,7 @@ function loadAccess() {
96157
96674
  }
96158
96675
  function readPeopleFile() {
96159
96676
  try {
96160
- const raw = readFileSync59(PEOPLE_FILE, "utf8");
96677
+ const raw = readFileSync60(PEOPLE_FILE, "utf8");
96161
96678
  const parsed = JSON.parse(raw);
96162
96679
  if (!Array.isArray(parsed.entries))
96163
96680
  return [];
@@ -96179,11 +96696,11 @@ function assertAllowedChat(chat_id) {
96179
96696
  function saveAccess(a) {
96180
96697
  if (STATIC)
96181
96698
  return;
96182
- mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
96699
+ mkdirSync49(STATE_DIR, { recursive: true, mode: 448 });
96183
96700
  const tmp = ACCESS_FILE + ".tmp";
96184
- writeFileSync49(tmp, JSON.stringify(a, null, 2) + `
96701
+ writeFileSync50(tmp, JSON.stringify(a, null, 2) + `
96185
96702
  `, { mode: 384 });
96186
- renameSync22(tmp, ACCESS_FILE);
96703
+ renameSync23(tmp, ACCESS_FILE);
96187
96704
  }
96188
96705
  function pruneExpired(a) {
96189
96706
  const now = Date.now();
@@ -96201,7 +96718,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
96201
96718
  if (isGatewayMain && HISTORY_ENABLED) {
96202
96719
  try {
96203
96720
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
96204
- process.stderr.write(`telegram gateway: history capture enabled at ${join61(STATE_DIR, "history.db")}
96721
+ process.stderr.write(`telegram gateway: history capture enabled at ${join62(STATE_DIR, "history.db")}
96205
96722
  `);
96206
96723
  } catch (err) {
96207
96724
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -96220,12 +96737,12 @@ if (isGatewayMain)
96220
96737
  let markerTurnKey = null;
96221
96738
  let markerAgeMs = null;
96222
96739
  try {
96223
- const markerPath = join61(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
96224
- if (existsSync55(markerPath)) {
96740
+ const markerPath = join62(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
96741
+ if (existsSync56(markerPath)) {
96225
96742
  const st = statSync20(markerPath);
96226
96743
  markerAgeMs = Date.now() - st.mtimeMs;
96227
96744
  try {
96228
- const payload = JSON.parse(readFileSync59(markerPath, "utf8"));
96745
+ const payload = JSON.parse(readFileSync60(markerPath, "utf8"));
96229
96746
  if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
96230
96747
  markerTurnKey = payload.turnKey;
96231
96748
  }
@@ -96245,10 +96762,10 @@ if (isGatewayMain)
96245
96762
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
96246
96763
  `);
96247
96764
  } else {
96248
- process.stderr.write(`telegram gateway: turn-registry initialized at ${join61(agentDir, "telegram", "registry.db")}
96765
+ process.stderr.write(`telegram gateway: turn-registry initialized at ${join62(agentDir, "telegram", "registry.db")}
96249
96766
  `);
96250
96767
  }
96251
- const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join61(STATE_DIR, "bridge-dead-escalation.json"));
96768
+ const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join62(STATE_DIR, "bridge-dead-escalation.json"));
96252
96769
  if (bridgeDeadMarker != null) {
96253
96770
  bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
96254
96771
  process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
@@ -96261,7 +96778,7 @@ if (isGatewayMain)
96261
96778
  const pending2 = findLatestTurnIfInterrupted(turnsDb);
96262
96779
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
96263
96780
  if (pending2 != null && selfAgent) {
96264
- const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
96781
+ const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join62(STATE_DIR, "clean-shutdown.json");
96265
96782
  const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
96266
96783
  const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
96267
96784
  const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
@@ -96374,7 +96891,7 @@ if (isGatewayMain)
96374
96891
  `);
96375
96892
  }
96376
96893
  }
96377
- const pendingEnvPath = join61(agentDir, ".pending-turn.env");
96894
+ const pendingEnvPath = join62(agentDir, ".pending-turn.env");
96378
96895
  try {
96379
96896
  if (pending2 != null) {
96380
96897
  const lines = [
@@ -96388,13 +96905,13 @@ if (isGatewayMain)
96388
96905
  pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
96389
96906
  ];
96390
96907
  const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
96391
- writeFileSync49(pendingEnvTmp, lines.join(`
96908
+ writeFileSync50(pendingEnvTmp, lines.join(`
96392
96909
  `) + `
96393
96910
  `, { mode: 384 });
96394
- renameSync22(pendingEnvTmp, pendingEnvPath);
96911
+ renameSync23(pendingEnvTmp, pendingEnvPath);
96395
96912
  process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
96396
96913
  `);
96397
- } else if (existsSync55(pendingEnvPath)) {
96914
+ } else if (existsSync56(pendingEnvPath)) {
96398
96915
  rmSync6(pendingEnvPath, { force: true });
96399
96916
  process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
96400
96917
  `);
@@ -96502,7 +97019,7 @@ function checkApprovals() {
96502
97019
  return;
96503
97020
  }
96504
97021
  for (const senderId of files) {
96505
- const file = join61(APPROVED_DIR, senderId);
97022
+ const file = join62(APPROVED_DIR, senderId);
96506
97023
  bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
96507
97024
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
96508
97025
  `);
@@ -96675,12 +97192,12 @@ function noteAgentOutputAt(key, ts) {
96675
97192
  lastAgentOutputAt.delete(oldest);
96676
97193
  }
96677
97194
  }
96678
- var OBLIGATION_STORE_PATH = join61(STATE_DIR, "obligations.json");
97195
+ var OBLIGATION_STORE_PATH = join62(STATE_DIR, "obligations.json");
96679
97196
  var obligationStoreFs = {
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)
97197
+ readFileSync: (p) => readFileSync60(p, "utf8"),
97198
+ writeFileSync: (p, d) => writeFileSync50(p, d),
97199
+ renameSync: (a, b) => renameSync23(a, b),
97200
+ existsSync: (p) => existsSync56(p)
96684
97201
  };
96685
97202
  var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
96686
97203
  onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
@@ -97459,9 +97976,9 @@ function emitTurnRecord(turn, endedAt) {
97459
97976
  return;
97460
97977
  }
97461
97978
  },
97462
- rename: (from, to) => renameSync22(from, to)
97979
+ rename: (from, to) => renameSync23(from, to)
97463
97980
  });
97464
- appendFileSync8(turnsPath, rec);
97981
+ appendFileSync9(turnsPath, rec);
97465
97982
  } catch {}
97466
97983
  }
97467
97984
  function maybeProactiveCompact() {
@@ -98087,7 +98604,7 @@ function sweepStaleAlwaysAllowCorrelations(now = Date.now()) {
98087
98604
  var MENTAL_MODEL_CORRELATION_TTL_MS = 720000;
98088
98605
  var pendingMentalModelCorrelations = createSweepableStore((entry, now) => now - entry.createdAt > MENTAL_MODEL_CORRELATION_TTL_MS);
98089
98606
  function mentalModelCorrelationKey(agentName3, unifiedDiff) {
98090
- return `${agentName3}::${createHash5("sha256").update(unifiedDiff).digest("hex")}`;
98607
+ return `${agentName3}::${createHash6("sha256").update(unifiedDiff).digest("hex")}`;
98091
98608
  }
98092
98609
  function sweepStaleMentalModelCorrelations(now = Date.now()) {
98093
98610
  pendingMentalModelCorrelations.sweep(now);
@@ -99023,28 +99540,28 @@ var statusPinState = new Map;
99023
99540
  var statusPinChatIds = new Map;
99024
99541
  var statusPinPinnedAt = new Map;
99025
99542
  var statusPinRightsCache = new PinRightsCache2;
99026
- var STATUS_PIN_STORE_PATH = join61(STATE_DIR, "status-pins.json");
99543
+ var STATUS_PIN_STORE_PATH = join62(STATE_DIR, "status-pins.json");
99027
99544
  var statusPinStoreFs = {
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)
99545
+ readFileSync: (p) => readFileSync60(p, "utf8"),
99546
+ writeFileSync: (p, d) => writeFileSync50(p, d),
99547
+ renameSync: (a, b) => renameSync23(a, b),
99548
+ existsSync: (p) => existsSync56(p)
99032
99549
  };
99033
99550
  var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
99034
- var ACTIVITY_CARD_STORE_PATH = join61(STATE_DIR, "activity-cards-pending.json");
99551
+ var ACTIVITY_CARD_STORE_PATH = join62(STATE_DIR, "activity-cards-pending.json");
99035
99552
  var activityCardStoreFs = {
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)
99553
+ readFileSync: (p) => readFileSync60(p, "utf8"),
99554
+ writeFileSync: (p, d) => writeFileSync50(p, d),
99555
+ renameSync: (a, b) => renameSync23(a, b),
99556
+ existsSync: (p) => existsSync56(p)
99040
99557
  };
99041
99558
  var activityCardPersistEnabled = !STATIC;
99042
- var QUEUED_CARD_STORE_PATH = join61(STATE_DIR, "queued-cards-pending.json");
99559
+ var QUEUED_CARD_STORE_PATH = join62(STATE_DIR, "queued-cards-pending.json");
99043
99560
  var queuedCardStoreFs = {
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)
99561
+ readFileSync: (p) => readFileSync60(p, "utf8"),
99562
+ writeFileSync: (p, d) => writeFileSync50(p, d),
99563
+ renameSync: (a, b) => renameSync23(a, b),
99564
+ existsSync: (p) => existsSync56(p)
99048
99565
  };
99049
99566
  var queuedCardPersistEnabled = !STATIC;
99050
99567
  function persistQueuedCard(key, chatId, threadId, messageId) {
@@ -99417,12 +99934,12 @@ var getPinnedProgressCardMessageId = null;
99417
99934
  var completeProgressCardTurn = null;
99418
99935
  var subagentWatcher = null;
99419
99936
  var workerActivityFeed = null;
99420
- var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join61(STATE_DIR, "gateway.sock");
99937
+ var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join62(STATE_DIR, "gateway.sock");
99421
99938
  if (isGatewayMain)
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");
99939
+ mkdirSync49(STATE_DIR, { recursive: true, mode: 448 });
99940
+ var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join62(STATE_DIR, "gateway.pid.json");
99941
+ var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join62(STATE_DIR, "gateway-session.json");
99942
+ var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join62(STATE_DIR, "clean-shutdown.json");
99426
99943
  var GATEWAY_STARTED_AT_MS = Date.now();
99427
99944
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
99428
99945
  var activeBootCard = null;
@@ -99451,7 +99968,7 @@ function ensureIssuesCard(chatId, threadId) {
99451
99968
  bot: botApi,
99452
99969
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
99453
99970
  `),
99454
- persistPath: join61(stateDir, "issues-card.json")
99971
+ persistPath: join62(stateDir, "issues-card.json")
99455
99972
  });
99456
99973
  activeIssuesWatcher = startIssuesWatcher({
99457
99974
  stateDir,
@@ -99649,13 +100166,13 @@ if (isGatewayMain)
99649
100166
  var inboundSpool;
99650
100167
  if (isGatewayMain)
99651
100168
  inboundSpool = STATIC ? undefined : createInboundSpool({
99652
- path: join61(STATE_DIR, "inbound-spool.jsonl"),
100169
+ path: join62(STATE_DIR, "inbound-spool.jsonl"),
99653
100170
  fs: {
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),
100171
+ appendFileSync: (p, d) => appendFileSync9(p, d),
100172
+ readFileSync: (p) => readFileSync60(p, "utf8"),
100173
+ writeFileSync: (p, d) => writeFileSync50(p, d),
100174
+ renameSync: (a, b) => renameSync23(a, b),
100175
+ existsSync: (p) => existsSync56(p),
99659
100176
  statSizeSync: (p) => statSync20(p).size
99660
100177
  },
99661
100178
  onDegraded: (info) => {
@@ -99717,13 +100234,13 @@ async function maybeRedeliverUndeliveredAnswer() {
99717
100234
  let transcriptText;
99718
100235
  try {
99719
100236
  const projectsDir = getProjectsDirForCwd();
99720
- const path2 = join61(projectsDir, `${sessionId}.jsonl`);
99721
- if (!existsSync55(path2)) {
100237
+ const path2 = join62(projectsDir, `${sessionId}.jsonl`);
100238
+ if (!existsSync56(path2)) {
99722
100239
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
99723
100240
  `);
99724
100241
  return;
99725
100242
  }
99726
- transcriptText = readFileSync59(path2, "utf8");
100243
+ transcriptText = readFileSync60(path2, "utf8");
99727
100244
  } catch (err) {
99728
100245
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
99729
100246
  `);
@@ -99882,8 +100399,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
99882
100399
  isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
99883
100400
  isShuttingDown: () => shuttingDown,
99884
100401
  escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
99885
- crashLogPath: join61(STATE_DIR, "bridge-crash.log"),
99886
- markerPath: join61(STATE_DIR, "bridge-dead-escalation.json"),
100402
+ crashLogPath: join62(STATE_DIR, "bridge-crash.log"),
100403
+ markerPath: join62(STATE_DIR, "bridge-dead-escalation.json"),
99887
100404
  log: (line) => process.stderr.write(`${line}
99888
100405
  `),
99889
100406
  priorStreak: bridgeDeadPriorStreak,
@@ -99989,8 +100506,8 @@ if (isGatewayMain)
99989
100506
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
99990
100507
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
99991
100508
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
99992
- configSnapshotPath: join61(resolvedAgentDirForCard, ".config-snapshot.json"),
99993
- bootCardStatePath: join61(resolvedAgentDirForCard, ".boot-card-msgid.json"),
100509
+ configSnapshotPath: join62(resolvedAgentDirForCard, ".config-snapshot.json"),
100510
+ bootCardStatePath: join62(resolvedAgentDirForCard, ".boot-card-msgid.json"),
99994
100511
  floodStatePath: FLOOD_STATE_PATH,
99995
100512
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
99996
100513
  }, ackMsgId).then((handle) => {
@@ -100672,7 +101189,7 @@ if (isGatewayMain)
100672
101189
  const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
100673
101190
  if (Number.isInteger(receiverUid))
100674
101191
  allowedUids.push(receiverUid);
100675
- const socketPath = join61(STATE_DIR, "webhook.sock");
101192
+ const socketPath = join62(STATE_DIR, "webhook.sock");
100676
101193
  const webhookInject = (agentName3, inbound) => {
100677
101194
  const msg = inbound;
100678
101195
  const delivered = ipcServer.sendToAgent(agentName3, msg);
@@ -100900,9 +101417,9 @@ function redactOutboundText(text5, site) {
100900
101417
  var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
100901
101418
  var VOICE_OUT_HARD_CHUNK_CAP = 4096;
100902
101419
  var voiceOnDemandCache = new VoiceOnDemandCache({
100903
- persistPath: join61(STATE_DIR, "voice-ondemand.json")
101420
+ persistPath: join62(STATE_DIR, "voice-ondemand.json")
100904
101421
  });
100905
- var VOICE_CACHE_DIR = join61(STATE_DIR, "voice-cache");
101422
+ var VOICE_CACHE_DIR = join62(STATE_DIR, "voice-cache");
100906
101423
  var voicePreSynthQueue = new PreSynthQueue({
100907
101424
  runJob: async (job) => {
100908
101425
  const sidecarToken = await materializeSidecarToken2();
@@ -101304,11 +101821,11 @@ async function executeSendGif(rawArgs) {
101304
101821
  };
101305
101822
  }
101306
101823
  async function publishToTelegraph(text5, shortName, authorName) {
101307
- const accountPath = join61(STATE_DIR, "telegraph-account.json");
101824
+ const accountPath = join62(STATE_DIR, "telegraph-account.json");
101308
101825
  let account = null;
101309
101826
  try {
101310
- if (existsSync55(accountPath)) {
101311
- const raw = readFileSync59(accountPath, "utf-8");
101827
+ if (existsSync56(accountPath)) {
101828
+ const raw = readFileSync60(accountPath, "utf-8");
101312
101829
  const parsed = JSON.parse(raw);
101313
101830
  if (parsed.shortName && parsed.accessToken) {
101314
101831
  account = parsed;
@@ -101327,8 +101844,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
101327
101844
  }
101328
101845
  account = created.value;
101329
101846
  try {
101330
- mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
101331
- writeFileSync49(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
101847
+ mkdirSync49(STATE_DIR, { recursive: true, mode: 448 });
101848
+ writeFileSync50(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
101332
101849
  } catch (err) {
101333
101850
  process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
101334
101851
  `);
@@ -101444,7 +101961,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
101444
101961
  }
101445
101962
  function readLiveSwitchroomConfigText() {
101446
101963
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
101447
- return readFileSync59(cfgPath, "utf8");
101964
+ return readFileSync60(cfgPath, "utf8");
101448
101965
  }
101449
101966
  async function executeReact(args) {
101450
101967
  if (!args.chat_id)
@@ -101483,9 +102000,9 @@ async function executeDownloadAttachment(args) {
101483
102000
  fileUniqueId: file.file_unique_id,
101484
102001
  now: Date.now()
101485
102002
  });
101486
- mkdirSync48(INBOX_DIR, { recursive: true, mode: 448 });
102003
+ mkdirSync49(INBOX_DIR, { recursive: true, mode: 448 });
101487
102004
  assertInsideInbox2(INBOX_DIR, dlPath);
101488
- writeFileSync49(dlPath, buf, { mode: 384 });
102005
+ writeFileSync50(dlPath, buf, { mode: 384 });
101489
102006
  return { content: [{ type: "text", text: dlPath }] };
101490
102007
  }
101491
102008
  async function executeEditMessage(args) {
@@ -102845,14 +103362,14 @@ function restartMarkerPath() {
102845
103362
  const agentDir = resolveAgentDirFromEnv();
102846
103363
  if (!agentDir)
102847
103364
  return null;
102848
- return join61(agentDir, "restart-pending.json");
103365
+ return join62(agentDir, "restart-pending.json");
102849
103366
  }
102850
103367
  function writeRestartMarker(marker) {
102851
103368
  const p = restartMarkerPath();
102852
103369
  if (!p)
102853
103370
  return;
102854
103371
  try {
102855
- writeFileSync49(p, JSON.stringify(marker));
103372
+ writeFileSync50(p, JSON.stringify(marker));
102856
103373
  lastPlannedRestartAt = Date.now();
102857
103374
  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}
102858
103375
  `);
@@ -102871,7 +103388,7 @@ function readRestartMarker() {
102871
103388
  if (!p)
102872
103389
  return null;
102873
103390
  try {
102874
- return JSON.parse(readFileSync59(p, "utf8"));
103391
+ return JSON.parse(readFileSync60(p, "utf8"));
102875
103392
  } catch {
102876
103393
  return null;
102877
103394
  }
@@ -103020,7 +103537,7 @@ var _dockerReachable;
103020
103537
  function isDockerReachable() {
103021
103538
  if (_dockerReachable !== undefined)
103022
103539
  return _dockerReachable;
103023
- if (!existsSync55("/var/run/docker.sock")) {
103540
+ if (!existsSync56("/var/run/docker.sock")) {
103024
103541
  _dockerReachable = false;
103025
103542
  return _dockerReachable;
103026
103543
  }
@@ -103037,12 +103554,12 @@ function _resetDockerReachableCache() {
103037
103554
  }
103038
103555
  function spawnSwitchroomDetached(args, onFailure) {
103039
103556
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
103040
- const logPath = join61(STATE_DIR, "detached-spawn.log");
103557
+ const logPath = join62(STATE_DIR, "detached-spawn.log");
103041
103558
  let outFd = null;
103042
103559
  try {
103043
- mkdirSync48(STATE_DIR, { recursive: true });
103560
+ mkdirSync49(STATE_DIR, { recursive: true });
103044
103561
  outFd = openSync12(logPath, "a");
103045
- writeFileSync49(logPath, `
103562
+ writeFileSync50(logPath, `
103046
103563
  [${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
103047
103564
  `, { flag: "a" });
103048
103565
  } catch {}
@@ -103068,7 +103585,7 @@ function spawnSwitchroomDetached(args, onFailure) {
103068
103585
  return;
103069
103586
  let tail = "";
103070
103587
  try {
103071
- const full = readFileSync59(logPath, "utf8");
103588
+ const full = readFileSync60(logPath, "utf8");
103072
103589
  tail = full.split(`
103073
103590
  `).slice(-30).join(`
103074
103591
  `).trim();
@@ -103330,10 +103847,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
103330
103847
  }
103331
103848
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
103332
103849
  try {
103333
- const auditPath = join61(homedir20(), ".switchroom", "vault-audit.log");
103334
- if (!existsSync55(auditPath))
103850
+ const auditPath = join62(homedir20(), ".switchroom", "vault-audit.log");
103851
+ if (!existsSync56(auditPath))
103335
103852
  return [];
103336
- const raw = readFileSync59(auditPath, "utf8");
103853
+ const raw = readFileSync60(auditPath, "utf8");
103337
103854
  return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
103338
103855
  } catch {
103339
103856
  return [];
@@ -103384,7 +103901,7 @@ async function buildAgentMetadata(agentName3) {
103384
103901
  try {
103385
103902
  const agentDir = resolveAgentDirFromEnv();
103386
103903
  if (agentDir) {
103387
- const raw = readFileSync59(join61(agentDir, ".claude", ".claude.json"), "utf8");
103904
+ const raw = readFileSync60(join62(agentDir, ".claude", ".claude.json"), "utf8");
103388
103905
  claudeJson = JSON.parse(raw);
103389
103906
  }
103390
103907
  } catch {}
@@ -103513,7 +104030,7 @@ function buildModelDeps(restartCtx) {
103513
104030
  try {
103514
104031
  const agentDir = resolveAgentDirFromEnv();
103515
104032
  if (agentDir) {
103516
- const local = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
104033
+ const local = await fetchQuota2({ claudeConfigDir: join62(agentDir, ".claude") });
103517
104034
  if (local.ok)
103518
104035
  return formatQuotaLine2(local.data);
103519
104036
  }
@@ -103759,9 +104276,9 @@ function effortMenuReplyMarkup(reply) {
103759
104276
  function flushAgentHandoff(agentDir) {
103760
104277
  let removed = 0;
103761
104278
  for (const fname of [".handoff.md", ".handoff-topic"]) {
103762
- const p = join61(agentDir, fname);
104279
+ const p = join62(agentDir, fname);
103763
104280
  try {
103764
- if (existsSync55(p)) {
104281
+ if (existsSync56(p)) {
103765
104282
  unlinkSync27(p);
103766
104283
  removed++;
103767
104284
  }
@@ -103817,7 +104334,7 @@ async function handleNewCommand(ctx) {
103817
104334
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
103818
104335
  if (agentDir != null) {
103819
104336
  try {
103820
- writeFileSync49(join61(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
104337
+ writeFileSync50(join62(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
103821
104338
  `, "utf8");
103822
104339
  } catch (err) {
103823
104340
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -103930,16 +104447,16 @@ function buildFolderPickerDeps() {
103930
104447
  };
103931
104448
  }
103932
104449
  var lockoutOps = {
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)
104450
+ readFileSync: (p, enc) => readFileSync60(p, enc),
104451
+ writeFileSync: (p, data, opts) => writeFileSync50(p, data, opts),
104452
+ existsSync: (p) => existsSync56(p),
104453
+ mkdirSync: (p, opts) => mkdirSync49(p, opts),
104454
+ joinPath: (...parts) => join62(...parts)
103938
104455
  };
103939
104456
  var FLEET_FALLBACK_DEDUP_MS = 30000;
103940
104457
  function isAuthBrokerSocketReachable() {
103941
104458
  try {
103942
- return existsSync55(resolveAuthBrokerSocketPath2());
104459
+ return existsSync56(resolveAuthBrokerSocketPath2());
103943
104460
  } catch {
103944
104461
  return false;
103945
104462
  }
@@ -104194,7 +104711,7 @@ async function runCreditWatch() {
104194
104711
  if (!agentDir)
104195
104712
  return;
104196
104713
  const agentName3 = getMyAgentName();
104197
- const claudeConfigDir = join61(agentDir, ".claude");
104714
+ const claudeConfigDir = join62(agentDir, ".claude");
104198
104715
  const stateDir = STATE_DIR;
104199
104716
  const reason = readClaudeJsonOverage(claudeConfigDir);
104200
104717
  const prev = loadCreditState(stateDir);
@@ -105950,7 +106467,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
105950
106467
  await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
105951
106468
  return;
105952
106469
  }
105953
- const result = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
106470
+ const result = await fetchQuota2({ claudeConfigDir: join62(agentDir, ".claude") });
105954
106471
  if (!result.ok) {
105955
106472
  await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
105956
106473
  return;
@@ -106336,7 +106853,7 @@ ${interimLabel}` : interimLabel
106336
106853
  const unifiedDiff = (() => {
106337
106854
  try {
106338
106855
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
106339
- const raw = readFileSync59(cfgPath, "utf8");
106856
+ const raw = readFileSync60(cfgPath, "utf8");
106340
106857
  return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
106341
106858
  } catch (err) {
106342
106859
  process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
@@ -106614,6 +107131,7 @@ ${labelWithResume}` : labelWithResume,
106614
107131
  handleChecklistUpdate(ctx, "checklist_tasks_added", checklistHandlerDeps);
106615
107132
  });
106616
107133
  bot2.on("message:pinned_message", (ctx) => handlePinnedMessage(ctx, pinnedMessageHandlerDeps));
107134
+ bot2.on("message:rich_message", (ctx) => handleRichMessageMessage(ctx, mediaEnvelopeDeps));
106617
107135
  installUnhandledMessageCatchAll(bot2, (ctx, text5) => routeInbound(ctx, text5, undefined, undefined, inboundRouterDeps), (line) => process.stderr.write(line));
106618
107136
  bot2.on("message_reaction", (ctx) => {
106619
107137
  handleMessageReaction(ctx);
@@ -107192,7 +107710,7 @@ async function startGateway() {
107192
107710
  return;
107193
107711
  }
107194
107712
  })();
107195
- const resolvedAgentDirForBootCard = agentDir ?? join61(homedir20(), ".switchroom", "agents", agentSlug);
107713
+ const resolvedAgentDirForBootCard = agentDir ?? join62(homedir20(), ".switchroom", "agents", agentSlug);
107196
107714
  const handle = await startBootCard(chatId, threadId, botApiForCard, {
107197
107715
  agentName: agentDisplayName,
107198
107716
  agentSlug,
@@ -107206,8 +107724,8 @@ async function startGateway() {
107206
107724
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
107207
107725
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
107208
107726
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
107209
- configSnapshotPath: join61(resolvedAgentDirForBootCard, ".config-snapshot.json"),
107210
- bootCardStatePath: join61(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
107727
+ configSnapshotPath: join62(resolvedAgentDirForBootCard, ".config-snapshot.json"),
107728
+ bootCardStatePath: join62(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
107211
107729
  floodStatePath: FLOOD_STATE_PATH,
107212
107730
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
107213
107731
  }, ackMsgId);
@@ -107238,10 +107756,10 @@ async function startGateway() {
107238
107756
  try {
107239
107757
  const smAgentDir = resolveAgentDirFromEnv();
107240
107758
  if (smAgentDir) {
107241
- const activePath = join61(smAgentDir, ".active-session-model");
107242
- if (existsSync55(activePath)) {
107759
+ const activePath = join62(smAgentDir, ".active-session-model");
107760
+ if (existsSync56(activePath)) {
107243
107761
  try {
107244
- const launched = readFileSync59(activePath, "utf8").trim();
107762
+ const launched = readFileSync60(activePath, "utf8").trim();
107245
107763
  const configured = (() => {
107246
107764
  const d = switchroomExecJson(["agent", "list"]);
107247
107765
  const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
@@ -107274,24 +107792,24 @@ async function startGateway() {
107274
107792
  deliverModelSwitchBootNotice({
107275
107793
  ...modelBootCardDeps,
107276
107794
  confirmation,
107277
- hasSessionModelAlert: existsSync55(join61(smAgentDir, ".session-model-alert"))
107795
+ hasSessionModelAlert: existsSync56(join62(smAgentDir, ".session-model-alert"))
107278
107796
  });
107279
107797
  }
107280
107798
  } catch {}
107281
107799
  }
107282
- const activeEffortPath = join61(smAgentDir, ".active-session-effort");
107283
- if (existsSync55(activeEffortPath)) {
107800
+ const activeEffortPath = join62(smAgentDir, ".active-session-effort");
107801
+ if (existsSync56(activeEffortPath)) {
107284
107802
  try {
107285
- const launchedEffort = readFileSync59(activeEffortPath, "utf8").trim();
107803
+ const launchedEffort = readFileSync60(activeEffortPath, "utf8").trim();
107286
107804
  const configuredEffort = getConfiguredEffortForPersist();
107287
107805
  sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
107288
107806
  } catch {}
107289
107807
  }
107290
- const alertPath = join61(smAgentDir, ".session-model-alert");
107291
- if (existsSync55(alertPath)) {
107808
+ const alertPath = join62(smAgentDir, ".session-model-alert");
107809
+ if (existsSync56(alertPath)) {
107292
107810
  let alertText = null;
107293
107811
  try {
107294
- alertText = readFileSync59(alertPath, "utf8").trim();
107812
+ alertText = readFileSync60(alertPath, "utf8").trim();
107295
107813
  } catch {
107296
107814
  alertText = null;
107297
107815
  }