switchroom 0.13.13 → 0.13.14

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.
@@ -16565,7 +16565,7 @@ class AuthBrokerClient {
16565
16565
  const id = req.id;
16566
16566
  const frame = encodeRequest(req);
16567
16567
  return new Promise((resolve2, reject) => {
16568
- const timer2 = setTimeout(() => {
16568
+ const timer3 = setTimeout(() => {
16569
16569
  this.pending.delete(id);
16570
16570
  reject(new AuthBrokerUnreachableError(`request ${req.op} timed out after ${this.timeoutMs}ms`, this.socketPath));
16571
16571
  }, this.timeoutMs);
@@ -16578,7 +16578,7 @@ class AuthBrokerClient {
16578
16578
  }
16579
16579
  },
16580
16580
  reject,
16581
- timer: timer2
16581
+ timer: timer3
16582
16582
  });
16583
16583
  sock.write(frame, (err) => {
16584
16584
  if (err) {
@@ -25066,12 +25066,12 @@ async function rpc(req, opts) {
25066
25066
  resolve5(val);
25067
25067
  };
25068
25068
  const client3 = new net3.Socket;
25069
- const timer2 = setTimeout(() => {
25069
+ const timer3 = setTimeout(() => {
25070
25070
  client3.destroy();
25071
25071
  settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
25072
25072
  }, timeoutMs);
25073
25073
  client3.on("error", (err) => {
25074
- clearTimeout(timer2);
25074
+ clearTimeout(timer3);
25075
25075
  const code = err.code ?? "ERR";
25076
25076
  let msg;
25077
25077
  if (code === "ENOENT")
@@ -25091,7 +25091,7 @@ async function rpc(req, opts) {
25091
25091
  `);
25092
25092
  if (newlineIdx !== -1) {
25093
25093
  const line = buffer.slice(0, newlineIdx).trimEnd();
25094
- clearTimeout(timer2);
25094
+ clearTimeout(timer3);
25095
25095
  client3.destroy();
25096
25096
  try {
25097
25097
  const resp = decodeResponse2(line);
@@ -25108,7 +25108,7 @@ async function rpc(req, opts) {
25108
25108
  try {
25109
25109
  client3.write(encodeRequest2(req));
25110
25110
  } catch (err) {
25111
- clearTimeout(timer2);
25111
+ clearTimeout(timer3);
25112
25112
  client3.destroy();
25113
25113
  settle({
25114
25114
  kind: "unreachable",
@@ -27352,14 +27352,14 @@ import { join as join21 } from "path";
27352
27352
  import { execFile as execFileCb } from "child_process";
27353
27353
  import { promisify as promisify3 } from "util";
27354
27354
  async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
27355
- let timer2;
27355
+ let timer3;
27356
27356
  const timeout = new Promise((resolve6) => {
27357
- timer2 = setTimeout(() => resolve6({ status: "fail", label, detail: "timed out" }), timeoutMs);
27357
+ timer3 = setTimeout(() => resolve6({ status: "fail", label, detail: "timed out" }), timeoutMs);
27358
27358
  });
27359
27359
  try {
27360
27360
  return await Promise.race([p, timeout]);
27361
27361
  } finally {
27362
- clearTimeout(timer2);
27362
+ clearTimeout(timer3);
27363
27363
  }
27364
27364
  }
27365
27365
  function formatMs(ms) {
@@ -28163,7 +28163,7 @@ function diffProbes(probes, cache, opts = {}) {
28163
28163
  const snoozeBoots = opts.snoozeBoots ?? DEFAULT_SNOOZE_BOOTS;
28164
28164
  const snoozeMs = opts.snoozeMs ?? DEFAULT_SNOOZE_MS;
28165
28165
  const now = opts.now ?? Date.now;
28166
- const nowMs = now();
28166
+ const nowMs2 = now();
28167
28167
  const out = {};
28168
28168
  for (const [key, r] of Object.entries(probes)) {
28169
28169
  if (!r)
@@ -28182,12 +28182,12 @@ function diffProbes(probes, cache, opts = {}) {
28182
28182
  continue;
28183
28183
  }
28184
28184
  let consecutiveBoots = 1;
28185
- let firstSeenMs = nowMs;
28185
+ let firstSeenMs = nowMs2;
28186
28186
  if (prev != null && prev.fingerprint === fp) {
28187
28187
  consecutiveBoots = prev.consecutiveBoots + 1;
28188
28188
  firstSeenMs = prev.firstSeenMs;
28189
28189
  }
28190
- const ageMs = nowMs - firstSeenMs;
28190
+ const ageMs = nowMs2 - firstSeenMs;
28191
28191
  const snoozed = consecutiveBoots > snoozeBoots || ageMs >= snoozeMs;
28192
28192
  out[key] = {
28193
28193
  fingerprint: fp,
@@ -28198,7 +28198,7 @@ function diffProbes(probes, cache, opts = {}) {
28198
28198
  fingerprint: fp,
28199
28199
  consecutiveBoots,
28200
28200
  firstSeenMs,
28201
- lastSeenMs: nowMs
28201
+ lastSeenMs: nowMs2
28202
28202
  }
28203
28203
  };
28204
28204
  }
@@ -37296,6 +37296,144 @@ function startTimer(deps) {
37296
37296
  timer.unref();
37297
37297
  }
37298
37298
 
37299
+ // pending-work-progress.ts
37300
+ var EDIT_INTERVAL_MS = 60000;
37301
+ var POLL_INTERVAL_MS = 5000;
37302
+ var MAX_LIFETIME_MS = 30 * 60000;
37303
+ var TELEGRAM_MSG_CAP = 4000;
37304
+ var SUFFIX_RE = /\n\n\u2014 still working \(\d+m\)$/;
37305
+ var stateByKey = new Map;
37306
+ var timer2 = null;
37307
+ var activeDeps2 = null;
37308
+ function enabled2() {
37309
+ const v = process.env.SWITCHROOM_DISABLE_PENDING_PROGRESS;
37310
+ return !(v === "1" || v === "true");
37311
+ }
37312
+ function nowMs() {
37313
+ return activeDeps2?.nowMs ? activeDeps2.nowMs() : Date.now();
37314
+ }
37315
+ function ensure(key) {
37316
+ let s = stateByKey.get(key);
37317
+ if (!s) {
37318
+ s = {
37319
+ pending: false,
37320
+ anchorMessageId: null,
37321
+ anchorOriginalText: "",
37322
+ activatedAt: null,
37323
+ lastEditAt: null
37324
+ };
37325
+ stateByKey.set(key, s);
37326
+ }
37327
+ return s;
37328
+ }
37329
+ function noteAsyncDispatch(key) {
37330
+ if (!enabled2())
37331
+ return;
37332
+ ensure(key).pending = true;
37333
+ }
37334
+ function noteOutbound3(key, opts) {
37335
+ if (!enabled2())
37336
+ return;
37337
+ const s = ensure(key);
37338
+ s.anchorMessageId = opts.messageId;
37339
+ s.anchorOriginalText = opts.text.replace(SUFFIX_RE, "");
37340
+ }
37341
+ function noteTurnEnd(key) {
37342
+ if (!enabled2())
37343
+ return;
37344
+ const s = stateByKey.get(key);
37345
+ if (s == null)
37346
+ return;
37347
+ if (s.pending && s.anchorMessageId != null) {
37348
+ s.activatedAt = nowMs();
37349
+ s.lastEditAt = s.activatedAt;
37350
+ activeDeps2?.emitMetric?.({
37351
+ kind: "pending_progress_started",
37352
+ chatKey: key
37353
+ });
37354
+ } else {
37355
+ stateByKey.delete(key);
37356
+ }
37357
+ }
37358
+ function clearPending(key, reason) {
37359
+ if (!stateByKey.has(key))
37360
+ return;
37361
+ const s = stateByKey.get(key);
37362
+ const elapsed = s.activatedAt != null ? nowMs() - s.activatedAt : 0;
37363
+ stateByKey.delete(key);
37364
+ activeDeps2?.emitMetric?.({
37365
+ kind: "pending_progress_cleared",
37366
+ chatKey: key,
37367
+ elapsedMs: elapsed,
37368
+ reason
37369
+ });
37370
+ }
37371
+ function startTimer2(deps) {
37372
+ if (!enabled2())
37373
+ return;
37374
+ if (timer2 != null)
37375
+ return;
37376
+ activeDeps2 = deps;
37377
+ const interval = deps.pollIntervalMs ?? POLL_INTERVAL_MS;
37378
+ timer2 = setInterval(() => tick2(nowMs()), interval);
37379
+ if (typeof timer2.unref === "function")
37380
+ timer2.unref();
37381
+ }
37382
+ function parseKey2(key) {
37383
+ const idx = key.indexOf(":");
37384
+ if (idx < 0)
37385
+ return { chatId: key, threadId: null };
37386
+ const chatId = key.slice(0, idx);
37387
+ const tail = key.slice(idx + 1);
37388
+ if (tail === "" || tail === "undefined")
37389
+ return { chatId, threadId: null };
37390
+ const n = Number(tail);
37391
+ return { chatId, threadId: Number.isFinite(n) ? n : null };
37392
+ }
37393
+ function tick2(now) {
37394
+ if (activeDeps2 == null)
37395
+ return;
37396
+ for (const [key, s] of stateByKey.entries()) {
37397
+ if (s.activatedAt == null || s.anchorMessageId == null)
37398
+ continue;
37399
+ const elapsed = now - s.activatedAt;
37400
+ if (elapsed >= MAX_LIFETIME_MS) {
37401
+ clearPending(key, "timeout");
37402
+ continue;
37403
+ }
37404
+ const sinceEdit = s.lastEditAt == null ? 0 : now - s.lastEditAt;
37405
+ if (sinceEdit < EDIT_INTERVAL_MS)
37406
+ continue;
37407
+ const minutes = Math.max(1, Math.round(elapsed / 60000));
37408
+ const suffix = `
37409
+
37410
+ \u2014 still working (${minutes}m)`;
37411
+ const newText = s.anchorOriginalText + suffix;
37412
+ if (newText.length > TELEGRAM_MSG_CAP) {
37413
+ s.lastEditAt = now;
37414
+ continue;
37415
+ }
37416
+ const { chatId, threadId } = parseKey2(key);
37417
+ s.lastEditAt = now;
37418
+ const editCtx = {
37419
+ chatId,
37420
+ threadId,
37421
+ messageId: s.anchorMessageId,
37422
+ newText
37423
+ };
37424
+ Promise.resolve().then(() => activeDeps2.editMessage(editCtx)).then(() => {
37425
+ activeDeps2.emitMetric?.({
37426
+ kind: "pending_progress_edited",
37427
+ chatKey: key,
37428
+ elapsedMs: elapsed
37429
+ });
37430
+ }).catch((err) => {
37431
+ process.stderr.write(`pending-work-progress: edit failed key=${key} ` + `msg=${editCtx.messageId}: ${err.message}
37432
+ `);
37433
+ });
37434
+ }
37435
+ }
37436
+
37299
37437
  // silent-end.ts
37300
37438
  import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync5 } from "node:fs";
37301
37439
  import { dirname as dirname6, join as join6 } from "node:path";
@@ -38956,7 +39094,7 @@ class AuthBrokerClient2 {
38956
39094
  const id = req.id;
38957
39095
  const frame = encodeRequest(req);
38958
39096
  return new Promise((resolve2, reject) => {
38959
- const timer2 = setTimeout(() => {
39097
+ const timer3 = setTimeout(() => {
38960
39098
  this.pending.delete(id);
38961
39099
  reject(new AuthBrokerUnreachableError2(`request ${req.op} timed out after ${this.timeoutMs}ms`, this.socketPath));
38962
39100
  }, this.timeoutMs);
@@ -38969,7 +39107,7 @@ class AuthBrokerClient2 {
38969
39107
  }
38970
39108
  },
38971
39109
  reject,
38972
- timer: timer2
39110
+ timer: timer3
38973
39111
  });
38974
39112
  sock.write(frame, (err) => {
38975
39113
  if (err) {
@@ -39122,7 +39260,7 @@ async function startAccountAuthSession(label, opts = {}) {
39122
39260
  cleanup();
39123
39261
  reject(new Error(`claude setup-token did not print an OAuth URL within ${urlTimeoutMs}ms`));
39124
39262
  }, urlTimeoutMs);
39125
- const tick2 = setInterval(() => {
39263
+ const tick3 = setInterval(() => {
39126
39264
  const url = parseSetupTokenUrl(buffer);
39127
39265
  if (url) {
39128
39266
  cleanup();
@@ -39136,7 +39274,7 @@ async function startAccountAuthSession(label, opts = {}) {
39136
39274
  child.once("exit", onExit);
39137
39275
  function cleanup() {
39138
39276
  clearTimeout(deadline);
39139
- clearInterval(tick2);
39277
+ clearInterval(tick3);
39140
39278
  child.removeListener("exit", onExit);
39141
39279
  }
39142
39280
  }).catch((err) => {
@@ -39794,7 +39932,7 @@ function startRestartWatchdog(config) {
39794
39932
  }
39795
39933
  const unit = `switchroom-${agentName3}`;
39796
39934
  let snapshot = null;
39797
- function tick2() {
39935
+ function tick3() {
39798
39936
  let raw;
39799
39937
  try {
39800
39938
  raw = execShow(unit);
@@ -39824,14 +39962,14 @@ function startRestartWatchdog(config) {
39824
39962
  log?.(`restart-watchdog: tick agent=${agentName3} ${decision.detail}`);
39825
39963
  }
39826
39964
  }
39827
- tick2();
39828
- const timer2 = setInterval(tick2, pollIntervalMs);
39829
- if (typeof timer2.unref === "function")
39830
- timer2.unref();
39965
+ tick3();
39966
+ const timer3 = setInterval(tick3, pollIntervalMs);
39967
+ if (typeof timer3.unref === "function")
39968
+ timer3.unref();
39831
39969
  log?.(`restart-watchdog: started agent=${agentName3} unit=${unit} interval=${pollIntervalMs}ms`);
39832
39970
  return {
39833
39971
  stop() {
39834
- clearInterval(timer2);
39972
+ clearInterval(timer3);
39835
39973
  }
39836
39974
  };
39837
39975
  }
@@ -41186,11 +41324,11 @@ class BrokerCredentialsExpiredError extends Error {
41186
41324
  account;
41187
41325
  expiresAt;
41188
41326
  nowMs;
41189
- constructor(account, expiresAt, nowMs) {
41190
- super(`Google credentials for account '${account}' expired at ${new Date(expiresAt).toISOString()} (now ${new Date(nowMs).toISOString()}). Re-run \`switchroom auth google account add ${account}\` to mint fresh tokens.`);
41327
+ constructor(account, expiresAt, nowMs2) {
41328
+ super(`Google credentials for account '${account}' expired at ${new Date(expiresAt).toISOString()} (now ${new Date(nowMs2).toISOString()}). Re-run \`switchroom auth google account add ${account}\` to mint fresh tokens.`);
41191
41329
  this.account = account;
41192
41330
  this.expiresAt = expiresAt;
41193
- this.nowMs = nowMs;
41331
+ this.nowMs = nowMs2;
41194
41332
  this.name = "BrokerCredentialsExpiredError";
41195
41333
  }
41196
41334
  }
@@ -41898,7 +42036,7 @@ class InjectError extends Error {
41898
42036
  this.code = code;
41899
42037
  }
41900
42038
  }
41901
- var POLL_INTERVAL_MS = 150;
42039
+ var POLL_INTERVAL_MS2 = 150;
41902
42040
  var OUTPUT_BYTE_CAP = 3000;
41903
42041
  function validateInjectCommand(command) {
41904
42042
  if (typeof command !== "string" || command.trim().length === 0) {
@@ -42067,12 +42205,12 @@ async function injectSlashCommandWith(runner, args) {
42067
42205
  let last = before;
42068
42206
  let stableSince = null;
42069
42207
  while (Date.now() - start < timeoutMs) {
42070
- await sleep(POLL_INTERVAL_MS);
42208
+ await sleep(POLL_INTERVAL_MS2);
42071
42209
  const cur = runner.capture(socket, session) ?? "";
42072
42210
  if (cur === last && cur !== before) {
42073
42211
  if (stableSince === null) {
42074
42212
  stableSince = Date.now();
42075
- } else if (Date.now() - stableSince >= POLL_INTERVAL_MS) {
42213
+ } else if (Date.now() - stableSince >= POLL_INTERVAL_MS2) {
42076
42214
  last = cur;
42077
42215
  break;
42078
42216
  }
@@ -43021,7 +43159,7 @@ async function hostdRequest(opts, req) {
43021
43159
  const socket = connect(opts.socketPath);
43022
43160
  let buf = "";
43023
43161
  let settled = false;
43024
- const timer2 = setTimeout(() => {
43162
+ const timer3 = setTimeout(() => {
43025
43163
  if (settled)
43026
43164
  return;
43027
43165
  settled = true;
@@ -43035,7 +43173,7 @@ async function hostdRequest(opts, req) {
43035
43173
  if (settled)
43036
43174
  return;
43037
43175
  settled = true;
43038
- clearTimeout(timer2);
43176
+ clearTimeout(timer3);
43039
43177
  socket.destroy();
43040
43178
  reject(err);
43041
43179
  }
@@ -43046,7 +43184,7 @@ async function hostdRequest(opts, req) {
43046
43184
  if (settled)
43047
43185
  return;
43048
43186
  settled = true;
43049
- clearTimeout(timer2);
43187
+ clearTimeout(timer3);
43050
43188
  socket.destroy();
43051
43189
  reject(new Error("hostd: response exceeded frame budget"));
43052
43190
  return;
@@ -43061,14 +43199,14 @@ async function hostdRequest(opts, req) {
43061
43199
  if (settled)
43062
43200
  return;
43063
43201
  settled = true;
43064
- clearTimeout(timer2);
43202
+ clearTimeout(timer3);
43065
43203
  socket.end();
43066
43204
  resolve6(resp);
43067
43205
  } catch (err) {
43068
43206
  if (settled)
43069
43207
  return;
43070
43208
  settled = true;
43071
- clearTimeout(timer2);
43209
+ clearTimeout(timer3);
43072
43210
  socket.destroy();
43073
43211
  reject(new Error(`hostd: bad response frame: ${err.message}`, { cause: err }));
43074
43212
  }
@@ -43077,14 +43215,14 @@ async function hostdRequest(opts, req) {
43077
43215
  if (settled)
43078
43216
  return;
43079
43217
  settled = true;
43080
- clearTimeout(timer2);
43218
+ clearTimeout(timer3);
43081
43219
  reject(err);
43082
43220
  });
43083
43221
  socket.on("close", () => {
43084
43222
  if (settled)
43085
43223
  return;
43086
43224
  settled = true;
43087
- clearTimeout(timer2);
43225
+ clearTimeout(timer3);
43088
43226
  reject(new Error("hostd: connection closed before response"));
43089
43227
  });
43090
43228
  });
@@ -44448,9 +44586,9 @@ function transition(state3, event) {
44448
44586
 
44449
44587
  // gateway/inbound-delivery-machine-shadow.ts
44450
44588
  var state3 = initialState();
44451
- var enabled2 = process.env.SWITCHROOM_DELIVERY_MACHINE_SHADOW !== "0";
44589
+ var enabled3 = process.env.SWITCHROOM_DELIVERY_MACHINE_SHADOW !== "0";
44452
44590
  function shadowEmit(event) {
44453
- if (!enabled2)
44591
+ if (!enabled3)
44454
44592
  return [];
44455
44593
  try {
44456
44594
  const result = transition(state3, event);
@@ -44508,12 +44646,12 @@ function redeliverBufferedInbound2(buffer, agent, send, spool) {
44508
44646
  }
44509
44647
 
44510
44648
  // gateway/inbound-delivery-machine-dispatch.ts
44511
- var enabled3 = process.env.SWITCHROOM_DELIVERY_MACHINE_CUTOVER !== "0";
44649
+ var enabled4 = process.env.SWITCHROOM_DELIVERY_MACHINE_CUTOVER !== "0";
44512
44650
  function isDispatchEnabled() {
44513
- return enabled3;
44651
+ return enabled4;
44514
44652
  }
44515
44653
  function dispatchEffects(effects, ctx) {
44516
- if (!enabled3)
44654
+ if (!enabled4)
44517
44655
  return;
44518
44656
  for (const effect of effects) {
44519
44657
  dispatchOne(effect, ctx);
@@ -44822,10 +44960,10 @@ function createPollHealthCheck(options) {
44822
44960
  setIntervalFn = setInterval,
44823
44961
  clearIntervalFn = clearInterval
44824
44962
  } = options;
44825
- let timer2 = null;
44963
+ let timer3 = null;
44826
44964
  let failures = 0;
44827
44965
  let active = false;
44828
- async function tick2() {
44966
+ async function tick3() {
44829
44967
  try {
44830
44968
  await ping();
44831
44969
  if (failures > 0) {
@@ -44849,9 +44987,9 @@ function createPollHealthCheck(options) {
44849
44987
  }
44850
44988
  function doStop() {
44851
44989
  active = false;
44852
- if (timer2 !== null) {
44853
- clearIntervalFn(timer2);
44854
- timer2 = null;
44990
+ if (timer3 !== null) {
44991
+ clearIntervalFn(timer3);
44992
+ timer3 = null;
44855
44993
  }
44856
44994
  }
44857
44995
  return {
@@ -44860,11 +44998,11 @@ function createPollHealthCheck(options) {
44860
44998
  return;
44861
44999
  active = true;
44862
45000
  failures = 0;
44863
- timer2 = setIntervalFn(() => {
44864
- tick2();
45001
+ timer3 = setIntervalFn(() => {
45002
+ tick3();
44865
45003
  }, intervalMs);
44866
- if (typeof timer2?.unref === "function") {
44867
- timer2.unref();
45004
+ if (typeof timer3?.unref === "function") {
45005
+ timer3.unref();
44868
45006
  }
44869
45007
  log(`telegram gateway: poll.health_check started interval=${intervalMs}ms threshold=${failureThreshold}`);
44870
45008
  },
@@ -47603,7 +47741,7 @@ function startIssuesWatcher(opts) {
47603
47741
  log(`issues-watcher: refresh failed: ${err.message}`);
47604
47742
  }
47605
47743
  }
47606
- async function tick2() {
47744
+ async function tick3() {
47607
47745
  if (stopped)
47608
47746
  return;
47609
47747
  const signature = signatureProvider(path);
@@ -47612,25 +47750,25 @@ function startIssuesWatcher(opts) {
47612
47750
  lastSignature = signature;
47613
47751
  await readAndRefresh();
47614
47752
  }
47615
- tick2().catch((err) => {
47753
+ tick3().catch((err) => {
47616
47754
  log(`issues-watcher: initial tick failed: ${err.message}`);
47617
47755
  });
47618
- const timer2 = setIntervalFn(() => {
47619
- tick2().catch((err) => {
47756
+ const timer3 = setIntervalFn(() => {
47757
+ tick3().catch((err) => {
47620
47758
  log(`issues-watcher: tick failed: ${err.message}`);
47621
47759
  });
47622
47760
  }, intervalMs);
47623
- if (typeof timer2.unref === "function") {
47624
- timer2.unref();
47761
+ if (typeof timer3.unref === "function") {
47762
+ timer3.unref();
47625
47763
  }
47626
47764
  return {
47627
47765
  stop() {
47628
47766
  if (stopped)
47629
47767
  return;
47630
47768
  stopped = true;
47631
- clearIntervalFn(timer2);
47769
+ clearIntervalFn(timer3);
47632
47770
  },
47633
- tick: tick2
47771
+ tick: tick3
47634
47772
  };
47635
47773
  }
47636
47774
  function defaultSignatureProvider(path) {
@@ -48016,11 +48154,11 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
48016
48154
  }
48017
48155
 
48018
48156
  // ../src/build-info.ts
48019
- var VERSION = "0.13.13";
48020
- var COMMIT_SHA = "dc583d57";
48021
- var COMMIT_DATE = "2026-05-22T22:02:14+10:00";
48022
- var LATEST_PR = null;
48023
- var COMMITS_AHEAD_OF_TAG = 3;
48157
+ var VERSION = "0.13.14";
48158
+ var COMMIT_SHA = "0cf961a6";
48159
+ var COMMIT_DATE = "2026-05-23T01:15:10Z";
48160
+ var LATEST_PR = 1670;
48161
+ var COMMITS_AHEAD_OF_TAG = 0;
48024
48162
 
48025
48163
  // gateway/boot-version.ts
48026
48164
  function formatRelativeAgo(iso) {
@@ -48119,12 +48257,12 @@ async function rpc2(req, opts) {
48119
48257
  resolve7(val);
48120
48258
  };
48121
48259
  const client3 = new net4.Socket;
48122
- const timer2 = setTimeout(() => {
48260
+ const timer3 = setTimeout(() => {
48123
48261
  client3.destroy();
48124
48262
  settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
48125
48263
  }, timeoutMs);
48126
48264
  client3.on("error", (err) => {
48127
- clearTimeout(timer2);
48265
+ clearTimeout(timer3);
48128
48266
  const code = err.code ?? "ERR";
48129
48267
  let msg;
48130
48268
  if (code === "ENOENT")
@@ -48144,7 +48282,7 @@ async function rpc2(req, opts) {
48144
48282
  `);
48145
48283
  if (newlineIdx !== -1) {
48146
48284
  const line = buffer.slice(0, newlineIdx).trimEnd();
48147
- clearTimeout(timer2);
48285
+ clearTimeout(timer3);
48148
48286
  client3.destroy();
48149
48287
  try {
48150
48288
  const resp = decodeResponse2(line);
@@ -48161,7 +48299,7 @@ async function rpc2(req, opts) {
48161
48299
  try {
48162
48300
  client3.write(encodeRequest2(req));
48163
48301
  } catch (err) {
48164
- clearTimeout(timer2);
48302
+ clearTimeout(timer3);
48165
48303
  client3.destroy();
48166
48304
  settle({
48167
48305
  kind: "unreachable",
@@ -48208,13 +48346,13 @@ async function unlockViaBroker(passphrase, opts) {
48208
48346
  settled = true;
48209
48347
  resolve7(val);
48210
48348
  };
48211
- const timer2 = setTimeout(() => {
48349
+ const timer3 = setTimeout(() => {
48212
48350
  client3.destroy();
48213
48351
  settle({ ok: false, msg: "Timeout waiting for broker" });
48214
48352
  }, timeoutMs);
48215
48353
  const client3 = net4.createConnection({ path: unlockSocketPath });
48216
48354
  client3.on("error", (err) => {
48217
- clearTimeout(timer2);
48355
+ clearTimeout(timer3);
48218
48356
  settle({ ok: false, msg: `Broker unreachable: ${err.message}` });
48219
48357
  });
48220
48358
  let buffer = "";
@@ -48224,7 +48362,7 @@ async function unlockViaBroker(passphrase, opts) {
48224
48362
  `);
48225
48363
  if (newlineIdx !== -1) {
48226
48364
  const line = buffer.slice(0, newlineIdx).trimEnd();
48227
- clearTimeout(timer2);
48365
+ clearTimeout(timer3);
48228
48366
  client3.destroy();
48229
48367
  if (line === "OK") {
48230
48368
  settle({ ok: true });
@@ -49078,17 +49216,17 @@ async function postCompactCard(occ, cap) {
49078
49216
  const messageId = sent?.message_id;
49079
49217
  if (typeof messageId !== "number")
49080
49218
  return;
49081
- const timer2 = setTimeout(() => {
49219
+ const timer3 = setTimeout(() => {
49082
49220
  resolveCompactCard("timeout", null);
49083
49221
  }, COMPACT_CARD_TIMEOUT_MS);
49084
- timer2.unref?.();
49222
+ timer3.unref?.();
49085
49223
  outstandingCompactCard = {
49086
49224
  chatId,
49087
49225
  threadId,
49088
49226
  messageId,
49089
49227
  occAtStart: occ,
49090
49228
  capAtStart: cap,
49091
- timer: timer2
49229
+ timer: timer3
49092
49230
  };
49093
49231
  } catch (err) {
49094
49232
  process.stderr.write(`telegram gateway: proactive-compact start card failed: ${err instanceof Error ? err.message : String(err)}
@@ -49871,6 +50009,7 @@ startTimer({
49871
50009
  lastPtyPreviewByChat.delete(fbKey);
49872
50010
  preambleSuppressor.dropNow();
49873
50011
  endTurn(fbKey);
50012
+ noteTurnEnd(fbKey);
49874
50013
  purgeReactionTracking(fbKey);
49875
50014
  const fbExtraPurge = purgeStaleTurnsForChat(fbChatId, activeTurnStartedAt.keys(), purgeReactionTracking);
49876
50015
  if (turnMatchesFallback && currentTurn === wedgedTurn)
@@ -49884,6 +50023,16 @@ startTimer({
49884
50023
  `);
49885
50024
  }
49886
50025
  });
50026
+ startTimer2({
50027
+ editMessage: async (ctx) => {
50028
+ await swallowingApiCall(() => lockedBot.api.editMessageText(ctx.chatId, ctx.messageId, ctx.newText), {
50029
+ chat_id: ctx.chatId,
50030
+ verb: "pending-progress-edit",
50031
+ ...ctx.threadId != null ? { threadId: ctx.threadId } : {}
50032
+ });
50033
+ },
50034
+ emitMetric: (event) => emitRuntimeMetric(event)
50035
+ });
49887
50036
  var inboundSpool = STATIC ? undefined : createInboundSpool({
49888
50037
  path: join32(STATE_DIR, "inbound-spool.jsonl"),
49889
50038
  fs: {
@@ -49973,18 +50122,18 @@ var ipcServer = createIpcServer({
49973
50122
  process.stderr.write(`telegram gateway: bridge-reconnect: skipping boot card (${dedupeDecision.reason})
49974
50123
  `);
49975
50124
  } else {
49976
- const nowMs = Date.now();
50125
+ const nowMs2 = Date.now();
49977
50126
  const marker = readRestartMarker();
49978
50127
  const cleanMarker = readCleanShutdownMarker(GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH);
49979
50128
  const storedSession = readSessionMarker(GATEWAY_SESSION_MARKER_PATH);
49980
- const markerAgeMs = marker ? nowMs - marker.ts : undefined;
50129
+ const markerAgeMs = marker ? nowMs2 - marker.ts : undefined;
49981
50130
  if (marker) {
49982
50131
  const ageSec = Math.max(1, Math.round((markerAgeMs ?? 0) / 1000));
49983
50132
  process.stderr.write(`telegram gateway: bridge-reconnect: restart-marker present, chat_id=${marker.chat_id} age=${ageSec}s agent=${client3.agentName}
49984
50133
  `);
49985
50134
  clearRestartMarker();
49986
50135
  }
49987
- const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs });
50136
+ const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs2 });
49988
50137
  const target = resolveBootChatId(marker, markerAgeMs);
49989
50138
  if (target) {
49990
50139
  const { chatId, threadId, ackMsgId } = target;
@@ -50113,6 +50262,10 @@ ${reminder}
50113
50262
  if (ev.toolUseId != null && ev.toolUseId.length > 0 && !isTelegramSurfaceTool(ev.toolName)) {
50114
50263
  const label = toolLabel(ev.toolName, ev.input, undefined, ev.precomputedLabel);
50115
50264
  noteToolStart(key, ev.toolUseId, ev.toolName, label.length > 0 ? label : null, Date.now());
50265
+ const evInput = ev.input;
50266
+ if (ev.toolName === "Agent" || ev.toolName === "Task" || ev.toolName === "Bash" && evInput?.run_in_background === true) {
50267
+ noteAsyncDispatch(key);
50268
+ }
50116
50269
  }
50117
50270
  } else if (ev.kind === "tool_result") {
50118
50271
  if (ev.toolUseId != null && ev.toolUseId.length > 0) {
@@ -50649,6 +50802,15 @@ ${url}`;
50649
50802
  rememberAgentButtonMeta(chat_id, keyboardMsgId, replyButtonMeta);
50650
50803
  }
50651
50804
  }
50805
+ if (sentIds.length === chunks.length && chunks.length > 0) {
50806
+ const anchorMsgId = sentIds[chunks.length - 1];
50807
+ if (typeof anchorMsgId === "number") {
50808
+ noteOutbound3(statusKey(chat_id, threadId), {
50809
+ messageId: anchorMsgId,
50810
+ text: chunks[chunks.length - 1]
50811
+ });
50812
+ }
50813
+ }
50652
50814
  const allPhotos = files.length >= 2 && files.length <= 10 && files.every((f) => PHOTO_EXTS.has(extname(f).toLowerCase()));
50653
50815
  const replyParams = reply_to != null && replyMode !== "off" ? { reply_parameters: { message_id: reply_to } } : {};
50654
50816
  if (allPhotos) {
@@ -50835,6 +50997,10 @@ async function executeStreamReply(args) {
50835
50997
  const sChatId = args.chat_id;
50836
50998
  const sThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
50837
50999
  outboundDedup.record(sChatId, sThreadId, args.text, Date.now());
51000
+ noteOutbound3(statusKey(sChatId, sThreadId), {
51001
+ messageId: result.messageId,
51002
+ text: args.text
51003
+ });
50838
51004
  }
50839
51005
  if (turn != null && isFinalAnswerReply({
50840
51006
  text: args.text ?? "",
@@ -50996,7 +51162,7 @@ async function executeAskUser(rawArgs) {
50996
51162
  return { content: [{ type: "text", text: JSON.stringify({ kind: "cancelled", reason: `send-failed: ${msg}` }) }] };
50997
51163
  }
50998
51164
  return new Promise((resolveTool) => {
50999
- const timer2 = setTimeout(() => {
51165
+ const timer3 = setTimeout(() => {
51000
51166
  const entry = pendingAskUser.get(askId);
51001
51167
  pendingAskUser.delete(askId);
51002
51168
  if (!entry)
@@ -51016,7 +51182,7 @@ async function executeAskUser(rawArgs) {
51016
51182
  threadId,
51017
51183
  messageId,
51018
51184
  startedAt: Date.now(),
51019
- timer: timer2,
51185
+ timer: timer3,
51020
51186
  resolve: (outcome) => {
51021
51187
  resolveTool({ content: [{ type: "text", text: JSON.stringify(outcome) }] });
51022
51188
  }
@@ -51580,6 +51746,10 @@ function handleSessionEvent(ev) {
51580
51746
  switch (ev.kind) {
51581
51747
  case "enqueue": {
51582
51748
  typingWrapper.drainAll();
51749
+ if (ev.chatId) {
51750
+ const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
51751
+ clearPending(statusKey(ev.chatId, enqThreadId), "handback");
51752
+ }
51583
51753
  if (ev.chatId) {
51584
51754
  const prior = currentTurn;
51585
51755
  if (prior?.answerStream != null) {
@@ -51769,6 +51939,7 @@ function handleSessionEvent(ev) {
51769
51939
  ctrl.setError();
51770
51940
  purgeReactionTracking(ceKey);
51771
51941
  endTurn(ceKey);
51942
+ noteTurnEnd(ceKey);
51772
51943
  if (turn.answerStream != null) {
51773
51944
  turn.answerStream.stop();
51774
51945
  turn.answerStream = null;
@@ -51881,6 +52052,7 @@ function handleSessionEvent(ev) {
51881
52052
  } catch {}
51882
52053
  clear(tKey);
51883
52054
  endTurn(tKey);
52055
+ noteTurnEnd(tKey);
51884
52056
  }
51885
52057
  lastPtyPreviewByChat.delete(statusKey(chatId, threadId));
51886
52058
  pendingPtyPartial = null;
@@ -51907,6 +52079,7 @@ function handleSessionEvent(ev) {
51907
52079
  const tKey = statusKey(chatId, threadId);
51908
52080
  clear(tKey);
51909
52081
  endTurn(tKey);
52082
+ noteTurnEnd(tKey);
51910
52083
  }
51911
52084
  (async () => {
51912
52085
  await new Promise((resolve8) => setTimeout(resolve8, 500));
@@ -52050,6 +52223,7 @@ function handleSessionEvent(ev) {
52050
52223
  }
52051
52224
  clear(tKey);
52052
52225
  endTurn(tKey);
52226
+ noteTurnEnd(tKey);
52053
52227
  }
52054
52228
  lastPtyPreviewByChat.delete(statusKey(chatId, threadId));
52055
52229
  pendingPtyPartial = null;
@@ -52749,6 +52923,7 @@ ${preBlock(write.output)}`;
52749
52923
  logStreamingEvent({ kind: "inbound_ack", chatId: chat_id, messageId: msgId, ackDelayMs: Date.now() - inboundReceivedAt });
52750
52924
  reset(statusKey(chat_id, messageThreadId), Date.now());
52751
52925
  startTurn(statusKey(chat_id, messageThreadId), Date.now());
52926
+ clearPending(statusKey(chat_id, messageThreadId), "inbound");
52752
52927
  startTurnTypingLoop(chat_id);
52753
52928
  emitRuntimeMetric({
52754
52929
  kind: "turn_started",
@@ -57142,21 +57317,21 @@ var didOneTimeSetup = false;
57142
57317
  }
57143
57318
  } catch {}
57144
57319
  try {
57145
- const nowMs = Date.now();
57320
+ const nowMs2 = Date.now();
57146
57321
  const marker = readRestartMarker();
57147
57322
  const cleanMarker = readCleanShutdownMarker(GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH);
57148
57323
  const currentSession = { pid: process.pid, startedAtMs: GATEWAY_STARTED_AT_MS };
57149
57324
  const storedSession = readSessionMarker(GATEWAY_SESSION_MARKER_PATH);
57150
57325
  const isRealRestart = shouldFireRestartBanner({ stored: storedSession, current: currentSession });
57151
57326
  if (cleanMarker) {
57152
- const ageSec = Math.max(0, Math.round((nowMs - cleanMarker.ts) / 1000));
57327
+ const ageSec = Math.max(0, Math.round((nowMs2 - cleanMarker.ts) / 1000));
57153
57328
  const reasonTag = cleanMarker.reason ? ` reason=${JSON.stringify(cleanMarker.reason)}` : "";
57154
- const cleanFresh = shouldSuppressRecoveryBanner(cleanMarker, nowMs, DEFAULT_MAX_AGE_MS);
57329
+ const cleanFresh = shouldSuppressRecoveryBanner(cleanMarker, nowMs2, DEFAULT_MAX_AGE_MS);
57155
57330
  const isOperatorMarker = typeof cleanMarker.reason === "string" && cleanMarker.reason.startsWith("operator:");
57156
57331
  if (cleanFresh) {
57157
57332
  process.stderr.write(`telegram gateway: boot.clean_shutdown_detected age=${ageSec}s signal=${cleanMarker.signal}${reasonTag}
57158
57333
  `);
57159
- } else if (isOperatorMarker && nowMs - cleanMarker.ts < 300000) {
57334
+ } else if (isOperatorMarker && nowMs2 - cleanMarker.ts < 300000) {
57160
57335
  process.stderr.write(`telegram gateway: boot.clean_shutdown_marker_fresh_extended age=${ageSec}s signal=${cleanMarker.signal}${reasonTag} window=5min
57161
57336
  `);
57162
57337
  } else {
@@ -57165,7 +57340,7 @@ var didOneTimeSetup = false;
57165
57340
  }
57166
57341
  }
57167
57342
  if (marker) {
57168
- const ageMs = nowMs - marker.ts;
57343
+ const ageMs = nowMs2 - marker.ts;
57169
57344
  const ageSec = Math.max(1, Math.round(ageMs / 1000));
57170
57345
  process.stderr.write(`telegram gateway: boot: restart-marker present, chat_id=${marker.chat_id} age=${ageSec}s within5min=${ageMs < 300000}
57171
57346
  `);
@@ -57175,11 +57350,11 @@ var didOneTimeSetup = false;
57175
57350
  process.stderr.write(`telegram gateway: boot: suppressed boot card \u2014 session marker matches current process (pid=${process.pid} startedAt=${GATEWAY_STARTED_AT_MS})
57176
57351
  `);
57177
57352
  } else {
57178
- const markerAgeMs = marker ? nowMs - marker.ts : undefined;
57179
- const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs });
57353
+ const markerAgeMs = marker ? nowMs2 - marker.ts : undefined;
57354
+ const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs2 });
57180
57355
  const target = resolveBootChatId(marker, markerAgeMs);
57181
57356
  if (reason === "crash") {
57182
- const cleanMarkerStale = cleanMarker ? !shouldSuppressRecoveryBanner(cleanMarker, nowMs, DEFAULT_MAX_AGE_MS) : false;
57357
+ const cleanMarkerStale = cleanMarker ? !shouldSuppressRecoveryBanner(cleanMarker, nowMs2, DEFAULT_MAX_AGE_MS) : false;
57183
57358
  const supervisor = process.env.SWITCHROOM_RUNTIME === "docker" ? "docker compose" : "systemd";
57184
57359
  const detailParts = [`gateway crashed and was auto-restarted by ${supervisor}`];
57185
57360
  if (cleanMarker?.signal)