switchroom 0.14.48 → 0.14.50

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.
@@ -49462,8 +49462,8 @@ var {
49462
49462
  } = import__.default;
49463
49463
 
49464
49464
  // src/build-info.ts
49465
- var VERSION = "0.14.48";
49466
- var COMMIT_SHA = "a6517652";
49465
+ var VERSION = "0.14.50";
49466
+ var COMMIT_SHA = "07e8b692";
49467
49467
 
49468
49468
  // src/cli/agent.ts
49469
49469
  init_source();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchroom",
3
- "version": "0.14.48",
3
+ "version": "0.14.50",
4
4
  "description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29493,6 +29493,37 @@ function persistSnapshot(path, snapshot) {
29493
29493
  }
29494
29494
  var init_config_snapshot = () => {};
29495
29495
 
29496
+ // gateway/boot-card-msgid.ts
29497
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync17 } from "node:fs";
29498
+ function bootCardChatKey(chatId, threadId) {
29499
+ return `${chatId}:${threadId ?? ""}`;
29500
+ }
29501
+ function readStore(path) {
29502
+ try {
29503
+ const parsed = JSON.parse(readFileSync27(path, "utf8"));
29504
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
29505
+ return parsed;
29506
+ }
29507
+ } catch {}
29508
+ return {};
29509
+ }
29510
+ function loadBootCardMsgId(path, chatKey3) {
29511
+ const id = readStore(path)[chatKey3];
29512
+ return typeof id === "number" && Number.isFinite(id) && id > 0 ? id : null;
29513
+ }
29514
+ function saveBootCardMsgId(path, chatKey3, messageId) {
29515
+ if (!(Number.isFinite(messageId) && messageId > 0))
29516
+ return;
29517
+ try {
29518
+ const store2 = readStore(path);
29519
+ if (store2[chatKey3] === messageId)
29520
+ return;
29521
+ store2[chatKey3] = messageId;
29522
+ writeFileSync17(path, JSON.stringify(store2), "utf8");
29523
+ } catch {}
29524
+ }
29525
+ var init_boot_card_msgid = () => {};
29526
+
29496
29527
  // gateway/boot-card.ts
29497
29528
  var exports_boot_card = {};
29498
29529
  __export(exports_boot_card, {
@@ -29640,22 +29671,46 @@ async function startBootCard(chatId, threadId, bot, opts, ackMessageId, log) {
29640
29671
  ...opts.updateOutcomeLine ? { updateOutcomeLine: opts.updateOutcomeLine } : {}
29641
29672
  });
29642
29673
  const silentBootCard = true;
29643
- let messageId;
29644
- try {
29645
- const sent = await bot.sendMessage(chatId, ackText, {
29646
- parse_mode: "HTML",
29647
- link_preview_options: { is_disabled: true },
29648
- ...threadId != null ? { message_thread_id: threadId } : {},
29649
- ...ackMessageId != null ? { reply_parameters: { message_id: ackMessageId } } : {},
29650
- ...silentBootCard ? { disable_notification: true } : {}
29651
- });
29652
- messageId = sent.message_id;
29653
- logger2(`telegram gateway: boot-card: posted msgId=${messageId} chatId=${chatId} reason=${opts.restartReason ?? "-"} reason_detail=${opts.restartReasonDetail ?? "-"} silent=${silentBootCard}
29674
+ const chatKey3 = bootCardChatKey(chatId, threadId);
29675
+ const reuseId = ackMessageId == null && opts.bootCardStatePath != null && bot.editMessageTextStrict != null ? loadBootCardMsgId(opts.bootCardStatePath, chatKey3) : null;
29676
+ let messageId = -1;
29677
+ if (reuseId != null && bot.editMessageTextStrict != null) {
29678
+ try {
29679
+ const outcome = await bot.editMessageTextStrict(chatId, reuseId, ackText, {
29680
+ parse_mode: "HTML",
29681
+ link_preview_options: { is_disabled: true },
29682
+ ...threadId != null ? { message_thread_id: threadId } : {}
29683
+ });
29684
+ if (outcome === "edited") {
29685
+ messageId = reuseId;
29686
+ logger2(`telegram gateway: boot-card: reused msgId=${messageId} chatId=${chatId} reason=${opts.restartReason ?? "-"} reason_detail=${opts.restartReasonDetail ?? "-"} edit_in_place=true notify=none
29654
29687
  `);
29655
- } catch (err) {
29656
- logger2(`telegram gateway: boot-card: failed to post ack: ${err?.message ?? String(err)}
29688
+ }
29689
+ } catch (err) {
29690
+ logger2(`telegram gateway: boot-card: edit-in-place probe failed (${err?.message ?? String(err)}) \u2014 sending fresh
29657
29691
  `);
29658
- return { messageId: -1, complete: () => {} };
29692
+ }
29693
+ }
29694
+ if (messageId < 0) {
29695
+ try {
29696
+ const sent = await bot.sendMessage(chatId, ackText, {
29697
+ parse_mode: "HTML",
29698
+ link_preview_options: { is_disabled: true },
29699
+ ...threadId != null ? { message_thread_id: threadId } : {},
29700
+ ...ackMessageId != null ? { reply_parameters: { message_id: ackMessageId } } : {},
29701
+ ...silentBootCard ? { disable_notification: true } : {}
29702
+ });
29703
+ messageId = sent.message_id;
29704
+ logger2(`telegram gateway: boot-card: posted msgId=${messageId} chatId=${chatId} reason=${opts.restartReason ?? "-"} reason_detail=${opts.restartReasonDetail ?? "-"} silent=${silentBootCard}
29705
+ `);
29706
+ } catch (err) {
29707
+ logger2(`telegram gateway: boot-card: failed to post ack: ${err?.message ?? String(err)}
29708
+ `);
29709
+ return { messageId: -1, complete: () => {} };
29710
+ }
29711
+ }
29712
+ if (opts.bootCardStatePath != null && messageId > 0) {
29713
+ saveBootCardMsgId(opts.bootCardStatePath, chatKey3, messageId);
29659
29714
  }
29660
29715
  const liveWindowMs = opts.agentLiveWindowMs ?? AGENT_LIVE_WINDOW_MS;
29661
29716
  setTimeoutFn(() => {
@@ -29822,6 +29877,7 @@ var init_boot_card = __esm(() => {
29822
29877
  init_boot_probes();
29823
29878
  init_boot_issue_cache();
29824
29879
  init_config_snapshot();
29880
+ init_boot_card_msgid();
29825
29881
  init_loader();
29826
29882
  init_merge();
29827
29883
  DOT = {
@@ -30211,8 +30267,8 @@ var init_flock = () => {};
30211
30267
  // ../src/vault/vault.ts
30212
30268
  import { randomBytes as randomBytes5, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
30213
30269
  import {
30214
- readFileSync as readFileSync34,
30215
- writeFileSync as writeFileSync22,
30270
+ readFileSync as readFileSync35,
30271
+ writeFileSync as writeFileSync23,
30216
30272
  existsSync as existsSync36,
30217
30273
  renameSync as renameSync11,
30218
30274
  mkdirSync as mkdirSync23,
@@ -30256,7 +30312,7 @@ function openVault(passphrase, vaultPath) {
30256
30312
  }
30257
30313
  let vaultFile;
30258
30314
  try {
30259
- vaultFile = JSON.parse(readFileSync34(vaultPath, "utf8"));
30315
+ vaultFile = JSON.parse(readFileSync35(vaultPath, "utf8"));
30260
30316
  } catch {
30261
30317
  throw new VaultError(`Failed to read vault file: ${vaultPath}`);
30262
30318
  }
@@ -30637,7 +30693,7 @@ __export(exports_tmux, {
30637
30693
  captureAgentPane: () => captureAgentPane
30638
30694
  });
30639
30695
  import { execFileSync as execFileSync4 } from "node:child_process";
30640
- import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync23 } from "node:fs";
30696
+ import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync24 } from "node:fs";
30641
30697
  import { resolve as resolve7 } from "node:path";
30642
30698
  function captureAgentPane(opts) {
30643
30699
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -30683,7 +30739,7 @@ function captureAgentPane(opts) {
30683
30739
  ` + `
30684
30740
  `;
30685
30741
  try {
30686
- writeFileSync23(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
30742
+ writeFileSync24(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
30687
30743
  mode: 420
30688
30744
  });
30689
30745
  } catch (err) {
@@ -31259,8 +31315,8 @@ var import_runner2 = __toESM(require_mod3(), 1);
31259
31315
  import { randomBytes as randomBytes6 } from "crypto";
31260
31316
  import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
31261
31317
  import {
31262
- readFileSync as readFileSync35,
31263
- writeFileSync as writeFileSync24,
31318
+ readFileSync as readFileSync36,
31319
+ writeFileSync as writeFileSync25,
31264
31320
  mkdirSync as mkdirSync26,
31265
31321
  readdirSync as readdirSync7,
31266
31322
  rmSync as rmSync4,
@@ -47320,6 +47376,9 @@ function shouldTrackDelivery(input) {
47320
47376
  return false;
47321
47377
  return true;
47322
47378
  }
47379
+ function isTrackableResumeSynthetic(meta) {
47380
+ return meta?.source === "resume_interrupted" && meta.message_id != null && meta.message_id !== "";
47381
+ }
47323
47382
 
47324
47383
  // gateway/pending-permission-decisions.ts
47325
47384
  var DEFAULT_PENDING_PERMISSION_CAP = 32;
@@ -50439,7 +50498,7 @@ function determineRestartReason(opts) {
50439
50498
  init_boot_card();
50440
50499
 
50441
50500
  // gateway/update-announce.ts
50442
- import { existsSync as existsSync29, mkdirSync as mkdirSync17, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync27 } from "node:fs";
50501
+ import { existsSync as existsSync29, mkdirSync as mkdirSync17, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync28 } from "node:fs";
50443
50502
  import { join as join26 } from "node:path";
50444
50503
  import { homedir as homedir12 } from "node:os";
50445
50504
 
@@ -50554,7 +50613,7 @@ var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
50554
50613
  function readLastTerminalUpdateAudit(opts = {}) {
50555
50614
  const path = opts.auditLogPath ?? defaultAuditLogPath();
50556
50615
  const exists = opts.exists ?? existsSync29;
50557
- const readFile = opts.readFile ?? ((p) => readFileSync27(p, "utf-8"));
50616
+ const readFile = opts.readFile ?? ((p) => readFileSync28(p, "utf-8"));
50558
50617
  if (!exists(path))
50559
50618
  return null;
50560
50619
  let raw;
@@ -50642,7 +50701,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
50642
50701
  }
50643
50702
 
50644
50703
  // issues-card.ts
50645
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "node:fs";
50704
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync18 } from "node:fs";
50646
50705
  var SEVERITY_EMOJI = {
50647
50706
  info: "\u2139\ufe0f",
50648
50707
  warn: "\u26a0\ufe0f",
@@ -50734,7 +50793,7 @@ function extractRetryAfterSecs2(err) {
50734
50793
  var COOLDOWN_JITTER_MS2 = 500;
50735
50794
  function readPersistedMessageId(path, log) {
50736
50795
  try {
50737
- const raw = readFileSync28(path, "utf8");
50796
+ const raw = readFileSync29(path, "utf8");
50738
50797
  const parsed = JSON.parse(raw);
50739
50798
  const v = parsed.messageId;
50740
50799
  if (typeof v === "number" && Number.isInteger(v) && v > 0)
@@ -50750,7 +50809,7 @@ function readPersistedMessageId(path, log) {
50750
50809
  }
50751
50810
  function writePersistedMessageId(path, messageId, log) {
50752
50811
  try {
50753
- writeFileSync17(path, JSON.stringify({ messageId }) + `
50812
+ writeFileSync18(path, JSON.stringify({ messageId }) + `
50754
50813
  `, { mode: 384 });
50755
50814
  } catch (err) {
50756
50815
  log(`issues-card: persist write failed (${err.message})`);
@@ -50853,11 +50912,11 @@ import {
50853
50912
  mkdirSync as mkdirSync18,
50854
50913
  openSync as openSync4,
50855
50914
  readdirSync as readdirSync5,
50856
- readFileSync as readFileSync29,
50915
+ readFileSync as readFileSync30,
50857
50916
  renameSync as renameSync10,
50858
50917
  statSync as statSync7,
50859
50918
  unlinkSync as unlinkSync10,
50860
- writeFileSync as writeFileSync18,
50919
+ writeFileSync as writeFileSync19,
50861
50920
  writeSync
50862
50921
  } from "node:fs";
50863
50922
  import { join as join27 } from "node:path";
@@ -50884,7 +50943,7 @@ function readAll(stateDir) {
50884
50943
  return [];
50885
50944
  let raw;
50886
50945
  try {
50887
- raw = readFileSync29(path, "utf-8");
50946
+ raw = readFileSync30(path, "utf-8");
50888
50947
  } catch {
50889
50948
  return [];
50890
50949
  }
@@ -50940,7 +50999,7 @@ function writeAll(stateDir, events) {
50940
50999
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
50941
51000
  `) + `
50942
51001
  `;
50943
- writeFileSync18(tmp, body, "utf-8");
51002
+ writeFileSync19(tmp, body, "utf-8");
50944
51003
  renameSync10(tmp, path);
50945
51004
  }
50946
51005
  var ORPHAN_TMP_TTL_MS = 60000;
@@ -51003,7 +51062,7 @@ function withLock(stateDir, fn) {
51003
51062
  function tryStealStaleLock(lockPath) {
51004
51063
  let pidStr;
51005
51064
  try {
51006
- pidStr = readFileSync29(lockPath, "utf-8").trim();
51065
+ pidStr = readFileSync30(lockPath, "utf-8").trim();
51007
51066
  } catch {
51008
51067
  return true;
51009
51068
  }
@@ -51781,7 +51840,7 @@ function extractFlowItems(line) {
51781
51840
  }
51782
51841
 
51783
51842
  // credits-watch.ts
51784
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync19, existsSync as existsSync32, mkdirSync as mkdirSync19 } from "fs";
51843
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync20, existsSync as existsSync32, mkdirSync as mkdirSync19 } from "fs";
51785
51844
  import { join as join29 } from "path";
51786
51845
  var STATE_FILE = "credits-watch.json";
51787
51846
  var FATAL_REASONS = new Set([
@@ -51799,7 +51858,7 @@ function readClaudeJsonOverage(claudeConfigDir) {
51799
51858
  return null;
51800
51859
  let raw;
51801
51860
  try {
51802
- raw = readFileSync30(path, "utf-8");
51861
+ raw = readFileSync31(path, "utf-8");
51803
51862
  } catch {
51804
51863
  return null;
51805
51864
  }
@@ -51883,7 +51942,7 @@ function loadCreditState(stateDir) {
51883
51942
  if (!existsSync32(path))
51884
51943
  return emptyCreditState();
51885
51944
  try {
51886
- const raw = readFileSync30(path, "utf-8");
51945
+ const raw = readFileSync31(path, "utf-8");
51887
51946
  const parsed = JSON.parse(raw);
51888
51947
  if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
51889
51948
  return {
@@ -51897,12 +51956,12 @@ function loadCreditState(stateDir) {
51897
51956
  function saveCreditState(stateDir, state4) {
51898
51957
  mkdirSync19(stateDir, { recursive: true });
51899
51958
  const path = join29(stateDir, STATE_FILE);
51900
- writeFileSync19(path, JSON.stringify(state4, null, 2) + `
51959
+ writeFileSync20(path, JSON.stringify(state4, null, 2) + `
51901
51960
  `, { mode: 384 });
51902
51961
  }
51903
51962
 
51904
51963
  // quota-watch.ts
51905
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync20, existsSync as existsSync33, mkdirSync as mkdirSync20 } from "fs";
51964
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync21, existsSync as existsSync33, mkdirSync as mkdirSync20 } from "fs";
51906
51965
  import { join as join30 } from "path";
51907
51966
  var STATE_FILE2 = "quota-watch.json";
51908
51967
  function emptyQuotaWatchState() {
@@ -51997,7 +52056,7 @@ function loadQuotaWatchState(stateDir) {
51997
52056
  if (!existsSync33(path))
51998
52057
  return emptyQuotaWatchState();
51999
52058
  try {
52000
- const raw = readFileSync31(path, "utf-8");
52059
+ const raw = readFileSync32(path, "utf-8");
52001
52060
  const parsed = JSON.parse(raw);
52002
52061
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
52003
52062
  return emptyQuotaWatchState();
@@ -52016,7 +52075,7 @@ function loadQuotaWatchState(stateDir) {
52016
52075
  function saveQuotaWatchState(stateDir, state4) {
52017
52076
  mkdirSync20(stateDir, { recursive: true });
52018
52077
  const path = join30(stateDir, STATE_FILE2);
52019
- writeFileSync20(path, JSON.stringify(state4, null, 2) + `
52078
+ writeFileSync21(path, JSON.stringify(state4, null, 2) + `
52020
52079
  `, { mode: 384 });
52021
52080
  }
52022
52081
  function patchQuotaWatchState(current, accountLabel, accountState) {
@@ -52032,18 +52091,18 @@ import {
52032
52091
  existsSync as existsSync34,
52033
52092
  mkdirSync as mkdirSync21,
52034
52093
  openSync as openSync5,
52035
- readFileSync as readFileSync32,
52094
+ readFileSync as readFileSync33,
52036
52095
  statSync as statSync9,
52037
52096
  unlinkSync as unlinkSync11,
52038
52097
  utimesSync as utimesSync2,
52039
- writeFileSync as writeFileSync21
52098
+ writeFileSync as writeFileSync22
52040
52099
  } from "node:fs";
52041
52100
  import { join as join31 } from "node:path";
52042
52101
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
52043
52102
  function writeTurnActiveMarker(stateDir, marker) {
52044
52103
  try {
52045
52104
  mkdirSync21(stateDir, { recursive: true });
52046
- writeFileSync21(join31(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
52105
+ writeFileSync22(join31(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
52047
52106
  `, { mode: 384 });
52048
52107
  } catch {}
52049
52108
  }
@@ -52080,7 +52139,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
52080
52139
  return false;
52081
52140
  let payload = null;
52082
52141
  try {
52083
- payload = readFileSync32(path, "utf8");
52142
+ payload = readFileSync33(path, "utf8");
52084
52143
  } catch {}
52085
52144
  unlinkSync11(path);
52086
52145
  if (opts.onRemove) {
@@ -52099,10 +52158,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
52099
52158
  }
52100
52159
 
52101
52160
  // ../src/build-info.ts
52102
- var VERSION = "0.14.48";
52103
- var COMMIT_SHA = "a6517652";
52104
- var COMMIT_DATE = "2026-06-03T07:57:29Z";
52105
- var LATEST_PR = 2120;
52161
+ var VERSION = "0.14.50";
52162
+ var COMMIT_SHA = "07e8b692";
52163
+ var COMMIT_DATE = "2026-06-03T10:08:18Z";
52164
+ var LATEST_PR = 2124;
52106
52165
  var COMMITS_AHEAD_OF_TAG = 0;
52107
52166
 
52108
52167
  // gateway/boot-version.ts
@@ -52617,15 +52676,18 @@ function promptClause(turn) {
52617
52676
  function buildResumeInterruptedInbound(ctx) {
52618
52677
  const ts = ctx.nowMs ?? Date.now();
52619
52678
  const elapsed = humanizeElapsed(ts - ctx.turn.started_at);
52679
+ const threadId = threadIdNum(ctx.turn);
52620
52680
  const meta = {
52621
52681
  source: "resume_interrupted",
52682
+ chat_id: ctx.turn.chat_id,
52683
+ ...threadId != null ? { message_thread_id: String(threadId) } : {},
52684
+ message_id: String(ts),
52622
52685
  resume_turn_key: ctx.turn.turn_key,
52623
52686
  interrupted_via: ctx.turn.ended_via ?? "restart",
52624
52687
  started_at: String(ctx.turn.started_at)
52625
52688
  };
52626
52689
  if (ctx.turn.user_prompt_preview)
52627
52690
  meta.original_prompt = ctx.turn.user_prompt_preview;
52628
- const threadId = threadIdNum(ctx.turn);
52629
52691
  return {
52630
52692
  type: "inbound",
52631
52693
  chatId: ctx.turn.chat_id,
@@ -52643,8 +52705,11 @@ function buildResumeWatchdogReportInbound(ctx) {
52643
52705
  const idle = humanizeElapsed(ctx.idleMs);
52644
52706
  const since = humanizeElapsed(ts - ctx.turn.started_at);
52645
52707
  const toolClause = ctx.turn.tool_call_count != null && ctx.turn.tool_call_count > 0 ? ` You'd run ${ctx.turn.tool_call_count} tool call${ctx.turn.tool_call_count === 1 ? "" : "s"} before it stalled.` : "";
52708
+ const threadId = threadIdNum(ctx.turn);
52646
52709
  const meta = {
52647
52710
  source: "resume_watchdog_timeout",
52711
+ chat_id: ctx.turn.chat_id,
52712
+ ...threadId != null ? { message_thread_id: String(threadId) } : {},
52648
52713
  resume_turn_key: ctx.turn.turn_key,
52649
52714
  interrupted_via: "timeout",
52650
52715
  idle_ms: String(ctx.idleMs),
@@ -52654,7 +52719,6 @@ function buildResumeWatchdogReportInbound(ctx) {
52654
52719
  meta.tool_call_count = String(ctx.turn.tool_call_count);
52655
52720
  if (ctx.turn.user_prompt_preview)
52656
52721
  meta.original_prompt = ctx.turn.user_prompt_preview;
52657
- const threadId = threadIdNum(ctx.turn);
52658
52722
  return {
52659
52723
  type: "inbound",
52660
52724
  chatId: ctx.turn.chat_id,
@@ -52849,7 +52913,7 @@ function formatBootVersion() {
52849
52913
  }
52850
52914
  try {
52851
52915
  chmodSync6(ENV_FILE, 384);
52852
- for (const line of readFileSync35(ENV_FILE, "utf8").split(`
52916
+ for (const line of readFileSync36(ENV_FILE, "utf8").split(`
52853
52917
  `)) {
52854
52918
  const m = line.match(/^(\w+)=(.*)$/);
52855
52919
  if (m && process.env[m[1]] === undefined)
@@ -52902,7 +52966,7 @@ installTgPostLogger(bot);
52902
52966
  var _rawSendMessageDraft = bot.api.raw.sendMessageDraft;
52903
52967
  var GRAMMY_VERSION = (() => {
52904
52968
  try {
52905
- const raw = readFileSync35(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
52969
+ const raw = readFileSync36(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
52906
52970
  return JSON.parse(raw).version ?? "unknown";
52907
52971
  } catch {
52908
52972
  return "unknown";
@@ -52994,7 +53058,7 @@ function assertSendable(f) {
52994
53058
  }
52995
53059
  function readAccessFile() {
52996
53060
  try {
52997
- const raw = readFileSync35(ACCESS_FILE, "utf8");
53061
+ const raw = readFileSync36(ACCESS_FILE, "utf8");
52998
53062
  const parsed = JSON.parse(raw);
52999
53063
  const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
53000
53064
  const groups = {};
@@ -53064,7 +53128,7 @@ function saveAccess(a) {
53064
53128
  return;
53065
53129
  mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
53066
53130
  const tmp = ACCESS_FILE + ".tmp";
53067
- writeFileSync24(tmp, JSON.stringify(a, null, 2) + `
53131
+ writeFileSync25(tmp, JSON.stringify(a, null, 2) + `
53068
53132
  `, { mode: 384 });
53069
53133
  renameSync12(tmp, ACCESS_FILE);
53070
53134
  }
@@ -53105,7 +53169,7 @@ try {
53105
53169
  const st = statSync13(markerPath);
53106
53170
  markerAgeMs = Date.now() - st.mtimeMs;
53107
53171
  try {
53108
- const payload = JSON.parse(readFileSync35(markerPath, "utf8"));
53172
+ const payload = JSON.parse(readFileSync36(markerPath, "utf8"));
53109
53173
  if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
53110
53174
  markerTurnKey = payload.turnKey;
53111
53175
  }
@@ -53176,7 +53240,7 @@ try {
53176
53240
  pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
53177
53241
  ];
53178
53242
  const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
53179
- writeFileSync24(pendingEnvTmp, lines.join(`
53243
+ writeFileSync25(pendingEnvTmp, lines.join(`
53180
53244
  `) + `
53181
53245
  `, { mode: 384 });
53182
53246
  renameSync12(pendingEnvTmp, pendingEnvPath);
@@ -53798,7 +53862,18 @@ function wrapBootCardApi(threadId) {
53798
53862
  const sent = await robustApiCall(() => lockedBot.api.sendMessage(cid, text, sendOpts), opts(cid));
53799
53863
  return sent;
53800
53864
  },
53801
- editMessageText: (cid, mid, text, editOpts) => robustApiCall(() => lockedBot.api.editMessageText(cid, mid, text, editOpts), opts(cid))
53865
+ editMessageText: (cid, mid, text, editOpts) => robustApiCall(() => lockedBot.api.editMessageText(cid, mid, text, editOpts), opts(cid)),
53866
+ editMessageTextStrict: async (cid, mid, text, editOpts) => {
53867
+ try {
53868
+ await lockedBot.api.editMessageText(cid, mid, text, editOpts);
53869
+ return "edited";
53870
+ } catch (err) {
53871
+ const desc = err instanceof import_grammy9.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
53872
+ if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
53873
+ return "edited";
53874
+ return "gone";
53875
+ }
53876
+ }
53802
53877
  };
53803
53878
  }
53804
53879
  function wrapIssuesCardApi(threadId) {
@@ -54434,7 +54509,8 @@ _deliveryMachineTick.unref?.();
54434
54509
  function trackRedeliveredInbound(merged) {
54435
54510
  if (!DELIVERY_CONFIRM_ENABLED)
54436
54511
  return;
54437
- if (!shouldTrackDelivery({
54512
+ const isTrackableResume = isTrackableResumeSynthetic(merged.meta);
54513
+ if (!isTrackableResume && !shouldTrackDelivery({
54438
54514
  isSteering: false,
54439
54515
  isInterrupt: false,
54440
54516
  hasSource: merged.meta?.source != null,
@@ -54496,8 +54572,8 @@ var inboundSpool = STATIC ? undefined : createInboundSpool({
54496
54572
  path: join35(STATE_DIR, "inbound-spool.jsonl"),
54497
54573
  fs: {
54498
54574
  appendFileSync: (p, d) => appendFileSync5(p, d),
54499
- readFileSync: (p) => readFileSync35(p, "utf8"),
54500
- writeFileSync: (p, d) => writeFileSync24(p, d),
54575
+ readFileSync: (p) => readFileSync36(p, "utf8"),
54576
+ writeFileSync: (p, d) => writeFileSync25(p, d),
54501
54577
  renameSync: (a, b) => renameSync12(a, b),
54502
54578
  existsSync: (p) => existsSync38(p),
54503
54579
  statSizeSync: (p) => statSync13(p).size
@@ -54642,6 +54718,7 @@ var ipcServer = createIpcServer({
54642
54718
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
54643
54719
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
54644
54720
  configSnapshotPath: join35(resolvedAgentDirForCard, ".config-snapshot.json"),
54721
+ bootCardStatePath: join35(resolvedAgentDirForCard, ".boot-card-msgid.json"),
54645
54722
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
54646
54723
  }, ackMsgId).then((handle) => {
54647
54724
  activeBootCard = handle;
@@ -56081,7 +56158,7 @@ async function publishToTelegraph(text, shortName, authorName) {
56081
56158
  let account = null;
56082
56159
  try {
56083
56160
  if (existsSync38(accountPath)) {
56084
- const raw = readFileSync35(accountPath, "utf-8");
56161
+ const raw = readFileSync36(accountPath, "utf-8");
56085
56162
  const parsed = JSON.parse(raw);
56086
56163
  if (parsed.shortName && parsed.accessToken) {
56087
56164
  account = parsed;
@@ -56101,7 +56178,7 @@ async function publishToTelegraph(text, shortName, authorName) {
56101
56178
  account = created.value;
56102
56179
  try {
56103
56180
  mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
56104
- writeFileSync24(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
56181
+ writeFileSync25(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
56105
56182
  } catch (err) {
56106
56183
  process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
56107
56184
  `);
@@ -56575,7 +56652,7 @@ async function executeDownloadAttachment(args) {
56575
56652
  });
56576
56653
  mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
56577
56654
  assertInsideInbox(INBOX_DIR, dlPath);
56578
- writeFileSync24(dlPath, buf, { mode: 384 });
56655
+ writeFileSync25(dlPath, buf, { mode: 384 });
56579
56656
  return { content: [{ type: "text", text: dlPath }] };
56580
56657
  }
56581
56658
  async function executeEditMessage(args) {
@@ -58464,7 +58541,7 @@ function writeRestartMarker(marker) {
58464
58541
  if (!p)
58465
58542
  return;
58466
58543
  try {
58467
- writeFileSync24(p, JSON.stringify(marker));
58544
+ writeFileSync25(p, JSON.stringify(marker));
58468
58545
  lastPlannedRestartAt = Date.now();
58469
58546
  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}
58470
58547
  `);
@@ -58483,7 +58560,7 @@ function readRestartMarker() {
58483
58560
  if (!p)
58484
58561
  return null;
58485
58562
  try {
58486
- return JSON.parse(readFileSync35(p, "utf8"));
58563
+ return JSON.parse(readFileSync36(p, "utf8"));
58487
58564
  } catch {
58488
58565
  return null;
58489
58566
  }
@@ -58620,7 +58697,7 @@ function spawnSwitchroomDetached(args, onFailure) {
58620
58697
  try {
58621
58698
  mkdirSync26(STATE_DIR, { recursive: true });
58622
58699
  outFd = openSync8(logPath, "a");
58623
- writeFileSync24(logPath, `
58700
+ writeFileSync25(logPath, `
58624
58701
  [${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
58625
58702
  `, { flag: "a" });
58626
58703
  } catch {}
@@ -58646,7 +58723,7 @@ function spawnSwitchroomDetached(args, onFailure) {
58646
58723
  return;
58647
58724
  let tail = "";
58648
58725
  try {
58649
- const full = readFileSync35(logPath, "utf8");
58726
+ const full = readFileSync36(logPath, "utf8");
58650
58727
  tail = full.split(`
58651
58728
  `).slice(-30).join(`
58652
58729
  `).trim();
@@ -58991,7 +59068,7 @@ function readRecentDenialsForAgent(agentName3, windowMs, limit) {
58991
59068
  const auditPath = join35(homedir14(), ".switchroom", "vault-audit.log");
58992
59069
  if (!existsSync38(auditPath))
58993
59070
  return [];
58994
- const raw = readFileSync35(auditPath, "utf8");
59071
+ const raw = readFileSync36(auditPath, "utf8");
58995
59072
  return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
58996
59073
  } catch {
58997
59074
  return [];
@@ -59042,7 +59119,7 @@ async function buildAgentMetadata(agentName3) {
59042
59119
  try {
59043
59120
  const agentDir = resolveAgentDirFromEnv();
59044
59121
  if (agentDir) {
59045
- const raw = readFileSync35(join35(agentDir, ".claude", ".claude.json"), "utf8");
59122
+ const raw = readFileSync36(join35(agentDir, ".claude", ".claude.json"), "utf8");
59046
59123
  claudeJson = JSON.parse(raw);
59047
59124
  }
59048
59125
  } catch {}
@@ -59314,7 +59391,7 @@ async function handleNewOrResetCommand(ctx, kind) {
59314
59391
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
59315
59392
  if (agentDir != null) {
59316
59393
  try {
59317
- writeFileSync24(join35(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
59394
+ writeFileSync25(join35(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
59318
59395
  `, "utf8");
59319
59396
  } catch (err) {
59320
59397
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -59678,8 +59755,8 @@ bot.command("interrupt", async (ctx) => {
59678
59755
  await runSwitchroomCommand(ctx, ["agent", "interrupt", name], `interrupt ${name}`);
59679
59756
  });
59680
59757
  var lockoutOps = {
59681
- readFileSync: (p, enc) => readFileSync35(p, enc),
59682
- writeFileSync: (p, data, opts) => writeFileSync24(p, data, opts),
59758
+ readFileSync: (p, enc) => readFileSync36(p, enc),
59759
+ writeFileSync: (p, data, opts) => writeFileSync25(p, data, opts),
59683
59760
  existsSync: (p) => existsSync38(p),
59684
59761
  mkdirSync: (p, opts) => mkdirSync26(p, opts),
59685
59762
  joinPath: (...parts) => join35(...parts)
@@ -60054,7 +60131,7 @@ async function handleVaultRecentDenialCallback(ctx, data) {
60054
60131
  const tokenPath = join35(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
60055
60132
  try {
60056
60133
  mkdirSync26(join35(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
60057
- writeFileSync24(tokenPath, token, { mode: 384 });
60134
+ writeFileSync25(tokenPath, token, { mode: 384 });
60058
60135
  } catch (err) {
60059
60136
  await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
60060
60137
  <i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
@@ -60133,7 +60210,7 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
60133
60210
  const tokenPath = join35(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
60134
60211
  try {
60135
60212
  mkdirSync26(join35(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
60136
- writeFileSync24(tokenPath, token, { mode: 384 });
60213
+ writeFileSync25(tokenPath, token, { mode: 384 });
60137
60214
  } catch (err) {
60138
60215
  await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
60139
60216
  <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 });
@@ -60637,7 +60714,7 @@ async function executeGrantWizard(ctx, chatId, state4) {
60637
60714
  const tokenPath = join35(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
60638
60715
  try {
60639
60716
  mkdirSync26(join35(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
60640
- writeFileSync24(tokenPath, token, { mode: 384 });
60717
+ writeFileSync25(tokenPath, token, { mode: 384 });
60641
60718
  } catch (err) {
60642
60719
  await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
60643
60720
  return;
@@ -61933,7 +62010,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
61933
62010
  const unifiedDiff = (() => {
61934
62011
  try {
61935
62012
  const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
61936
- const raw = readFileSync35(cfgPath, "utf8");
62013
+ const raw = readFileSync36(cfgPath, "utf8");
61937
62014
  return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
61938
62015
  } catch (err) {
61939
62016
  process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
@@ -62081,7 +62158,7 @@ bot.on("message:photo", async (ctx) => {
62081
62158
  });
62082
62159
  mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
62083
62160
  assertInsideInbox(INBOX_DIR, dlPath);
62084
- writeFileSync24(dlPath, buf, { mode: 384 });
62161
+ writeFileSync25(dlPath, buf, { mode: 384 });
62085
62162
  return dlPath;
62086
62163
  } catch (err) {
62087
62164
  const msg = err instanceof Error ? err.message : "unknown error";
@@ -62122,7 +62199,7 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
62122
62199
  try {
62123
62200
  const path = __require("path").join(__require("os").homedir(), ".switchroom", "openai-api-key");
62124
62201
  if (existsSync38(path)) {
62125
- apiKey = readFileSync35(path, "utf-8").trim();
62202
+ apiKey = readFileSync36(path, "utf-8").trim();
62126
62203
  }
62127
62204
  } catch (err) {
62128
62205
  process.stderr.write(`telegram gateway: voice-in: failed to read api key: ${err.message}
@@ -62992,6 +63069,7 @@ var didOneTimeSetup = false;
62992
63069
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
62993
63070
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
62994
63071
  configSnapshotPath: join35(resolvedAgentDirForBootCard, ".config-snapshot.json"),
63072
+ bootCardStatePath: join35(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
62995
63073
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
62996
63074
  }, ackMsgId);
62997
63075
  activeBootCard = handle;