switchroom 0.15.41 → 0.15.43

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.
@@ -23960,7 +23960,8 @@ var init_schema = __esm(() => {
23960
23960
  SessionSchema = exports_external.object({
23961
23961
  max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
23962
23962
  max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
23963
- max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
23963
+ max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
23964
+ idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
23964
23965
  }).optional();
23965
23966
  SessionContinuitySchema = exports_external.object({
23966
23967
  enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
@@ -28928,10 +28929,10 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
28928
28929
  }
28929
28930
 
28930
28931
  // gateway/quota-cache.ts
28931
- import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16 } from "fs";
28932
- import { join as join23, dirname as dirname8 } from "path";
28932
+ import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17 } from "fs";
28933
+ import { join as join24, dirname as dirname8 } from "path";
28933
28934
  function defaultCachePath() {
28934
- return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join23(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
28935
+ return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join24(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
28935
28936
  }
28936
28937
  function readQuotaCache(opts = {}) {
28937
28938
  const path = opts.path ?? defaultCachePath();
@@ -28966,8 +28967,8 @@ function writeQuotaCache(result, opts = {}) {
28966
28967
  result
28967
28968
  };
28968
28969
  try {
28969
- mkdirSync16(dirname8(path), { recursive: true });
28970
- writeFileSync15(path, JSON.stringify(entry, null, 2), { mode: 384 });
28970
+ mkdirSync17(dirname8(path), { recursive: true });
28971
+ writeFileSync16(path, JSON.stringify(entry, null, 2), { mode: 384 });
28971
28972
  } catch {}
28972
28973
  }
28973
28974
  var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
@@ -28978,7 +28979,7 @@ var init_quota_cache = __esm(() => {
28978
28979
 
28979
28980
  // gateway/boot-probes.ts
28980
28981
  import { readFileSync as readFileSync25, readdirSync as readdirSync4, existsSync as existsSync27 } from "fs";
28981
- import { join as join24 } from "path";
28982
+ import { join as join25 } from "path";
28982
28983
  import { execFile as execFileCb } from "child_process";
28983
28984
  import { promisify as promisify3 } from "util";
28984
28985
  async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
@@ -29020,8 +29021,8 @@ function mapPlan(billingType, hasExtra) {
29020
29021
  }
29021
29022
  async function probeAccount(agentDir) {
29022
29023
  return withTimeout("Account", (async () => {
29023
- const claudeDir = join24(agentDir, ".claude");
29024
- const claudeJsonPath = join24(claudeDir, ".claude.json");
29024
+ const claudeDir = join25(agentDir, ".claude");
29025
+ const claudeJsonPath = join25(claudeDir, ".claude.json");
29025
29026
  let cfg = {};
29026
29027
  try {
29027
29028
  const raw = readFileSync25(claudeJsonPath, "utf8");
@@ -29042,8 +29043,8 @@ async function probeAccount(agentDir) {
29042
29043
  let tokenStr = "";
29043
29044
  let status = "ok";
29044
29045
  for (const candidate of [
29045
- join24(claudeDir, ".oauth-token.meta.json"),
29046
- join24(claudeDir, "accounts", "default", ".oauth-token.meta.json")
29046
+ join25(claudeDir, ".oauth-token.meta.json"),
29047
+ join25(claudeDir, "accounts", "default", ".oauth-token.meta.json")
29047
29048
  ]) {
29048
29049
  if (existsSync27(candidate)) {
29049
29050
  try {
@@ -29429,9 +29430,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
29429
29430
  let claudeDirForProbe = null;
29430
29431
  for (const candidate of [
29431
29432
  claudeConfigDir,
29432
- join24(claudeConfigDir, "accounts", "default")
29433
+ join25(claudeConfigDir, "accounts", "default")
29433
29434
  ]) {
29434
- if (existsSync27(join24(candidate, ".oauth-token"))) {
29435
+ if (existsSync27(join25(candidate, ".oauth-token"))) {
29435
29436
  claudeDirForProbe = candidate;
29436
29437
  break;
29437
29438
  }
@@ -29666,7 +29667,7 @@ async function probeSkills(agentDir, opts = {}) {
29666
29667
  return withTimeout("Skills", (async () => {
29667
29668
  const fs2 = opts.fs ?? realSkillsFs;
29668
29669
  const max = opts.maxNamesShown ?? 3;
29669
- const skillsDir = join24(agentDir, ".claude", "skills");
29670
+ const skillsDir = join25(agentDir, ".claude", "skills");
29670
29671
  if (!fs2.exists(skillsDir)) {
29671
29672
  return { status: "ok", label: "Skills", detail: "no skills dir" };
29672
29673
  }
@@ -29681,17 +29682,17 @@ async function probeSkills(agentDir, opts = {}) {
29681
29682
  }
29682
29683
  const dangling = [];
29683
29684
  for (const name of entries) {
29684
- const skillPath = join24(skillsDir, name);
29685
+ const skillPath = join25(skillsDir, name);
29685
29686
  if (!fs2.exists(skillPath)) {
29686
29687
  dangling.push(name);
29687
29688
  continue;
29688
29689
  }
29689
- const skillMd = join24(skillPath, "SKILL.md");
29690
+ const skillMd = join25(skillPath, "SKILL.md");
29690
29691
  if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
29691
29692
  continue;
29692
29693
  }
29693
29694
  }
29694
- const overlayDir = opts.overlaySkillsDir ?? join24(agentDir, "skills.d");
29695
+ const overlayDir = opts.overlaySkillsDir ?? join25(agentDir, "skills.d");
29695
29696
  const overlaySlugs = new Set;
29696
29697
  if (fs2.exists(overlayDir)) {
29697
29698
  let overlayEntries = [];
@@ -29733,7 +29734,7 @@ function renderBucketedSkills(switchroom, agent) {
29733
29734
  }
29734
29735
  async function probeConnections(agentDir, opts = {}) {
29735
29736
  return withTimeout("Connections", (async () => {
29736
- const path = join24(agentDir, ".claude", "connection-health.json");
29737
+ const path = join25(agentDir, ".claude", "connection-health.json");
29737
29738
  const read = opts.readFileImpl ?? ((p) => readFileSync25(p, "utf8"));
29738
29739
  let issues = [];
29739
29740
  try {
@@ -29782,7 +29783,7 @@ var init_boot_probes = __esm(() => {
29782
29783
  });
29783
29784
 
29784
29785
  // gateway/boot-issue-cache.ts
29785
- import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, renameSync as renameSync9 } from "fs";
29786
+ import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync18, renameSync as renameSync9 } from "fs";
29786
29787
  import { dirname as dirname9 } from "path";
29787
29788
  function fingerprintProbe(key, r) {
29788
29789
  if (r.status === "ok")
@@ -29909,9 +29910,9 @@ function applyAndSave(path, cache, diff) {
29909
29910
  }
29910
29911
  }
29911
29912
  try {
29912
- mkdirSync17(dirname9(path), { recursive: true });
29913
+ mkdirSync18(dirname9(path), { recursive: true });
29913
29914
  const tmp = `${path}.tmp`;
29914
- writeFileSync16(tmp, JSON.stringify(next), { mode: 384 });
29915
+ writeFileSync17(tmp, JSON.stringify(next), { mode: 384 });
29915
29916
  renameSync9(tmp, path);
29916
29917
  } catch {}
29917
29918
  return next;
@@ -29925,7 +29926,7 @@ var init_boot_issue_cache = __esm(() => {
29925
29926
 
29926
29927
  // gateway/config-snapshot.ts
29927
29928
  import { createHash as createHash2 } from "crypto";
29928
- import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as writeFileSync17, mkdirSync as mkdirSync18, renameSync as renameSync10 } from "fs";
29929
+ import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as writeFileSync18, mkdirSync as mkdirSync19, renameSync as renameSync10 } from "fs";
29929
29930
  import { dirname as dirname10 } from "path";
29930
29931
  function hashStringArray(items) {
29931
29932
  if (!items || items.length === 0)
@@ -30024,16 +30025,16 @@ function loadSnapshot(path, now = Date.now) {
30024
30025
  }
30025
30026
  function persistSnapshot(path, snapshot) {
30026
30027
  try {
30027
- mkdirSync18(dirname10(path), { recursive: true });
30028
+ mkdirSync19(dirname10(path), { recursive: true });
30028
30029
  const tmp = `${path}.tmp`;
30029
- writeFileSync17(tmp, JSON.stringify(snapshot), { mode: 384 });
30030
+ writeFileSync18(tmp, JSON.stringify(snapshot), { mode: 384 });
30030
30031
  renameSync10(tmp, path);
30031
30032
  } catch {}
30032
30033
  }
30033
30034
  var init_config_snapshot = () => {};
30034
30035
 
30035
30036
  // gateway/boot-card-msgid.ts
30036
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync18 } from "node:fs";
30037
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
30037
30038
  function bootCardChatKey(chatId, threadId) {
30038
30039
  return `${chatId}:${threadId ?? ""}`;
30039
30040
  }
@@ -30058,7 +30059,7 @@ function saveBootCardMsgId(path, chatKey3, messageId) {
30058
30059
  if (store2[chatKey3] === messageId)
30059
30060
  return;
30060
30061
  store2[chatKey3] = messageId;
30061
- writeFileSync18(path, JSON.stringify(store2), "utf8");
30062
+ writeFileSync19(path, JSON.stringify(store2), "utf8");
30062
30063
  } catch {}
30063
30064
  }
30064
30065
  var init_boot_card_msgid = () => {};
@@ -30073,7 +30074,7 @@ __export(exports_boot_card, {
30073
30074
  renderBootCard: () => renderBootCard,
30074
30075
  renderAccountRows: () => renderAuthLine
30075
30076
  });
30076
- import { join as join25 } from "path";
30077
+ import { join as join26 } from "path";
30077
30078
  function resolvePersonaName(slug, loadConfig3) {
30078
30079
  try {
30079
30080
  const config = loadConfig3 ? loadConfig3() : loadConfig();
@@ -30163,7 +30164,7 @@ function renderBootCard(opts) {
30163
30164
  `);
30164
30165
  }
30165
30166
  async function runAllProbes(opts) {
30166
- const claudeDir = join25(opts.agentDir, ".claude");
30167
+ const claudeDir = join26(opts.agentDir, ".claude");
30167
30168
  const probes = {};
30168
30169
  const slug = opts.agentSlug ?? opts.agentName;
30169
30170
  await Promise.allSettled([
@@ -30919,7 +30920,7 @@ __export(exports_tmux, {
30919
30920
  captureAgentPane: () => captureAgentPane
30920
30921
  });
30921
30922
  import { execFileSync as execFileSync4 } from "node:child_process";
30922
- import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync24 } from "node:fs";
30923
+ import { mkdirSync as mkdirSync26, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync25 } from "node:fs";
30923
30924
  import { resolve as resolve7 } from "node:path";
30924
30925
  function captureAgentPane(opts) {
30925
30926
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -30931,7 +30932,7 @@ function captureAgentPane(opts) {
30931
30932
  const reasonSlug = sanitizeReason(reason);
30932
30933
  const outPath = resolve7(outDir, `${ts}-${reasonSlug}.txt`);
30933
30934
  try {
30934
- mkdirSync25(outDir, { recursive: true, mode: 493 });
30935
+ mkdirSync26(outDir, { recursive: true, mode: 493 });
30935
30936
  } catch (err) {
30936
30937
  const msg = `mkdir crash-reports failed: ${err.message}`;
30937
30938
  console.error(`[tmux-capture] ${agentName3}: ${msg}`);
@@ -30965,7 +30966,7 @@ function captureAgentPane(opts) {
30965
30966
  ` + `
30966
30967
  `;
30967
30968
  try {
30968
- writeFileSync24(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
30969
+ writeFileSync25(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
30969
30970
  mode: 420
30970
30971
  });
30971
30972
  } catch (err) {
@@ -31560,8 +31561,8 @@ import { randomBytes as randomBytes6 } from "crypto";
31560
31561
  import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
31561
31562
  import {
31562
31563
  readFileSync as readFileSync36,
31563
- writeFileSync as writeFileSync25,
31564
- mkdirSync as mkdirSync26,
31564
+ writeFileSync as writeFileSync26,
31565
+ mkdirSync as mkdirSync27,
31565
31566
  readdirSync as readdirSync7,
31566
31567
  rmSync as rmSync4,
31567
31568
  statSync as statSync13,
@@ -31575,7 +31576,7 @@ import {
31575
31576
  appendFileSync as appendFileSync5
31576
31577
  } from "fs";
31577
31578
  import { homedir as homedir14 } from "os";
31578
- import { join as join35, extname, sep as sep3, basename as basename10 } from "path";
31579
+ import { join as join36, extname, sep as sep3, basename as basename10 } from "path";
31579
31580
 
31580
31581
  // plugin-logger.ts
31581
31582
  import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
@@ -43163,7 +43164,7 @@ function helpText(agentName3) {
43163
43164
  ``,
43164
43165
  `This bot is the <b>${escapeHtml6(agentName3)}</b> agent. Text and photos route through to it; replies, reactions and progress cards come back.`,
43165
43166
  ``,
43166
- `Tool approvals surface as inline buttons (\u2705 / \u274c) or via <code>/approve</code>, <code>/deny</code>, <code>/pending</code>. Start a fresh session with <code>/new</code> or <code>/reset</code>.`,
43167
+ `Tool approvals surface as inline buttons (\u2705 / \u274c) or via <code>/approve</code>, <code>/deny</code>, <code>/pending</code>. Start a fresh session with <code>/new</code>, or trim/clear context with <code>/compact</code> / <code>/clear</code>.`,
43167
43168
  ``,
43168
43169
  `<code>/start</code> \u2014 pairing instructions`,
43169
43170
  `<code>/status</code> \u2014 agent, model, auth`,
@@ -43231,7 +43232,8 @@ var TELEGRAM_MENU_COMMANDS = [
43231
43232
  { command: "help", description: "What this bot can do" },
43232
43233
  { command: "status", description: "Agent, model, auth" },
43233
43234
  { command: "new", description: "Fresh session (flush handoff, restart)" },
43234
- { command: "reset", description: "Alias of /new" },
43235
+ { command: "compact", description: "Compact context (summarize, keep the thread)" },
43236
+ { command: "clear", description: "Clear context (fresh slate; memory in Hindsight)" },
43235
43237
  { command: "approve", description: "Approve pending tool permission" },
43236
43238
  { command: "deny", description: "Deny pending tool permission" },
43237
43239
  { command: "pending", description: "List pending permission prompts" },
@@ -43239,7 +43241,6 @@ var TELEGRAM_MENU_COMMANDS = [
43239
43241
  { command: "restart", description: "Restart the agent (drain by default)" },
43240
43242
  { command: "version", description: "Show versions + running agent health" },
43241
43243
  { command: "logs", description: "Show recent agent logs" },
43242
- { command: "inject", description: "Inject a Claude Code slash command (e.g. /cost)" },
43243
43244
  { command: "model", description: "Show or switch the Claude model" },
43244
43245
  { command: "effort", description: "Show or switch the reasoning effort" },
43245
43246
  { command: "doctor", description: "Health check (deps, services, MCP)" },
@@ -43256,7 +43257,8 @@ function switchroomHelpText(agentName3) {
43256
43257
  ``,
43257
43258
  `<b>Session &amp; approvals</b>`,
43258
43259
  `<code>/new</code> \u2014 fresh session (flush handoff, restart)`,
43259
- `<code>/reset</code> \u2014 alias of /new`,
43260
+ `<code>/compact</code> \u2014 compact context (summarize, keep the thread)`,
43261
+ `<code>/clear</code> \u2014 clear context (fresh slate; memory in Hindsight)`,
43260
43262
  `<code>/approve [id]</code> \u2014 approve pending tool permission`,
43261
43263
  `<code>/deny [id]</code> \u2014 deny pending tool permission`,
43262
43264
  `<code>/pending</code> \u2014 list pending permission prompts`,
@@ -43310,10 +43312,6 @@ function newSessionAckText(agentName3, flushedHandoff) {
43310
43312
  const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
43311
43313
  return `\uD83C\uDD95 Started fresh session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
43312
43314
  }
43313
- function resetSessionAckText(agentName3, flushedHandoff) {
43314
- const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
43315
- return `\uD83D\uDD04 Reset session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
43316
- }
43317
43315
 
43318
43316
  // gateway/auth-status-adapter.ts
43319
43317
  function formatExpiresInRelative(expiresAt, now = Date.now()) {
@@ -46383,6 +46381,38 @@ function numField(obj, key) {
46383
46381
  return 0;
46384
46382
  }
46385
46383
 
46384
+ // gateway/context-occupancy.ts
46385
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync9 } from "node:fs";
46386
+ import { join as join18 } from "node:path";
46387
+ var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
46388
+ var TIGHT_FRACTION = 0.8;
46389
+ function buildContextOccupancy(occupancy, cap, now) {
46390
+ if (!Number.isFinite(occupancy) || occupancy < 0) {
46391
+ return { occupancy: 0, cap: cap ?? null, headroom: null, pct: null, state: "unknown", computedAt: now };
46392
+ }
46393
+ const c = cap != null && cap > 0 ? cap : null;
46394
+ if (c == null) {
46395
+ return { occupancy, cap: null, headroom: null, pct: null, state: "ok", computedAt: now };
46396
+ }
46397
+ const pct = occupancy / c;
46398
+ return {
46399
+ occupancy,
46400
+ cap: c,
46401
+ headroom: c - occupancy,
46402
+ pct,
46403
+ state: pct >= TIGHT_FRACTION ? "tight" : "ok",
46404
+ computedAt: now
46405
+ };
46406
+ }
46407
+ function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
46408
+ try {
46409
+ const path = join18(stateDir, CONTEXT_OCCUPANCY_FILENAME);
46410
+ (deps?.mkdir ?? ((p, o) => mkdirSync12(p, o)))(stateDir, { recursive: true });
46411
+ (deps?.writeFile ?? ((p, d) => writeFileSync9(p, d)))(path, JSON.stringify(snapshot, null, 2) + `
46412
+ `);
46413
+ } catch {}
46414
+ }
46415
+
46386
46416
  // gateway/proactive-compact.ts
46387
46417
  var COMPACT_REARM_FRACTION = 0.6;
46388
46418
  var COMPACT_COOLDOWN_TURNS = 3;
@@ -46409,6 +46439,36 @@ function decideProactiveCompact(state3, occupancy, cap) {
46409
46439
  };
46410
46440
  }
46411
46441
 
46442
+ // gateway/idle-clear.ts
46443
+ var DEFAULT_IDLE_CLEAR_MS = 3 * 60 * 60 * 1000;
46444
+ function decideIdleClear(state3, now) {
46445
+ if (state3.idleClearMs <= 0)
46446
+ return { clear: false };
46447
+ if (state3.turnInFlight)
46448
+ return { clear: false };
46449
+ if (state3.alreadyCleared)
46450
+ return { clear: false };
46451
+ if (now - state3.lastActivityAt < state3.idleClearMs)
46452
+ return { clear: false };
46453
+ return { clear: true };
46454
+ }
46455
+ function idleDurationToMs(raw) {
46456
+ const m = /^(\d+)([smh])$/.exec(raw.trim());
46457
+ if (!m)
46458
+ return null;
46459
+ const n = Number(m[1]);
46460
+ switch (m[2]) {
46461
+ case "s":
46462
+ return n * 1000;
46463
+ case "m":
46464
+ return n * 60000;
46465
+ case "h":
46466
+ return n * 3600000;
46467
+ default:
46468
+ return null;
46469
+ }
46470
+ }
46471
+
46412
46472
  // gateway/compact-notify.ts
46413
46473
  function idleCompactNotifyState() {
46414
46474
  return { phase: "idle", fileAtStart: null };
@@ -46982,13 +47042,13 @@ function startWebhookIngestServer(opts) {
46982
47042
  }
46983
47043
 
46984
47044
  // ../src/web/webhook-gateway-record.ts
46985
- import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync14 } from "fs";
46986
- import { join as join20 } from "path";
47045
+ import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync15 } from "fs";
47046
+ import { join as join21 } from "path";
46987
47047
  import { homedir as homedir10 } from "os";
46988
47048
 
46989
47049
  // ../src/web/webhook-handler.ts
46990
- import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
46991
- import { join as join18 } from "path";
47050
+ import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
47051
+ import { join as join19 } from "path";
46992
47052
  var DEDUP_MAX = 1000;
46993
47053
  var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
46994
47054
  function loadDedupFile(path) {
@@ -47009,7 +47069,7 @@ function saveDedupFile(path, deliveries, now) {
47009
47069
  }
47010
47070
  const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
47011
47071
  const final = Object.fromEntries(sorted);
47012
- writeFileSync9(path, JSON.stringify({ deliveries: final }), {
47072
+ writeFileSync10(path, JSON.stringify({ deliveries: final }), {
47013
47073
  mode: 384
47014
47074
  });
47015
47075
  }
@@ -47017,8 +47077,8 @@ var agentDedupCache = new Map;
47017
47077
  function createFileDedupStore(resolveAgentDir) {
47018
47078
  return {
47019
47079
  check(agent, deliveryId, now) {
47020
- const telegramDir = join18(resolveAgentDir(agent), "telegram");
47021
- const filePath = join18(telegramDir, "webhook-dedup.json");
47080
+ const telegramDir = join19(resolveAgentDir(agent), "telegram");
47081
+ const filePath = join19(telegramDir, "webhook-dedup.json");
47022
47082
  if (!agentDedupCache.has(agent)) {
47023
47083
  agentDedupCache.set(agent, loadDedupFile(filePath));
47024
47084
  }
@@ -47028,7 +47088,7 @@ function createFileDedupStore(resolveAgentDir) {
47028
47088
  }
47029
47089
  deliveries[deliveryId] = now;
47030
47090
  try {
47031
- mkdirSync12(telegramDir, { recursive: true });
47091
+ mkdirSync13(telegramDir, { recursive: true });
47032
47092
  saveDedupFile(filePath, deliveries, now);
47033
47093
  } catch {}
47034
47094
  return;
@@ -47039,8 +47099,8 @@ var tokenBuckets = new Map;
47039
47099
  var throttleIssueWindow = new Map;
47040
47100
 
47041
47101
  // ../src/web/webhook-dispatch.ts
47042
- import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "fs";
47043
- import { join as join19 } from "path";
47102
+ import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
47103
+ import { join as join20 } from "path";
47044
47104
  import { homedir as homedir9 } from "os";
47045
47105
 
47046
47106
  // ../src/agent-scheduler/ipc-client.ts
@@ -47349,7 +47409,7 @@ function loadCooldownFile(path) {
47349
47409
  }
47350
47410
  function saveCooldownFile(path, dispatches) {
47351
47411
  try {
47352
- writeFileSync10(path, JSON.stringify({ dispatches }), {
47412
+ writeFileSync11(path, JSON.stringify({ dispatches }), {
47353
47413
  mode: 384
47354
47414
  });
47355
47415
  } catch {}
@@ -47360,8 +47420,8 @@ function createFileCooldownStore(resolveAgentDir) {
47360
47420
  isCoolingDown(agent, key, cooldownMs, now) {
47361
47421
  if (cooldownMs <= 0)
47362
47422
  return false;
47363
- const telegramDir = join19(resolveAgentDir(agent), "telegram");
47364
- const filePath = join19(telegramDir, "webhook-cooldown.json");
47423
+ const telegramDir = join20(resolveAgentDir(agent), "telegram");
47424
+ const filePath = join20(telegramDir, "webhook-cooldown.json");
47365
47425
  if (!cache.has(agent)) {
47366
47426
  cache.set(agent, loadCooldownFile(filePath));
47367
47427
  }
@@ -47372,7 +47432,7 @@ function createFileCooldownStore(resolveAgentDir) {
47372
47432
  }
47373
47433
  dispatches[key] = now;
47374
47434
  try {
47375
- mkdirSync13(telegramDir, { recursive: true });
47435
+ mkdirSync14(telegramDir, { recursive: true });
47376
47436
  saveCooldownFile(filePath, dispatches);
47377
47437
  } catch {}
47378
47438
  return false;
@@ -47418,9 +47478,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
47418
47478
  }
47419
47479
  function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
47420
47480
  const log = deps.log ?? ((s) => process.stderr.write(s));
47421
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join19(homedir9(), ".switchroom", "agents", a));
47481
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir9(), ".switchroom", "agents", a));
47422
47482
  const now = (deps.now ?? Date.now)();
47423
- const socketPath = join19(resolveAgentDir(agent), "telegram", "gateway.sock");
47483
+ const socketPath = join20(resolveAgentDir(agent), "telegram", "gateway.sock");
47424
47484
  const inbound = {
47425
47485
  type: "inbound",
47426
47486
  chatId: ctx.chatId,
@@ -47492,7 +47552,7 @@ function evaluateDispatch(args, deps = {}) {
47492
47552
  const log = deps.log ?? ((s) => process.stderr.write(s));
47493
47553
  const now = (deps.now ?? Date.now)();
47494
47554
  const nowDate = deps.nowDate ?? (() => new Date(now));
47495
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join19(homedir9(), ".switchroom", "agents", a));
47555
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir9(), ".switchroom", "agents", a));
47496
47556
  const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
47497
47557
  if (!DISPATCH_SOURCES.includes(args.source))
47498
47558
  return 0;
@@ -47568,10 +47628,10 @@ function resolveChannelTarget(config, agentName3) {
47568
47628
  function recordWebhookEvent(rec, deps = {}) {
47569
47629
  const log = deps.log ?? ((s) => process.stderr.write(s));
47570
47630
  const now = rec.ts || (deps.now ?? Date.now)();
47571
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir10(), ".switchroom", "agents", a));
47631
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join21(homedir10(), ".switchroom", "agents", a));
47572
47632
  const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
47573
47633
  const agent = rec.agent;
47574
- const telegramDir = join20(resolveAgentDir(agent), "telegram");
47634
+ const telegramDir = join21(resolveAgentDir(agent), "telegram");
47575
47635
  if (rec.source === "github" && rec.delivery_id) {
47576
47636
  const originalTs = dedupStore.check(agent, rec.delivery_id, now);
47577
47637
  if (originalTs !== undefined) {
@@ -47580,9 +47640,9 @@ function recordWebhookEvent(rec, deps = {}) {
47580
47640
  return { status: "deduped", ts: originalTs };
47581
47641
  }
47582
47642
  }
47583
- const logPath = join20(telegramDir, "webhook-events.jsonl");
47643
+ const logPath = join21(telegramDir, "webhook-events.jsonl");
47584
47644
  try {
47585
- mkdirSync14(telegramDir, { recursive: true });
47645
+ mkdirSync15(telegramDir, { recursive: true });
47586
47646
  const record = {
47587
47647
  ts: now,
47588
47648
  source: rec.source,
@@ -50257,6 +50317,20 @@ function createPollHealthCheck(options) {
50257
50317
  };
50258
50318
  }
50259
50319
 
50320
+ // gateway/poll-stall-recovery.ts
50321
+ var POLL_STALL_EXIT_CODE = 1;
50322
+ function recoverFromPollStall(deps = {}) {
50323
+ const exit = deps.exit ?? ((code) => process.exit(code));
50324
+ const log = deps.log ?? ((msg) => {
50325
+ process.stderr.write(msg.endsWith(`
50326
+ `) ? msg : msg + `
50327
+ `);
50328
+ });
50329
+ const agentName3 = deps.agentName ?? "-";
50330
+ log(`telegram gateway: poll.health_check.stall_recovery exiting code=${POLL_STALL_EXIT_CODE} ` + `pid=${process.pid} agent=${agentName3} \u2014 supervisor will restart the gateway with a fresh runner ` + `(not awaiting runnerHandle.stop(): grammy stop() blocks on a non-abortable getUpdates retry backoff during an outage)`);
50331
+ exit(POLL_STALL_EXIT_CODE);
50332
+ }
50333
+
50260
50334
  // gateway/reaction-trigger.ts
50261
50335
  var REACTIONS_DEFAULTS = Object.freeze({
50262
50336
  enabled: true,
@@ -50481,10 +50555,10 @@ function escapeBody2(s) {
50481
50555
  }
50482
50556
 
50483
50557
  // gateway/pid-file.ts
50484
- import { writeFileSync as writeFileSync11, readFileSync as readFileSync18, unlinkSync as unlinkSync7, renameSync as renameSync6 } from "node:fs";
50558
+ import { writeFileSync as writeFileSync12, readFileSync as readFileSync18, unlinkSync as unlinkSync7, renameSync as renameSync6 } from "node:fs";
50485
50559
  function writePidFile(path, record) {
50486
50560
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
50487
- writeFileSync11(tmp, JSON.stringify(record), "utf-8");
50561
+ writeFileSync12(tmp, JSON.stringify(record), "utf-8");
50488
50562
  renameSync6(tmp, path);
50489
50563
  }
50490
50564
  function clearPidFile(path) {
@@ -50706,10 +50780,10 @@ function safeCount(fn) {
50706
50780
  }
50707
50781
 
50708
50782
  // gateway/session-marker.ts
50709
- import { writeFileSync as writeFileSync12, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
50783
+ import { writeFileSync as writeFileSync13, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
50710
50784
  function writeSessionMarker(path, marker) {
50711
50785
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
50712
- writeFileSync12(tmp, JSON.stringify(marker), "utf-8");
50786
+ writeFileSync13(tmp, JSON.stringify(marker), "utf-8");
50713
50787
  renameSync7(tmp, path);
50714
50788
  }
50715
50789
  function readSessionMarker(path) {
@@ -50736,11 +50810,11 @@ function shouldFireRestartBanner(input) {
50736
50810
  }
50737
50811
 
50738
50812
  // gateway/clean-shutdown-marker.ts
50739
- import { writeFileSync as writeFileSync13, readFileSync as readFileSync21, renameSync as renameSync8, unlinkSync as unlinkSync9 } from "node:fs";
50813
+ import { writeFileSync as writeFileSync14, readFileSync as readFileSync21, renameSync as renameSync8, unlinkSync as unlinkSync9 } from "node:fs";
50740
50814
  var DEFAULT_MAX_AGE_MS = 60000;
50741
50815
  function writeCleanShutdownMarker(path, marker) {
50742
50816
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
50743
- writeFileSync13(tmp, JSON.stringify(marker), "utf-8");
50817
+ writeFileSync14(tmp, JSON.stringify(marker), "utf-8");
50744
50818
  renameSync8(tmp, path);
50745
50819
  }
50746
50820
  function readCleanShutdownMarker(path) {
@@ -51333,7 +51407,7 @@ import {
51333
51407
  readdirSync as readdirSync3,
51334
51408
  readFileSync as readFileSync23
51335
51409
  } from "fs";
51336
- import { join as join22 } from "path";
51410
+ import { join as join23 } from "path";
51337
51411
 
51338
51412
  // operator-events.ts
51339
51413
  function classifyClaudeError(raw) {
@@ -51819,18 +51893,18 @@ function bumpSubagentActivity(db2, args) {
51819
51893
  import {
51820
51894
  closeSync as closeSync3,
51821
51895
  existsSync as existsSync24,
51822
- mkdirSync as mkdirSync15,
51896
+ mkdirSync as mkdirSync16,
51823
51897
  openSync as openSync3,
51824
51898
  readFileSync as readFileSync22,
51825
51899
  statSync as statSync6,
51826
51900
  unlinkSync as unlinkSync10,
51827
51901
  utimesSync,
51828
- writeFileSync as writeFileSync14
51902
+ writeFileSync as writeFileSync15
51829
51903
  } from "node:fs";
51830
- import { join as join21 } from "node:path";
51904
+ import { join as join22 } from "node:path";
51831
51905
  var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
51832
51906
  function touchTurnActiveMarker(stateDir) {
51833
- const path = join21(stateDir, TURN_ACTIVE_MARKER_FILE);
51907
+ const path = join22(stateDir, TURN_ACTIVE_MARKER_FILE);
51834
51908
  if (!existsSync24(path))
51835
51909
  return;
51836
51910
  const now = new Date;
@@ -52357,8 +52431,8 @@ function startSubagentWatcher(config) {
52357
52431
  function rescanSubagentDirs() {
52358
52432
  if (stopped)
52359
52433
  return;
52360
- const claudeHome = join22(agentDir, ".claude");
52361
- const projectsRoot = join22(claudeHome, "projects");
52434
+ const claudeHome = join23(agentDir, ".claude");
52435
+ const projectsRoot = join23(claudeHome, "projects");
52362
52436
  if (!fs2.existsSync(projectsRoot))
52363
52437
  return;
52364
52438
  let projectDirs;
@@ -52375,7 +52449,7 @@ function startSubagentWatcher(config) {
52375
52449
  }
52376
52450
  continue;
52377
52451
  }
52378
- const projectPath = join22(projectsRoot, pDir);
52452
+ const projectPath = join23(projectsRoot, pDir);
52379
52453
  let sessionDirs;
52380
52454
  try {
52381
52455
  sessionDirs = fs2.readdirSync(projectPath);
@@ -52385,7 +52459,7 @@ function startSubagentWatcher(config) {
52385
52459
  for (const sDir of sessionDirs) {
52386
52460
  if (sDir.endsWith(".jsonl"))
52387
52461
  continue;
52388
- const subagentsPath = join22(projectPath, sDir, "subagents");
52462
+ const subagentsPath = join23(projectPath, sDir, "subagents");
52389
52463
  if (!fs2.existsSync(subagentsPath))
52390
52464
  continue;
52391
52465
  if (!dirWatchers.has(subagentsPath)) {
@@ -52393,7 +52467,7 @@ function startSubagentWatcher(config) {
52393
52467
  const w = fs2.watch(subagentsPath, (_event, filename) => {
52394
52468
  if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
52395
52469
  return;
52396
- const filePath = join22(subagentsPath, filename.toString());
52470
+ const filePath = join23(subagentsPath, filename.toString());
52397
52471
  if (!knownFiles.has(filePath)) {
52398
52472
  scanSubagentsDir(subagentsPath);
52399
52473
  }
@@ -52418,7 +52492,7 @@ function startSubagentWatcher(config) {
52418
52492
  for (const e of entries) {
52419
52493
  if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
52420
52494
  continue;
52421
- const filePath = join22(subagentsPath, e);
52495
+ const filePath = join23(subagentsPath, e);
52422
52496
  if (knownFiles.has(filePath))
52423
52497
  continue;
52424
52498
  const agentId = e.slice("agent-".length, -".jsonl".length);
@@ -52544,15 +52618,15 @@ function determineRestartReason(opts) {
52544
52618
  init_boot_card();
52545
52619
 
52546
52620
  // gateway/update-announce.ts
52547
- import { existsSync as existsSync30, mkdirSync as mkdirSync19, openSync as openSync5, closeSync as closeSync5, readFileSync as readFileSync29 } from "node:fs";
52548
- import { join as join27 } from "node:path";
52621
+ import { existsSync as existsSync30, mkdirSync as mkdirSync20, openSync as openSync5, closeSync as closeSync5, readFileSync as readFileSync29 } from "node:fs";
52622
+ import { join as join28 } from "node:path";
52549
52623
  import { homedir as homedir12 } from "node:os";
52550
52624
 
52551
52625
  // ../src/host-control/audit-reader.ts
52552
52626
  import { homedir as homedir11 } from "node:os";
52553
- import { join as join26 } from "node:path";
52627
+ import { join as join27 } from "node:path";
52554
52628
  function defaultAuditLogPath(home2 = homedir11()) {
52555
- return join26(home2, ".switchroom", "host-control-audit.log");
52629
+ return join27(home2, ".switchroom", "host-control-audit.log");
52556
52630
  }
52557
52631
  function parseAuditLine(line) {
52558
52632
  const trimmed = line.trim();
@@ -52720,15 +52794,15 @@ function renderUpdateOutcomeLine(entry) {
52720
52794
  `);
52721
52795
  }
52722
52796
  function claimUpdateAnnouncement(requestId, opts = {}) {
52723
- const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join27(homedir12(), ".switchroom");
52724
- const dir = join27(stateDir, "update-announced");
52797
+ const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join28(homedir12(), ".switchroom");
52798
+ const dir = join28(stateDir, "update-announced");
52725
52799
  try {
52726
- mkdirSync19(dir, { recursive: true });
52800
+ mkdirSync20(dir, { recursive: true });
52727
52801
  } catch {
52728
52802
  return false;
52729
52803
  }
52730
52804
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
52731
- const path = join27(dir, safeId);
52805
+ const path = join28(dir, safeId);
52732
52806
  try {
52733
52807
  const fd = openSync5(path, "wx");
52734
52808
  closeSync5(fd);
@@ -52747,7 +52821,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
52747
52821
  }
52748
52822
 
52749
52823
  // issues-card.ts
52750
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync19 } from "node:fs";
52824
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync20 } from "node:fs";
52751
52825
  var SEVERITY_EMOJI = {
52752
52826
  info: "\u2139\ufe0f",
52753
52827
  warn: "\u26a0\ufe0f",
@@ -52855,7 +52929,7 @@ function readPersistedMessageId(path, log) {
52855
52929
  }
52856
52930
  function writePersistedMessageId(path, messageId, log) {
52857
52931
  try {
52858
- writeFileSync19(path, JSON.stringify({ messageId }) + `
52932
+ writeFileSync20(path, JSON.stringify({ messageId }) + `
52859
52933
  `, { mode: 384 });
52860
52934
  } catch (err) {
52861
52935
  log(`issues-card: persist write failed (${err.message})`);
@@ -52949,23 +53023,23 @@ function createIssuesCardHandle(opts) {
52949
53023
 
52950
53024
  // issues-watcher.ts
52951
53025
  import { existsSync as existsSync32, statSync as statSync9 } from "node:fs";
52952
- import { join as join29 } from "node:path";
53026
+ import { join as join30 } from "node:path";
52953
53027
 
52954
53028
  // ../src/issues/store.ts
52955
53029
  import {
52956
53030
  closeSync as closeSync6,
52957
53031
  existsSync as existsSync31,
52958
- mkdirSync as mkdirSync20,
53032
+ mkdirSync as mkdirSync21,
52959
53033
  openSync as openSync6,
52960
53034
  readdirSync as readdirSync5,
52961
53035
  readFileSync as readFileSync31,
52962
53036
  renameSync as renameSync11,
52963
53037
  statSync as statSync8,
52964
53038
  unlinkSync as unlinkSync11,
52965
- writeFileSync as writeFileSync20,
53039
+ writeFileSync as writeFileSync21,
52966
53040
  writeSync as writeSync2
52967
53041
  } from "node:fs";
52968
- import { join as join28 } from "node:path";
53042
+ import { join as join29 } from "node:path";
52969
53043
  import { randomBytes as randomBytes5 } from "node:crypto";
52970
53044
  import { execSync } from "node:child_process";
52971
53045
 
@@ -52984,7 +53058,7 @@ init_redact();
52984
53058
  var ISSUES_FILE = "issues.jsonl";
52985
53059
  var ISSUES_LOCK = "issues.lock";
52986
53060
  function readAll(stateDir) {
52987
- const path = join28(stateDir, ISSUES_FILE);
53061
+ const path = join29(stateDir, ISSUES_FILE);
52988
53062
  if (!existsSync31(path))
52989
53063
  return [];
52990
53064
  let raw;
@@ -53021,7 +53095,7 @@ function list(stateDir, opts = {}) {
53021
53095
  });
53022
53096
  }
53023
53097
  function resolve6(stateDir, fingerprint, nowFn = Date.now) {
53024
- if (!existsSync31(join28(stateDir, ISSUES_FILE)))
53098
+ if (!existsSync31(join29(stateDir, ISSUES_FILE)))
53025
53099
  return 0;
53026
53100
  return withLock(stateDir, () => {
53027
53101
  const all = readAll(stateDir);
@@ -53039,13 +53113,13 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
53039
53113
  });
53040
53114
  }
53041
53115
  function writeAll(stateDir, events) {
53042
- const path = join28(stateDir, ISSUES_FILE);
53116
+ const path = join29(stateDir, ISSUES_FILE);
53043
53117
  sweepOrphanTmpFiles(stateDir);
53044
53118
  const tmp = `${path}.tmp-${process.pid}-${randomBytes5(4).toString("hex")}`;
53045
53119
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
53046
53120
  `) + `
53047
53121
  `;
53048
- writeFileSync20(tmp, body, "utf-8");
53122
+ writeFileSync21(tmp, body, "utf-8");
53049
53123
  renameSync11(tmp, path);
53050
53124
  }
53051
53125
  var ORPHAN_TMP_TTL_MS = 60000;
@@ -53061,7 +53135,7 @@ function sweepOrphanTmpFiles(stateDir) {
53061
53135
  for (const entry of entries) {
53062
53136
  if (!entry.startsWith(TMP_PREFIX))
53063
53137
  continue;
53064
- const tmpPath2 = join28(stateDir, entry);
53138
+ const tmpPath2 = join29(stateDir, entry);
53065
53139
  try {
53066
53140
  const stat = statSync8(tmpPath2);
53067
53141
  if (stat.mtimeMs < cutoff) {
@@ -53073,7 +53147,7 @@ function sweepOrphanTmpFiles(stateDir) {
53073
53147
  var LOCK_RETRY_MS = 25;
53074
53148
  var LOCK_TIMEOUT_MS = 1e4;
53075
53149
  function withLock(stateDir, fn) {
53076
- const lockPath = join28(stateDir, ISSUES_LOCK);
53150
+ const lockPath = join29(stateDir, ISSUES_LOCK);
53077
53151
  const startedAt = Date.now();
53078
53152
  let fd = null;
53079
53153
  while (fd === null) {
@@ -53158,7 +53232,7 @@ function isIssueEvent(v) {
53158
53232
  // issues-watcher.ts
53159
53233
  var DEFAULT_POLL_INTERVAL_MS2 = 2000;
53160
53234
  function startIssuesWatcher(opts) {
53161
- const path = join29(opts.stateDir, ISSUES_FILE);
53235
+ const path = join30(opts.stateDir, ISSUES_FILE);
53162
53236
  const log = opts.log ?? (() => {});
53163
53237
  const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
53164
53238
  const setIntervalFn = opts.setInterval ?? setInterval;
@@ -54092,8 +54166,8 @@ function extractFlowItems(line) {
54092
54166
  }
54093
54167
 
54094
54168
  // credits-watch.ts
54095
- import { readFileSync as readFileSync32, writeFileSync as writeFileSync21, existsSync as existsSync33, mkdirSync as mkdirSync21 } from "fs";
54096
- import { join as join30 } from "path";
54169
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync22, existsSync as existsSync33, mkdirSync as mkdirSync22 } from "fs";
54170
+ import { join as join31 } from "path";
54097
54171
  var STATE_FILE = "credits-watch.json";
54098
54172
  var DEFAULT_CREDIT_FATAL_REASONS = new Set;
54099
54173
  var KNOWN_CREDIT_REASONS = [
@@ -54115,7 +54189,7 @@ function emptyCreditState() {
54115
54189
  return { lastNotifiedReason: null, lastNotifiedAt: 0 };
54116
54190
  }
54117
54191
  function readClaudeJsonOverage(claudeConfigDir) {
54118
- const path = join30(claudeConfigDir, ".claude.json");
54192
+ const path = join31(claudeConfigDir, ".claude.json");
54119
54193
  if (!existsSync33(path))
54120
54194
  return null;
54121
54195
  let raw;
@@ -54201,7 +54275,7 @@ function escapeHtml9(s) {
54201
54275
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
54202
54276
  }
54203
54277
  function loadCreditState(stateDir) {
54204
- const path = join30(stateDir, STATE_FILE);
54278
+ const path = join31(stateDir, STATE_FILE);
54205
54279
  if (!existsSync33(path))
54206
54280
  return emptyCreditState();
54207
54281
  try {
@@ -54217,15 +54291,15 @@ function loadCreditState(stateDir) {
54217
54291
  return emptyCreditState();
54218
54292
  }
54219
54293
  function saveCreditState(stateDir, state4) {
54220
- mkdirSync21(stateDir, { recursive: true });
54221
- const path = join30(stateDir, STATE_FILE);
54222
- writeFileSync21(path, JSON.stringify(state4, null, 2) + `
54294
+ mkdirSync22(stateDir, { recursive: true });
54295
+ const path = join31(stateDir, STATE_FILE);
54296
+ writeFileSync22(path, JSON.stringify(state4, null, 2) + `
54223
54297
  `, { mode: 384 });
54224
54298
  }
54225
54299
 
54226
54300
  // quota-watch.ts
54227
- import { readFileSync as readFileSync33, writeFileSync as writeFileSync22, existsSync as existsSync34, mkdirSync as mkdirSync22 } from "fs";
54228
- import { join as join31 } from "path";
54301
+ import { readFileSync as readFileSync33, writeFileSync as writeFileSync23, existsSync as existsSync34, mkdirSync as mkdirSync23 } from "fs";
54302
+ import { join as join32 } from "path";
54229
54303
  var STATE_FILE2 = "quota-watch.json";
54230
54304
  function emptyQuotaWatchState() {
54231
54305
  return {};
@@ -54406,7 +54480,7 @@ function escapeHtml10(s) {
54406
54480
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
54407
54481
  }
54408
54482
  function loadQuotaWatchState(stateDir) {
54409
- const path = join31(stateDir, STATE_FILE2);
54483
+ const path = join32(stateDir, STATE_FILE2);
54410
54484
  if (!existsSync34(path))
54411
54485
  return emptyQuotaWatchState();
54412
54486
  try {
@@ -54427,9 +54501,9 @@ function loadQuotaWatchState(stateDir) {
54427
54501
  }
54428
54502
  }
54429
54503
  function saveQuotaWatchState(stateDir, state4) {
54430
- mkdirSync22(stateDir, { recursive: true });
54431
- const path = join31(stateDir, STATE_FILE2);
54432
- writeFileSync22(path, JSON.stringify(state4, null, 2) + `
54504
+ mkdirSync23(stateDir, { recursive: true });
54505
+ const path = join32(stateDir, STATE_FILE2);
54506
+ writeFileSync23(path, JSON.stringify(state4, null, 2) + `
54433
54507
  `, { mode: 384 });
54434
54508
  }
54435
54509
  function patchQuotaWatchState(current, accountLabel, accountState) {
@@ -54443,25 +54517,25 @@ init_auth_snapshot_format();
54443
54517
  import {
54444
54518
  closeSync as closeSync7,
54445
54519
  existsSync as existsSync35,
54446
- mkdirSync as mkdirSync23,
54520
+ mkdirSync as mkdirSync24,
54447
54521
  openSync as openSync7,
54448
54522
  readFileSync as readFileSync34,
54449
54523
  statSync as statSync10,
54450
54524
  unlinkSync as unlinkSync12,
54451
54525
  utimesSync as utimesSync2,
54452
- writeFileSync as writeFileSync23
54526
+ writeFileSync as writeFileSync24
54453
54527
  } from "node:fs";
54454
- import { join as join32 } from "node:path";
54528
+ import { join as join33 } from "node:path";
54455
54529
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
54456
54530
  function writeTurnActiveMarker(stateDir, marker) {
54457
54531
  try {
54458
- mkdirSync23(stateDir, { recursive: true });
54459
- writeFileSync23(join32(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
54532
+ mkdirSync24(stateDir, { recursive: true });
54533
+ writeFileSync24(join33(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
54460
54534
  `, { mode: 384 });
54461
54535
  } catch {}
54462
54536
  }
54463
54537
  function touchTurnActiveMarker2(stateDir) {
54464
- const path = join32(stateDir, TURN_ACTIVE_MARKER_FILE2);
54538
+ const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
54465
54539
  if (!existsSync35(path))
54466
54540
  return;
54467
54541
  const now = new Date;
@@ -54476,11 +54550,11 @@ function touchTurnActiveMarker2(stateDir) {
54476
54550
  }
54477
54551
  function removeTurnActiveMarker(stateDir) {
54478
54552
  try {
54479
- unlinkSync12(join32(stateDir, TURN_ACTIVE_MARKER_FILE2));
54553
+ unlinkSync12(join33(stateDir, TURN_ACTIVE_MARKER_FILE2));
54480
54554
  } catch {}
54481
54555
  }
54482
54556
  function sweepStaleTurnActiveMarker(stateDir, opts) {
54483
- const path = join32(stateDir, TURN_ACTIVE_MARKER_FILE2);
54557
+ const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
54484
54558
  if (!existsSync35(path))
54485
54559
  return false;
54486
54560
  const now = opts.now ?? Date.now();
@@ -54511,7 +54585,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
54511
54585
  }
54512
54586
  }
54513
54587
  function readTurnActiveMarkerAgeMs(stateDir, now) {
54514
- const path = join32(stateDir, TURN_ACTIVE_MARKER_FILE2);
54588
+ const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
54515
54589
  try {
54516
54590
  const st = statSync10(path);
54517
54591
  return (now ?? Date.now()) - st.mtimeMs;
@@ -54521,11 +54595,11 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
54521
54595
  }
54522
54596
 
54523
54597
  // ../src/build-info.ts
54524
- var VERSION = "0.15.41";
54525
- var COMMIT_SHA = "01f41098";
54526
- var COMMIT_DATE = "2026-06-18T14:13:42+10:00";
54598
+ var VERSION = "0.15.43";
54599
+ var COMMIT_SHA = "5480a4c3";
54600
+ var COMMIT_DATE = "2026-06-19T12:01:56+10:00";
54527
54601
  var LATEST_PR = null;
54528
- var COMMITS_AHEAD_OF_TAG = 4;
54602
+ var COMMITS_AHEAD_OF_TAG = 2;
54529
54603
 
54530
54604
  // gateway/boot-version.ts
54531
54605
  function formatRelativeAgo(iso) {
@@ -54598,11 +54672,11 @@ init_peercred();
54598
54672
  import * as net5 from "node:net";
54599
54673
  import * as fs2 from "node:fs";
54600
54674
  import { homedir as homedir13 } from "node:os";
54601
- import { join as join33 } from "node:path";
54675
+ import { join as join34 } from "node:path";
54602
54676
  var DEFAULT_TIMEOUT_MS4 = 2000;
54603
54677
  var UNLOCK_TIMEOUT_MS = 30000;
54604
- var LEGACY_SOCKET_PATH2 = join33(homedir13(), ".switchroom", "vault-broker.sock");
54605
- var OPERATOR_SOCKET_PATH2 = join33(homedir13(), ".switchroom", "broker-operator", "sock");
54678
+ var LEGACY_SOCKET_PATH2 = join34(homedir13(), ".switchroom", "vault-broker.sock");
54679
+ var OPERATOR_SOCKET_PATH2 = join34(homedir13(), ".switchroom", "broker-operator", "sock");
54606
54680
  function defaultBrokerSocketPath2() {
54607
54681
  if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
54608
54682
  return OPERATOR_SOCKET_PATH2;
@@ -55535,8 +55609,8 @@ function matchesAdminOnlyKey(key, patterns) {
55535
55609
  }
55536
55610
 
55537
55611
  // registry/turns-schema.ts
55538
- import { chmodSync as chmodSync5, mkdirSync as mkdirSync24 } from "fs";
55539
- import { join as join34 } from "path";
55612
+ import { chmodSync as chmodSync5, mkdirSync as mkdirSync25 } from "fs";
55613
+ import { join as join35 } from "path";
55540
55614
  var DatabaseClass2 = null;
55541
55615
  function loadDatabaseClass2() {
55542
55616
  if (DatabaseClass2 != null)
@@ -55599,9 +55673,9 @@ function applySchema(db2) {
55599
55673
  }
55600
55674
  function openTurnsDb(agentDir) {
55601
55675
  const Database = loadDatabaseClass2();
55602
- const dir = join34(agentDir, "telegram");
55603
- mkdirSync24(dir, { recursive: true, mode: 448 });
55604
- const path = join34(dir, "registry.db");
55676
+ const dir = join35(agentDir, "telegram");
55677
+ mkdirSync25(dir, { recursive: true, mode: 448 });
55678
+ const path = join35(dir, "registry.db");
55605
55679
  const db2 = new Database(path, { create: true });
55606
55680
  applySchema(db2);
55607
55681
  try {
@@ -55917,11 +55991,11 @@ installGlobalErrorHandlers();
55917
55991
  process.on("beforeExit", () => {
55918
55992
  shutdownAnalytics();
55919
55993
  });
55920
- var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join35(homedir14(), ".claude", "channels", "telegram");
55921
- var ACCESS_FILE = join35(STATE_DIR, "access.json");
55922
- var APPROVED_DIR = join35(STATE_DIR, "approved");
55923
- var ENV_FILE = join35(STATE_DIR, ".env");
55924
- var INBOX_DIR = join35(STATE_DIR, "inbox");
55994
+ var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join36(homedir14(), ".claude", "channels", "telegram");
55995
+ var ACCESS_FILE = join36(STATE_DIR, "access.json");
55996
+ var APPROVED_DIR = join36(STATE_DIR, "approved");
55997
+ var ENV_FILE = join36(STATE_DIR, ".env");
55998
+ var INBOX_DIR = join36(STATE_DIR, "inbox");
55925
55999
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
55926
56000
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
55927
56001
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -56113,7 +56187,7 @@ function assertSendable(f) {
56113
56187
  } catch {
56114
56188
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
56115
56189
  }
56116
- const inbox = join35(stateReal, "inbox");
56190
+ const inbox = join36(stateReal, "inbox");
56117
56191
  if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
56118
56192
  throw new Error(`refusing to send channel state: ${f}`);
56119
56193
  }
@@ -56200,9 +56274,9 @@ function assertAllowedChat(chat_id) {
56200
56274
  function saveAccess(a) {
56201
56275
  if (STATIC)
56202
56276
  return;
56203
- mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
56277
+ mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
56204
56278
  const tmp = ACCESS_FILE + ".tmp";
56205
- writeFileSync25(tmp, JSON.stringify(a, null, 2) + `
56279
+ writeFileSync26(tmp, JSON.stringify(a, null, 2) + `
56206
56280
  `, { mode: 384 });
56207
56281
  renameSync12(tmp, ACCESS_FILE);
56208
56282
  }
@@ -56222,7 +56296,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
56222
56296
  if (HISTORY_ENABLED) {
56223
56297
  try {
56224
56298
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
56225
- process.stderr.write(`telegram gateway: history capture enabled at ${join35(STATE_DIR, "history.db")}
56299
+ process.stderr.write(`telegram gateway: history capture enabled at ${join36(STATE_DIR, "history.db")}
56226
56300
  `);
56227
56301
  } catch (err) {
56228
56302
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -56238,7 +56312,7 @@ try {
56238
56312
  let markerTurnKey = null;
56239
56313
  let markerAgeMs = null;
56240
56314
  try {
56241
- const markerPath = join35(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
56315
+ const markerPath = join36(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
56242
56316
  if (existsSync38(markerPath)) {
56243
56317
  const st = statSync13(markerPath);
56244
56318
  markerAgeMs = Date.now() - st.mtimeMs;
@@ -56263,7 +56337,7 @@ try {
56263
56337
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
56264
56338
  `);
56265
56339
  } else {
56266
- process.stderr.write(`telegram gateway: turn-registry initialized at ${join35(agentDir, "telegram", "registry.db")}
56340
+ process.stderr.write(`telegram gateway: turn-registry initialized at ${join36(agentDir, "telegram", "registry.db")}
56267
56341
  `);
56268
56342
  }
56269
56343
  const pending2 = findLatestTurnIfInterrupted(turnsDb);
@@ -56300,7 +56374,7 @@ try {
56300
56374
  `);
56301
56375
  }
56302
56376
  }
56303
- const pendingEnvPath = join35(agentDir, ".pending-turn.env");
56377
+ const pendingEnvPath = join36(agentDir, ".pending-turn.env");
56304
56378
  try {
56305
56379
  if (pending2 != null) {
56306
56380
  const lines = [
@@ -56314,7 +56388,7 @@ try {
56314
56388
  pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
56315
56389
  ];
56316
56390
  const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
56317
- writeFileSync25(pendingEnvTmp, lines.join(`
56391
+ writeFileSync26(pendingEnvTmp, lines.join(`
56318
56392
  `) + `
56319
56393
  `, { mode: 384 });
56320
56394
  renameSync12(pendingEnvTmp, pendingEnvPath);
@@ -56393,7 +56467,7 @@ function checkApprovals() {
56393
56467
  return;
56394
56468
  }
56395
56469
  for (const senderId of files) {
56396
- const file = join35(APPROVED_DIR, senderId);
56470
+ const file = join36(APPROVED_DIR, senderId);
56397
56471
  bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
56398
56472
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
56399
56473
  `);
@@ -56403,6 +56477,9 @@ function checkApprovals() {
56403
56477
  }
56404
56478
  if (!STATIC)
56405
56479
  setInterval(checkApprovals, 5000).unref();
56480
+ var IDLE_CLEAR_CHECK_MS = Number(process.env.SWITCHROOM_IDLE_CLEAR_CHECK_MS ?? 60000);
56481
+ if (!STATIC && IDLE_CLEAR_CHECK_MS > 0)
56482
+ setInterval(maybeIdleClear, IDLE_CLEAR_CHECK_MS).unref();
56406
56483
  var chatThreadMap = new Map;
56407
56484
  var activeStatusReactions = new Map;
56408
56485
  var activeReactionMsgIds = new Map;
@@ -56473,10 +56550,10 @@ function noteAgentOutputAt(key, ts) {
56473
56550
  lastAgentOutputAt.delete(oldest);
56474
56551
  }
56475
56552
  }
56476
- var OBLIGATION_STORE_PATH = join35(STATE_DIR, "obligations.json");
56553
+ var OBLIGATION_STORE_PATH = join36(STATE_DIR, "obligations.json");
56477
56554
  var obligationStoreFs = {
56478
56555
  readFileSync: (p) => readFileSync36(p, "utf8"),
56479
- writeFileSync: (p, d) => writeFileSync25(p, d),
56556
+ writeFileSync: (p, d) => writeFileSync26(p, d),
56480
56557
  renameSync: (a, b) => renameSync12(a, b),
56481
56558
  existsSync: (p) => existsSync38(p)
56482
56559
  };
@@ -56864,8 +56941,33 @@ function purgeReactionTracking(key, endingTurn) {
56864
56941
  } else {
56865
56942
  maybeProactiveCompact();
56866
56943
  }
56944
+ snapshotContextOccupancy();
56867
56945
  }
56868
56946
  }
56947
+ function snapshotContextOccupancy() {
56948
+ try {
56949
+ const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
56950
+ const file = lastSessionActiveFile;
56951
+ if (!agentName3 || !file)
56952
+ return;
56953
+ const turns = readTurnUsages(file, 1);
56954
+ if (turns.length === 0)
56955
+ return;
56956
+ const t = turns[0];
56957
+ const occupancy = t.input + t.cacheRead + t.cacheCreate;
56958
+ let cap = null;
56959
+ try {
56960
+ const cfg = loadConfig2();
56961
+ const rawAgent = cfg.agents?.[agentName3] ?? {};
56962
+ const resolved = resolveAgentConfig2(cfg.defaults, cfg.profiles, rawAgent);
56963
+ cap = resolved.session?.max_context_tokens ?? null;
56964
+ } catch {
56965
+ cap = null;
56966
+ }
56967
+ const stateDir = process.env.SWITCHROOM_AGENT_STATE_DIR ?? "/state/agent";
56968
+ writeContextOccupancySnapshot(stateDir, buildContextOccupancy(occupancy, cap, Date.now()));
56969
+ } catch {}
56970
+ }
56869
56971
  function releaseTurnBufferGate(key, endingTurn) {
56870
56972
  if (!activeTurnStartedAt.has(key))
56871
56973
  return;
@@ -56957,6 +57059,81 @@ function maybeProactiveCompact() {
56957
57059
  compactDispatching = false;
56958
57060
  });
56959
57061
  }
57062
+ var lastIdleActivityAt = Date.now();
57063
+ var idleAutoCleared = false;
57064
+ var idleClearDispatching = false;
57065
+ function markIdleActivity() {
57066
+ lastIdleActivityAt = Date.now();
57067
+ idleAutoCleared = false;
57068
+ }
57069
+ function resolveIdleClearMs() {
57070
+ const env = process.env.SWITCHROOM_IDLE_CLEAR_MS;
57071
+ if (env != null && env !== "") {
57072
+ const n = Number(env);
57073
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_CLEAR_MS;
57074
+ }
57075
+ try {
57076
+ const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
57077
+ if (!agentName3)
57078
+ return DEFAULT_IDLE_CLEAR_MS;
57079
+ const cfg = loadConfig2();
57080
+ const rawAgent = cfg.agents?.[agentName3] ?? {};
57081
+ const resolved = resolveAgentConfig2(cfg.defaults, cfg.profiles, rawAgent);
57082
+ const raw = resolved.session?.idle_clear_after;
57083
+ if (raw == null)
57084
+ return DEFAULT_IDLE_CLEAR_MS;
57085
+ const ms = idleDurationToMs(raw);
57086
+ return ms == null ? DEFAULT_IDLE_CLEAR_MS : ms;
57087
+ } catch {
57088
+ return DEFAULT_IDLE_CLEAR_MS;
57089
+ }
57090
+ }
57091
+ function maybeIdleClear() {
57092
+ if (idleClearDispatching)
57093
+ return;
57094
+ const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
57095
+ if (!agentName3)
57096
+ return;
57097
+ const idleClearMs = resolveIdleClearMs();
57098
+ const decision = decideIdleClear({
57099
+ lastActivityAt: lastIdleActivityAt,
57100
+ idleClearMs,
57101
+ alreadyCleared: idleAutoCleared,
57102
+ turnInFlight: turnInFlightForGate()
57103
+ }, Date.now());
57104
+ if (!decision.clear)
57105
+ return;
57106
+ idleAutoCleared = true;
57107
+ idleClearDispatching = true;
57108
+ process.stderr.write(`telegram gateway: idle auto-/clear for ${agentName3} (idle >= ${Math.round(idleClearMs / 60000)}m)
57109
+ `);
57110
+ injectSlashCommand(agentName3, "/clear").then(() => {
57111
+ postIdleClearNotice(idleClearMs);
57112
+ }).catch((err) => {
57113
+ process.stderr.write(`telegram gateway: idle /clear inject failed for ${agentName3}: ${err instanceof Error ? err.message : String(err)}
57114
+ `);
57115
+ }).finally(() => {
57116
+ idleClearDispatching = false;
57117
+ });
57118
+ }
57119
+ async function postIdleClearNotice(idleClearMs) {
57120
+ try {
57121
+ const chatId = loadAccess().allowFrom[0];
57122
+ if (!chatId)
57123
+ return;
57124
+ const threadId = topicForRecipient({
57125
+ recipientChatId: chatId,
57126
+ resolvedTopic: resolveAgentOutboundTopic({ kind: "compact-watchdog" }) ?? chatThreadMap.get(chatId),
57127
+ supergroupChatId: resolveAgentSupergroupChatId()
57128
+ });
57129
+ const hrs = Math.round(idleClearMs / 3600000 * 10) / 10;
57130
+ const text2 = `\uD83E\uDDF9 <b>Cleared after ${hrs}h idle</b> \u2014 fresh slate next message; ` + `long-term memory is in Hindsight.`;
57131
+ await swallowingApiCall(() => bot.api.sendMessage(chatId, text2, {
57132
+ parse_mode: "HTML",
57133
+ ...threadId != null ? { message_thread_id: threadId } : {}
57134
+ }), { chat_id: chatId, verb: "idleAutoClear.notice" });
57135
+ } catch {}
57136
+ }
56960
57137
  async function postCompactCard(occ, cap) {
56961
57138
  try {
56962
57139
  const chatId = loadAccess().allowFrom[0];
@@ -57760,11 +57937,11 @@ var unpinProgressCardForChat = null;
57760
57937
  var getPinnedProgressCardMessageId = null;
57761
57938
  var completeProgressCardTurn = null;
57762
57939
  var subagentWatcher = null;
57763
- var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join35(STATE_DIR, "gateway.sock");
57764
- mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
57765
- var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join35(STATE_DIR, "gateway.pid.json");
57766
- var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join35(STATE_DIR, "gateway-session.json");
57767
- var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join35(STATE_DIR, "clean-shutdown.json");
57940
+ var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join36(STATE_DIR, "gateway.sock");
57941
+ mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
57942
+ var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join36(STATE_DIR, "gateway.pid.json");
57943
+ var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join36(STATE_DIR, "gateway-session.json");
57944
+ var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join36(STATE_DIR, "clean-shutdown.json");
57768
57945
  var GATEWAY_STARTED_AT_MS = Date.now();
57769
57946
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
57770
57947
  var activeBootCard = null;
@@ -57793,7 +57970,7 @@ function ensureIssuesCard(chatId, threadId) {
57793
57970
  bot: botApi,
57794
57971
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
57795
57972
  `),
57796
- persistPath: join35(stateDir, "issues-card.json")
57973
+ persistPath: join36(stateDir, "issues-card.json")
57797
57974
  });
57798
57975
  activeIssuesWatcher = startIssuesWatcher({
57799
57976
  stateDir,
@@ -58022,11 +58199,11 @@ startTimer2({
58022
58199
  }
58023
58200
  });
58024
58201
  var inboundSpool = STATIC ? undefined : createInboundSpool({
58025
- path: join35(STATE_DIR, "inbound-spool.jsonl"),
58202
+ path: join36(STATE_DIR, "inbound-spool.jsonl"),
58026
58203
  fs: {
58027
58204
  appendFileSync: (p, d) => appendFileSync5(p, d),
58028
58205
  readFileSync: (p) => readFileSync36(p, "utf8"),
58029
- writeFileSync: (p, d) => writeFileSync25(p, d),
58206
+ writeFileSync: (p, d) => writeFileSync26(p, d),
58030
58207
  renameSync: (a, b) => renameSync12(a, b),
58031
58208
  existsSync: (p) => existsSync38(p),
58032
58209
  statSizeSync: (p) => statSync13(p).size
@@ -58242,8 +58419,8 @@ var ipcServer = createIpcServer({
58242
58419
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
58243
58420
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
58244
58421
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
58245
- configSnapshotPath: join35(resolvedAgentDirForCard, ".config-snapshot.json"),
58246
- bootCardStatePath: join35(resolvedAgentDirForCard, ".boot-card-msgid.json"),
58422
+ configSnapshotPath: join36(resolvedAgentDirForCard, ".config-snapshot.json"),
58423
+ bootCardStatePath: join36(resolvedAgentDirForCard, ".boot-card-msgid.json"),
58247
58424
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
58248
58425
  }, ackMsgId).then((handle) => {
58249
58426
  activeBootCard = handle;
@@ -58667,6 +58844,7 @@ var ipcServer = createIpcServer({
58667
58844
  });
58668
58845
  },
58669
58846
  onInjectInbound(_client, msg) {
58847
+ markIdleActivity();
58670
58848
  const promptKey = typeof msg.inbound.meta?.prompt_key === "string" ? msg.inbound.meta.prompt_key : "unknown";
58671
58849
  const source = typeof msg.inbound.meta?.source === "string" ? msg.inbound.meta.source : "unknown";
58672
58850
  const { target, delivered, fellBackToMain } = deliverInjectWithFallback(msg.agentName, msg.inbound.meta, (t) => ipcServer.sendToAgent(t, msg.inbound));
@@ -58739,7 +58917,7 @@ var ipcServer = createIpcServer({
58739
58917
  const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
58740
58918
  if (Number.isInteger(receiverUid))
58741
58919
  allowedUids.push(receiverUid);
58742
- const socketPath = join35(STATE_DIR, "webhook.sock");
58920
+ const socketPath = join36(STATE_DIR, "webhook.sock");
58743
58921
  const webhookInject = (agentName3, inbound) => {
58744
58922
  const msg = inbound;
58745
58923
  const delivered = ipcServer.sendToAgent(agentName3, msg);
@@ -59850,7 +60028,7 @@ async function executeSendGif(rawArgs) {
59850
60028
  };
59851
60029
  }
59852
60030
  async function publishToTelegraph(text2, shortName, authorName) {
59853
- const accountPath = join35(STATE_DIR, "telegraph-account.json");
60031
+ const accountPath = join36(STATE_DIR, "telegraph-account.json");
59854
60032
  let account = null;
59855
60033
  try {
59856
60034
  if (existsSync38(accountPath)) {
@@ -59873,8 +60051,8 @@ async function publishToTelegraph(text2, shortName, authorName) {
59873
60051
  }
59874
60052
  account = created.value;
59875
60053
  try {
59876
- mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
59877
- writeFileSync25(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
60054
+ mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
60055
+ writeFileSync26(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
59878
60056
  } catch (err) {
59879
60057
  process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
59880
60058
  `);
@@ -60334,9 +60512,9 @@ async function executeDownloadAttachment(args) {
60334
60512
  fileUniqueId: file.file_unique_id,
60335
60513
  now: Date.now()
60336
60514
  });
60337
- mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
60515
+ mkdirSync27(INBOX_DIR, { recursive: true, mode: 448 });
60338
60516
  assertInsideInbox(INBOX_DIR, dlPath);
60339
- writeFileSync25(dlPath, buf, { mode: 384 });
60517
+ writeFileSync26(dlPath, buf, { mode: 384 });
60340
60518
  return { content: [{ type: "text", text: dlPath }] };
60341
60519
  }
60342
60520
  async function executeEditMessage(args) {
@@ -60711,6 +60889,7 @@ function handleSessionEvent(ev) {
60711
60889
  isDm: isDmChatId(ev.chatId)
60712
60890
  };
60713
60891
  currentTurn = next;
60892
+ markIdleActivity();
60714
60893
  process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
60715
60894
  `);
60716
60895
  rememberRecentTurn(next);
@@ -61497,6 +61676,7 @@ function maybeEarlyAckReaction(ctx, from) {
61497
61676
  bot.api.sendChatAction(chatId, "typing").catch(() => {});
61498
61677
  }
61499
61678
  async function handleInbound(ctx, text2, downloadImage, attachment, extraAttachments) {
61679
+ markIdleActivity();
61500
61680
  const isTopicMessage = ctx.message?.is_topic_message ?? false;
61501
61681
  const messageThreadId = ctx.message?.message_thread_id;
61502
61682
  if (TOPIC_ID != null) {
@@ -62321,14 +62501,14 @@ function restartMarkerPath() {
62321
62501
  const agentDir = resolveAgentDirFromEnv();
62322
62502
  if (!agentDir)
62323
62503
  return null;
62324
- return join35(agentDir, "restart-pending.json");
62504
+ return join36(agentDir, "restart-pending.json");
62325
62505
  }
62326
62506
  function writeRestartMarker(marker) {
62327
62507
  const p = restartMarkerPath();
62328
62508
  if (!p)
62329
62509
  return;
62330
62510
  try {
62331
- writeFileSync25(p, JSON.stringify(marker));
62511
+ writeFileSync26(p, JSON.stringify(marker));
62332
62512
  lastPlannedRestartAt = Date.now();
62333
62513
  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}
62334
62514
  `);
@@ -62513,12 +62693,12 @@ function _resetDockerReachableCache() {
62513
62693
  }
62514
62694
  function spawnSwitchroomDetached(args, onFailure) {
62515
62695
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
62516
- const logPath = join35(STATE_DIR, "detached-spawn.log");
62696
+ const logPath = join36(STATE_DIR, "detached-spawn.log");
62517
62697
  let outFd = null;
62518
62698
  try {
62519
- mkdirSync26(STATE_DIR, { recursive: true });
62699
+ mkdirSync27(STATE_DIR, { recursive: true });
62520
62700
  outFd = openSync8(logPath, "a");
62521
- writeFileSync25(logPath, `
62701
+ writeFileSync26(logPath, `
62522
62702
  [${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
62523
62703
  `, { flag: "a" });
62524
62704
  } catch {}
@@ -62906,7 +63086,7 @@ bot.use(async (ctx, next) => {
62906
63086
  });
62907
63087
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
62908
63088
  try {
62909
- const auditPath = join35(homedir14(), ".switchroom", "vault-audit.log");
63089
+ const auditPath = join36(homedir14(), ".switchroom", "vault-audit.log");
62910
63090
  if (!existsSync38(auditPath))
62911
63091
  return [];
62912
63092
  const raw = readFileSync36(auditPath, "utf8");
@@ -62961,7 +63141,7 @@ async function buildAgentMetadata(agentName3) {
62961
63141
  try {
62962
63142
  const agentDir = resolveAgentDirFromEnv();
62963
63143
  if (agentDir) {
62964
- const raw = readFileSync36(join35(agentDir, ".claude", ".claude.json"), "utf8");
63144
+ const raw = readFileSync36(join36(agentDir, ".claude", ".claude.json"), "utf8");
62965
63145
  claudeJson = JSON.parse(raw);
62966
63146
  }
62967
63147
  } catch {}
@@ -63076,17 +63256,26 @@ bot.command("agents", async (ctx) => {
63076
63256
  `);
63077
63257
  });
63078
63258
  });
63079
- bot.command("inject", async (ctx) => {
63080
- await handleInjectCommand(ctx, {
63081
- isAuthorized: isAuthorizedSender,
63259
+ function buildInjectDeps(opts) {
63260
+ return {
63261
+ isAuthorized: opts?.open ? () => true : isAuthorizedSender,
63082
63262
  inject: injectSlashCommand,
63083
- reply: async (ctx2, text2, opts) => switchroomReply(ctx2, text2, { html: opts?.html }),
63263
+ reply: async (ctx, text2, replyOpts) => switchroomReply(ctx, text2, { html: replyOpts?.html }),
63084
63264
  getAgentName: getMyAgentName,
63085
- getArgs: getCommandArgs,
63265
+ getArgs: opts?.fixedVerb != null ? () => opts.fixedVerb : getCommandArgs,
63086
63266
  escapeHtml: escapeHtmlForTg,
63087
63267
  preBlock,
63088
63268
  formatOutput: formatSwitchroomOutput
63089
- });
63269
+ };
63270
+ }
63271
+ bot.command("inject", async (ctx) => {
63272
+ await handleInjectCommand(ctx, buildInjectDeps());
63273
+ });
63274
+ bot.command("compact", async (ctx) => {
63275
+ await handleInjectCommand(ctx, buildInjectDeps({ open: true, fixedVerb: "/compact" }));
63276
+ });
63277
+ bot.command("clear", async (ctx) => {
63278
+ await handleInjectCommand(ctx, buildInjectDeps({ open: true, fixedVerb: "/clear" }));
63090
63279
  });
63091
63280
  function buildModelDeps() {
63092
63281
  return {
@@ -63103,7 +63292,7 @@ function buildModelDeps() {
63103
63292
  try {
63104
63293
  const agentDir = resolveAgentDirFromEnv();
63105
63294
  if (agentDir) {
63106
- const local = await fetchQuota2({ claudeConfigDir: join35(agentDir, ".claude") });
63295
+ const local = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
63107
63296
  if (local.ok)
63108
63297
  return formatQuotaLine2(local.data);
63109
63298
  }
@@ -63269,7 +63458,7 @@ bot.command("restart", async (ctx) => {
63269
63458
  function flushAgentHandoff(agentDir) {
63270
63459
  let removed = 0;
63271
63460
  for (const fname of [".handoff.md", ".handoff-topic"]) {
63272
- const p = join35(agentDir, fname);
63461
+ const p = join36(agentDir, fname);
63273
63462
  try {
63274
63463
  if (existsSync38(p)) {
63275
63464
  unlinkSync14(p);
@@ -63282,7 +63471,8 @@ function flushAgentHandoff(agentDir) {
63282
63471
  }
63283
63472
  return removed;
63284
63473
  }
63285
- async function handleNewOrResetCommand(ctx, kind) {
63474
+ async function handleNewCommand(ctx) {
63475
+ const kind = "new";
63286
63476
  if (!isAuthorizedSender(ctx))
63287
63477
  return;
63288
63478
  const name = (typeof ctx.match === "string" ? ctx.match : "").trim() || getMyAgentName();
@@ -63309,7 +63499,7 @@ async function handleNewOrResetCommand(ctx, kind) {
63309
63499
  const flushed = agentDir != null ? flushAgentHandoff(agentDir) : 0;
63310
63500
  const chatId = String(ctx.chat.id);
63311
63501
  const threadId = resolveThreadId(chatId, ctx.message?.message_thread_id);
63312
- const ackText = kind === "new" ? newSessionAckText(name, flushed > 0) : resetSessionAckText(name, flushed > 0);
63502
+ const ackText = newSessionAckText(name, flushed > 0);
63313
63503
  let ackId = null;
63314
63504
  try {
63315
63505
  const sent = await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendMessage(chatId, ackText, {
@@ -63327,7 +63517,7 @@ async function handleNewOrResetCommand(ctx, kind) {
63327
63517
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
63328
63518
  if (agentDir != null) {
63329
63519
  try {
63330
- writeFileSync25(join35(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
63520
+ writeFileSync26(join36(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
63331
63521
  `, "utf8");
63332
63522
  } catch (err) {
63333
63523
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -63354,8 +63544,7 @@ async function handleNewOrResetCommand(ctx, kind) {
63354
63544
  await switchroomReply(ctx, `\u274C <b>${escapeHtmlForTg(kind)} ${escapeHtmlForTg(name)} failed via hostd</b> (result=${escapeHtmlForTg(hostdResp.result)}):
63355
63545
  ` + preBlock(hostdResp.error ?? "(no error message)"), { html: true });
63356
63546
  }
63357
- bot.command("new", async (ctx) => handleNewOrResetCommand(ctx, "new"));
63358
- bot.command("reset", async (ctx) => handleNewOrResetCommand(ctx, "reset"));
63547
+ bot.command("new", async (ctx) => handleNewCommand(ctx));
63359
63548
  bot.command("update", async (ctx) => {
63360
63549
  if (!isAuthorizedSender(ctx))
63361
63550
  return;
@@ -63693,10 +63882,10 @@ bot.command("interrupt", async (ctx) => {
63693
63882
  });
63694
63883
  var lockoutOps = {
63695
63884
  readFileSync: (p, enc) => readFileSync36(p, enc),
63696
- writeFileSync: (p, data, opts) => writeFileSync25(p, data, opts),
63885
+ writeFileSync: (p, data, opts) => writeFileSync26(p, data, opts),
63697
63886
  existsSync: (p) => existsSync38(p),
63698
- mkdirSync: (p, opts) => mkdirSync26(p, opts),
63699
- joinPath: (...parts) => join35(...parts)
63887
+ mkdirSync: (p, opts) => mkdirSync27(p, opts),
63888
+ joinPath: (...parts) => join36(...parts)
63700
63889
  };
63701
63890
  var FLEET_FALLBACK_DEDUP_MS = 30000;
63702
63891
  function isAuthBrokerSocketReachable() {
@@ -63786,7 +63975,7 @@ async function runCreditWatch() {
63786
63975
  if (!agentDir)
63787
63976
  return;
63788
63977
  const agentName3 = getMyAgentName();
63789
- const claudeConfigDir = join35(agentDir, ".claude");
63978
+ const claudeConfigDir = join36(agentDir, ".claude");
63790
63979
  const stateDir = STATE_DIR;
63791
63980
  const reason = readClaudeJsonOverage(claudeConfigDir);
63792
63981
  const prev = loadCreditState(stateDir);
@@ -64312,10 +64501,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
64312
64501
  return;
64313
64502
  }
64314
64503
  const { token, id } = result;
64315
- const tokenPath = join35(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
64504
+ const tokenPath = join36(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
64316
64505
  try {
64317
- mkdirSync26(join35(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
64318
- writeFileSync25(tokenPath, token, { mode: 384 });
64506
+ mkdirSync27(join36(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
64507
+ writeFileSync26(tokenPath, token, { mode: 384 });
64319
64508
  } catch (err) {
64320
64509
  await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
64321
64510
  <i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
@@ -64391,10 +64580,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
64391
64580
  return;
64392
64581
  }
64393
64582
  const { token, id } = result;
64394
- const tokenPath = join35(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
64583
+ const tokenPath = join36(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
64395
64584
  try {
64396
- mkdirSync26(join35(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
64397
- writeFileSync25(tokenPath, token, { mode: 384 });
64585
+ mkdirSync27(join36(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
64586
+ writeFileSync26(tokenPath, token, { mode: 384 });
64398
64587
  } catch (err) {
64399
64588
  await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
64400
64589
  <i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(pending2.agent)} --keys ${escapeHtmlForTg(pending2.key)} --duration ${Math.round(pending2.ttl_seconds / 86400)}d</code> on the host.</i>`, { html: true });
@@ -64873,10 +65062,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
64873
65062
  return;
64874
65063
  }
64875
65064
  const { token, id } = result;
64876
- const tokenPath = join35(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
65065
+ const tokenPath = join36(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
64877
65066
  try {
64878
- mkdirSync26(join35(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
64879
- writeFileSync25(tokenPath, token, { mode: 384 });
65067
+ mkdirSync27(join36(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
65068
+ writeFileSync26(tokenPath, token, { mode: 384 });
64880
65069
  } catch (err) {
64881
65070
  await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
64882
65071
  return;
@@ -65724,7 +65913,7 @@ bot.command("usage", async (ctx) => {
65724
65913
  await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
65725
65914
  return;
65726
65915
  }
65727
- const result = await fetchQuota2({ claudeConfigDir: join35(agentDir, ".claude") });
65916
+ const result = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
65728
65917
  if (!result.ok) {
65729
65918
  await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
65730
65919
  return;
@@ -66464,9 +66653,9 @@ bot.on("message:photo", async (ctx) => {
66464
66653
  fileUniqueId: best.file_unique_id,
66465
66654
  now: Date.now()
66466
66655
  });
66467
- mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
66656
+ mkdirSync27(INBOX_DIR, { recursive: true, mode: 448 });
66468
66657
  assertInsideInbox(INBOX_DIR, dlPath);
66469
- writeFileSync25(dlPath, buf, { mode: 384 });
66658
+ writeFileSync26(dlPath, buf, { mode: 384 });
66470
66659
  return dlPath;
66471
66660
  } catch (err) {
66472
66661
  const msg = err instanceof Error ? err.message : "unknown error";
@@ -67245,25 +67434,14 @@ process.on("uncaughtException", (err) => {
67245
67434
  shutdown("uncaughtException");
67246
67435
  });
67247
67436
  var runnerHandle = null;
67248
- var POLL_HEALTH_INTERVAL_MS = Number(process.env.SWITCHROOM_POLL_HEALTH_INTERVAL_MS ?? 300000);
67437
+ var POLL_HEALTH_INTERVAL_MS = Number(process.env.SWITCHROOM_POLL_HEALTH_INTERVAL_MS ?? 60000);
67249
67438
  var POLL_HEALTH_THRESHOLD = Number(process.env.SWITCHROOM_POLL_HEALTH_THRESHOLD ?? 3);
67250
67439
  var pollHealthCheck = null;
67251
67440
  if (POLL_HEALTH_INTERVAL_MS > 0) {
67252
67441
  pollHealthCheck = createPollHealthCheck({
67253
67442
  ping: () => bot.api.getMe(),
67254
67443
  onStall: async () => {
67255
- const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
67256
- process.stderr.write(`telegram gateway: poll.health_check.stall_recovery stopping runner agent=${agentName3}
67257
- `);
67258
- if (runnerHandle != null && runnerHandle.isRunning()) {
67259
- try {
67260
- await runnerHandle.stop();
67261
- } catch (err) {
67262
- process.stderr.write(`telegram gateway: poll.health_check.stall_recovery runner.stop error: ${err.message}
67263
- `);
67264
- }
67265
- }
67266
- runnerHandle = null;
67444
+ recoverFromPollStall({ agentName: process.env.SWITCHROOM_AGENT_NAME ?? "-" });
67267
67445
  },
67268
67446
  intervalMs: POLL_HEALTH_INTERVAL_MS,
67269
67447
  failureThreshold: POLL_HEALTH_THRESHOLD,
@@ -67451,7 +67629,7 @@ var didOneTimeSetup = false;
67451
67629
  return;
67452
67630
  }
67453
67631
  })();
67454
- const resolvedAgentDirForBootCard = agentDir ?? join35(homedir14(), ".switchroom", "agents", agentSlug);
67632
+ const resolvedAgentDirForBootCard = agentDir ?? join36(homedir14(), ".switchroom", "agents", agentSlug);
67455
67633
  const handle = await startBootCard(chatId, threadId, botApiForCard, {
67456
67634
  agentName: agentDisplayName,
67457
67635
  agentSlug,
@@ -67465,8 +67643,8 @@ var didOneTimeSetup = false;
67465
67643
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
67466
67644
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
67467
67645
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
67468
- configSnapshotPath: join35(resolvedAgentDirForBootCard, ".config-snapshot.json"),
67469
- bootCardStatePath: join35(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
67646
+ configSnapshotPath: join36(resolvedAgentDirForBootCard, ".config-snapshot.json"),
67647
+ bootCardStatePath: join36(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
67470
67648
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
67471
67649
  }, ackMsgId);
67472
67650
  activeBootCard = handle;
@@ -67853,13 +68031,6 @@ var didOneTimeSetup = false;
67853
68031
  pollHealthCheck?.stop();
67854
68032
  pollHealthCheck?.start();
67855
68033
  await runnerHandle.task();
67856
- if (runnerHandle === null) {
67857
- const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
67858
- process.stderr.write(`telegram gateway: poll.health_check.stall_recovery restarting runner agent=${agentName3}
67859
- `);
67860
- await new Promise((r) => setTimeout(r, 2000));
67861
- continue;
67862
- }
67863
68034
  return;
67864
68035
  } catch (err) {
67865
68036
  if (err instanceof import_grammy9.GrammyError && err.error_code === 409) {