switchroom 0.16.11 → 0.16.12

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.
@@ -51674,8 +51674,8 @@ import { existsSync, readFileSync } from "node:fs";
51674
51674
  import { dirname, join } from "node:path";
51675
51675
 
51676
51676
  // src/build-info.ts
51677
- var VERSION = "0.16.11";
51678
- var COMMIT_SHA = "7846a496";
51677
+ var VERSION = "0.16.12";
51678
+ var COMMIT_SHA = "4cab85e1";
51679
51679
 
51680
51680
  // src/cli/resolve-version.ts
51681
51681
  function readPackageVersion() {
@@ -77783,6 +77783,13 @@ function extractBearerToken(req) {
77783
77783
  if (idx >= 0 && idx + 1 < parts.length)
77784
77784
  return parts[idx + 1];
77785
77785
  }
77786
+ const url = new URL(req.url);
77787
+ const queryToken = url.searchParams.get("token");
77788
+ if (queryToken)
77789
+ return queryToken;
77790
+ const hermesHeader = req.headers.get("X-Hermes-Session-Token");
77791
+ if (hermesHeader)
77792
+ return hermesHeader;
77786
77793
  return null;
77787
77794
  }
77788
77795
  function checkAuth(req, token, server) {
@@ -22587,7 +22587,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:f
22587
22587
  import { dirname as dirname4, join as join2 } from "node:path";
22588
22588
 
22589
22589
  // src/build-info.ts
22590
- var VERSION = "0.16.11";
22590
+ var VERSION = "0.16.12";
22591
22591
 
22592
22592
  // src/cli/resolve-version.ts
22593
22593
  function readPackageVersion() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchroom",
3
- "version": "0.16.11",
3
+ "version": "0.16.12",
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": {
@@ -50062,7 +50062,8 @@ function buildObligationRepresentInbound(o, now) {
50062
50062
  meta: {
50063
50063
  source: "obligation_represent",
50064
50064
  origin_turn_id: o.originTurnId,
50065
- represent_count: String(o.representCount + 1)
50065
+ represent_count: String(o.representCount + 1),
50066
+ chat_id: o.chatId
50066
50067
  }
50067
50068
  };
50068
50069
  }
@@ -56063,10 +56064,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
56063
56064
  }
56064
56065
 
56065
56066
  // ../src/build-info.ts
56066
- var VERSION = "0.16.11";
56067
- var COMMIT_SHA = "7846a496";
56068
- var COMMIT_DATE = "2026-06-28T09:51:30Z";
56069
- var LATEST_PR = 2630;
56067
+ var VERSION = "0.16.12";
56068
+ var COMMIT_SHA = "4cab85e1";
56069
+ var COMMIT_DATE = "2026-06-28T11:11:09Z";
56070
+ var LATEST_PR = 2634;
56070
56071
  var COMMITS_AHEAD_OF_TAG = 0;
56071
56072
 
56072
56073
  // gateway/boot-version.ts
@@ -60795,9 +60796,20 @@ function redactOutboundText(text2, site) {
60795
60796
  }
60796
60797
  async function executeReply(args) {
60797
60798
  const turn = currentTurn;
60798
- const chat_id = String(args.chat_id ?? "");
60799
- if (!chat_id)
60799
+ const _rawChatId = String(args.chat_id ?? "");
60800
+ if (!_rawChatId)
60800
60801
  throw new Error("reply: chat_id is required");
60802
+ const chat_id = (() => {
60803
+ const _a = loadAccess();
60804
+ if (_a.allowFrom.includes(_rawChatId) || _rawChatId in _a.groups)
60805
+ return _rawChatId;
60806
+ if (turn?.sessionChatId && (_a.allowFrom.includes(turn.sessionChatId) || (turn.sessionChatId in _a.groups))) {
60807
+ process.stderr.write(`telegram gateway: reply: model passed chat_id "${_rawChatId}" (not allowlisted) \u2014 ` + `routing to active turn chat "${turn.sessionChatId}"
60808
+ `);
60809
+ return turn.sessionChatId;
60810
+ }
60811
+ return _rawChatId;
60812
+ })();
60801
60813
  const rawText = args.text;
60802
60814
  if (rawText == null || rawText === "")
60803
60815
  throw new Error("reply: text is required and cannot be empty");
@@ -7972,8 +7972,25 @@ async function executeReply(args: Record<string, unknown>): Promise<{ content: A
7972
7972
  // module-scope currentTurn, which a future refactor could let roll over
7973
7973
  // mid-call.
7974
7974
  const turn = currentTurn
7975
- const chat_id = String(args.chat_id ?? '')
7976
- if (!chat_id) throw new Error('reply: chat_id is required')
7975
+ const _rawChatId = String(args.chat_id ?? '')
7976
+ if (!_rawChatId) throw new Error('reply: chat_id is required')
7977
+ // Non-Claude models (e.g. Gemini via LiteLLM sr-* routing) sometimes pass a
7978
+ // chat_id that is not in the allowlist — either an int/string mismatch, or
7979
+ // the model echoing the wrong identifier from context. When the raw value
7980
+ // fails the allowlist check and the active turn has a validated sessionChatId,
7981
+ // fall back to the turn's origin chat so the reply still lands correctly.
7982
+ const chat_id = (() => {
7983
+ const _a = loadAccess()
7984
+ if (_a.allowFrom.includes(_rawChatId) || _rawChatId in _a.groups) return _rawChatId
7985
+ if (turn?.sessionChatId && (_a.allowFrom.includes(turn.sessionChatId) || turn.sessionChatId in _a.groups)) {
7986
+ process.stderr.write(
7987
+ `telegram gateway: reply: model passed chat_id "${_rawChatId}" (not allowlisted) — ` +
7988
+ `routing to active turn chat "${turn.sessionChatId}"\n`,
7989
+ )
7990
+ return turn.sessionChatId
7991
+ }
7992
+ return _rawChatId // let assertAllowedChat below throw the human-readable error
7993
+ })()
7977
7994
  const rawText = args.text as string | undefined
7978
7995
  if (rawText == null || rawText === '') throw new Error('reply: text is required and cannot be empty')
7979
7996
  let text = repairEscapedWhitespace(rawText)
@@ -381,6 +381,7 @@ export function buildObligationRepresentInbound(o: Obligation, now: number): Inb
381
381
  source: 'obligation_represent',
382
382
  origin_turn_id: o.originTurnId,
383
383
  represent_count: String(o.representCount + 1),
384
+ chat_id: o.chatId,
384
385
  },
385
386
  }
386
387
  }
@@ -184,6 +184,7 @@ describe("buildObligationRepresentInbound", () => {
184
184
  expect(m.meta.origin_turn_id).toBe("-100123:3#715");
185
185
  expect(m.meta.source).toBe("obligation_represent"); // synthetic → not tracked, no new obligation
186
186
  expect(m.meta.represent_count).toBe("1");
187
+ expect(m.meta.chat_id).toBe("-100123"); // Bug C fix: chat_id in meta drives the <channel> tag
187
188
  expect(m.text).toContain("do the Meta report");
188
189
  expect(m.text).toMatch(/answer it now|reply tool/i);
189
190
  });