switchroom 0.19.31 → 0.19.32

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.
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.19.31", COMMIT_SHA = "d24ee3cc";
2123
+ var VERSION = "0.19.32", COMMIT_SHA = "5ccce0a7";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -21210,7 +21210,7 @@ function allocateAgentUid(name) {
21210
21210
  }
21211
21211
 
21212
21212
  // src/build-info.ts
21213
- var VERSION = "0.19.31";
21213
+ var VERSION = "0.19.32";
21214
21214
 
21215
21215
  // src/setup/hindsight-recall-tunables.ts
21216
21216
  var RECALL_DEADLINE_HEADROOM_SECONDS = 2;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "switchroom",
3
3
  "//version": "NOT the release version — source of truth is the git tag, resolved by scripts/build.mjs:resolveVersion() (see CLAUDE.md > Standard release process). This field is stale by design and only the Layer-4 dev/non-tag fallback for build.mjs + src/cli/resolve-version.ts; do NOT bump it expecting a release to pick it up. npm-pack tarball naming needs a real version — do that as an UNCOMMITTED pack-time bump (see release step 6), never a committed one.",
4
- "version": "0.19.31",
4
+ "version": "0.19.32",
5
5
  "description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -23467,6 +23467,9 @@ function projectTranscriptLine(line) {
23467
23467
  if (op === "dequeue") {
23468
23468
  return [{ kind: "dequeue" }];
23469
23469
  }
23470
+ if (op === "remove") {
23471
+ return [{ kind: "queue_remove", rawContent: obj.content ?? "" }];
23472
+ }
23470
23473
  return [];
23471
23474
  }
23472
23475
  if (type === "assistant") {
@@ -71213,6 +71213,9 @@ function projectTranscriptLine(line) {
71213
71213
  if (op === "dequeue") {
71214
71214
  return [{ kind: "dequeue" }];
71215
71215
  }
71216
+ if (op === "remove") {
71217
+ return [{ kind: "queue_remove", rawContent: obj.content ?? "" }];
71218
+ }
71216
71219
  return [];
71217
71220
  }
71218
71221
  if (type === "assistant") {
@@ -79814,6 +79817,195 @@ function decideFallbackTeardownNotice2(input) {
79814
79817
  }
79815
79818
 
79816
79819
  // gateway/stream-render.ts
79820
+ var PARKED_TURN_START_MAX = 16;
79821
+ var PARKED_TURN_START_TTL_MS = 30 * 60000;
79822
+ var parkedTurnStarts = [];
79823
+ function pruneParkedTurnStarts(now) {
79824
+ for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
79825
+ if (now - parkedTurnStarts[i].parkedAt > PARKED_TURN_START_TTL_MS) {
79826
+ const [dropped] = parkedTurnStarts.splice(i, 1);
79827
+ process.stderr.write(`telegram gateway: parked-turn-start expired chat=${dropped.chatId ?? "-"} ` + `msg=${dropped.messageId ?? "-"} age_ms=${now - dropped.parkedAt}
79828
+ `);
79829
+ }
79830
+ }
79831
+ }
79832
+ function parkTurnStart(env, now) {
79833
+ parkedTurnStarts.push({ ...env, parkedAt: now });
79834
+ while (parkedTurnStarts.length > PARKED_TURN_START_MAX) {
79835
+ const [dropped] = parkedTurnStarts.splice(0, 1);
79836
+ process.stderr.write(`telegram gateway: parked-turn-start evicted (cap ${PARKED_TURN_START_MAX}) ` + `chat=${dropped.chatId ?? "-"} msg=${dropped.messageId ?? "-"}
79837
+ `);
79838
+ }
79839
+ }
79840
+ function takeParkedTurnStart() {
79841
+ return parkedTurnStarts.pop() ?? null;
79842
+ }
79843
+ function discardParkedTurnStart(rawContent) {
79844
+ for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
79845
+ if (parkedTurnStarts[i].rawContent === rawContent) {
79846
+ parkedTurnStarts.splice(i, 1);
79847
+ return true;
79848
+ }
79849
+ }
79850
+ return false;
79851
+ }
79852
+ function beginTurn(deps, ev) {
79853
+ const {
79854
+ HANDBACK_PRETURN_ENABLED,
79855
+ STATE_DIR,
79856
+ clearActivitySummary,
79857
+ extractUserPromptPreview,
79858
+ getCurrentTurn,
79859
+ getPendingPtyPartial,
79860
+ handbackPreturnSignal,
79861
+ handlePtyPartial,
79862
+ isDmChatId,
79863
+ makeNarrativeGate,
79864
+ pendingCrossTurnGate,
79865
+ preambleSuppressor,
79866
+ promoteQueuedStatus,
79867
+ rememberRecentTurn,
79868
+ scheduleEarlyLivenessOpen,
79869
+ setCurrentTurn,
79870
+ setPendingPtyPartial,
79871
+ startTurnTypingLoop,
79872
+ statusKey,
79873
+ turnsDb,
79874
+ typingWrapper
79875
+ } = deps;
79876
+ typingWrapper.drainAll();
79877
+ if (ev.chatId) {
79878
+ const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
79879
+ clearPending2(statusKey(ev.chatId, enqThreadId), "handback");
79880
+ }
79881
+ if (ev.chatId) {
79882
+ const prior = getCurrentTurn();
79883
+ if (prior?.answerStream != null) {
79884
+ prior.answerStream.forceNewMessage();
79885
+ prior.answerStream.stop();
79886
+ prior.answerStream = null;
79887
+ }
79888
+ if (prior?.orphanedReplyTimeoutId != null) {
79889
+ clearTimeout(prior.orphanedReplyTimeoutId);
79890
+ prior.orphanedReplyTimeoutId = null;
79891
+ }
79892
+ prior?.narrativeGate?.teardown();
79893
+ if (prior != null && prior.endedAt == null) {
79894
+ clearActivitySummary(prior);
79895
+ process.stderr.write(`telegram gateway: ${formatTurnLifecycle("clear", "superseded", prior, Date.now())}
79896
+ `);
79897
+ }
79898
+ const startedAt = Date.now();
79899
+ const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
79900
+ const turnId = deriveTurnId(ev.chatId, enqThreadIdNum ?? null, ev.messageId) ?? `${chatKey(ev.chatId, enqThreadIdNum ?? null)}#synthetic-${startedAt}`;
79901
+ const xTurnGateKey = turnId;
79902
+ const consumedCrossTurnGate = pendingCrossTurnGate.get(xTurnGateKey);
79903
+ if (consumedCrossTurnGate != null)
79904
+ pendingCrossTurnGate.delete(xTurnGateKey);
79905
+ const next = {
79906
+ sessionChatId: ev.chatId,
79907
+ sessionThreadId: enqThreadIdNum,
79908
+ sourceMessageId: parseSourceMessageId(ev.messageId),
79909
+ startedAt,
79910
+ gatewayReceiveAt: startedAt,
79911
+ role: deriveTurnRole(ev.rawContent),
79912
+ ...consumedCrossTurnGate != null ? { crossTurnGate: consumedCrossTurnGate } : {},
79913
+ replyCalled: false,
79914
+ finalAnswerDelivered: false,
79915
+ finalAnswerSubstantive: false,
79916
+ finalAnswerEverDelivered: false,
79917
+ answerDelivered: false,
79918
+ flushedAnswerText: null,
79919
+ endedAt: null,
79920
+ firstPingAt: null,
79921
+ firstPingWasSubstantive: false,
79922
+ silentAnchorMessageId: null,
79923
+ silentAnchorText: "",
79924
+ capturedText: [],
79925
+ capturedBlockMeta: [],
79926
+ orphanedReplyTimeoutId: null,
79927
+ answerReadyFlushTimeoutId: null,
79928
+ liveness: new LivenessTracker(startedAt),
79929
+ turnId,
79930
+ registryKey: null,
79931
+ noReplyDrainTimer: null,
79932
+ lastAssistantMsgId: null,
79933
+ lastAssistantDone: false,
79934
+ toolCallCount: 0,
79935
+ labeledToolCount: 0,
79936
+ totalTokens: 0,
79937
+ seenUsageMessageIds: new Set,
79938
+ activityMessageId: null,
79939
+ activityInFlight: null,
79940
+ activityPendingRender: null,
79941
+ activityLastSentRender: null,
79942
+ activityEverOpened: false,
79943
+ activityDrainFailures: 0,
79944
+ mirrorLines: [],
79945
+ narrativeGate: undefined,
79946
+ lastReplyText: "",
79947
+ foregroundSubAgents: new Map,
79948
+ answerStream: null,
79949
+ isDm: isDmChatId(ev.chatId),
79950
+ emissionAuthority: new EmissionAuthority(statusKey(ev.chatId, enqThreadIdNum))
79951
+ };
79952
+ next.narrativeGate = makeNarrativeGate(next);
79953
+ if (HANDBACK_PRETURN_ENABLED) {
79954
+ const handbackAdoption = handbackPreturnSignal.tryAdopt(turnId);
79955
+ if (handbackAdoption != null) {
79956
+ if (handbackAdoption.activityMessageId != null) {
79957
+ next.activityMessageId = handbackAdoption.activityMessageId;
79958
+ next.activityEverOpened = true;
79959
+ }
79960
+ process.stderr.write(`telegram gateway: handback pre-turn adopted turnId=${turnId} ` + `key=${handbackAdoption.statusKey} card=${handbackAdoption.activityMessageId ?? "none"}
79961
+ `);
79962
+ }
79963
+ }
79964
+ startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
79965
+ setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
79966
+ scheduleEarlyLivenessOpen(next);
79967
+ process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
79968
+ `);
79969
+ rememberRecentTurn(next);
79970
+ promoteQueuedStatus(ev.chatId, enqThreadIdNum);
79971
+ shadowEmit({
79972
+ kind: "turnStart",
79973
+ key: statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : undefined),
79974
+ at: startedAt
79975
+ });
79976
+ preambleSuppressor.reset();
79977
+ clearSilentEndState2(statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null));
79978
+ if (turnsDb != null) {
79979
+ const evThreadIdNum = ev.threadId != null ? Number(ev.threadId) : null;
79980
+ const turnKey3 = chatKeyWithSuffix(ev.chatId, evThreadIdNum, String(startedAt));
79981
+ next.registryKey = turnKey3;
79982
+ const userPromptPreview = extractUserPromptPreview(ev.rawContent);
79983
+ try {
79984
+ recordTurnStart(turnsDb, {
79985
+ turnKey: turnKey3,
79986
+ chatId: String(ev.chatId),
79987
+ threadId: ev.threadId != null ? String(ev.threadId) : null,
79988
+ lastUserMsgId: ev.messageId != null ? String(ev.messageId) : null,
79989
+ userPromptPreview
79990
+ });
79991
+ } catch (err) {
79992
+ process.stderr.write(`telegram gateway: recordTurnStart failed turnKey=${turnKey3}: ${err.message}
79993
+ `);
79994
+ }
79995
+ writeTurnActiveMarker(STATE_DIR, {
79996
+ turnKey: turnKey3,
79997
+ chatId: String(ev.chatId),
79998
+ threadId: ev.threadId != null ? String(ev.threadId) : null,
79999
+ startedAt
80000
+ });
80001
+ }
80002
+ const pending = getPendingPtyPartial();
80003
+ if (pending != null) {
80004
+ setPendingPtyPartial(null);
80005
+ handlePtyPartial(pending);
80006
+ }
80007
+ }
80008
+ }
79817
80009
  function handleSessionEvent(deps, ev) {
79818
80010
  const {
79819
80011
  ANSWER_LANE,
@@ -79910,140 +80102,47 @@ function handleSessionEvent(deps, ev) {
79910
80102
  }
79911
80103
  switch (ev.kind) {
79912
80104
  case "enqueue": {
79913
- typingWrapper.drainAll();
79914
- if (ev.chatId) {
79915
- const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
79916
- clearPending2(statusKey(ev.chatId, enqThreadId), "handback");
79917
- }
80105
+ const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
80106
+ const now = Date.now();
79918
80107
  if (ev.chatId) {
79919
- const prior = getCurrentTurn();
79920
- if (prior?.answerStream != null) {
79921
- prior.answerStream.forceNewMessage();
79922
- prior.answerStream.stop();
79923
- prior.answerStream = null;
79924
- }
79925
- if (prior?.orphanedReplyTimeoutId != null) {
79926
- clearTimeout(prior.orphanedReplyTimeoutId);
79927
- prior.orphanedReplyTimeoutId = null;
79928
- }
79929
- prior?.narrativeGate?.teardown();
79930
- const startedAt = Date.now();
79931
- const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
79932
- const turnId = deriveTurnId(ev.chatId, enqThreadIdNum ?? null, ev.messageId) ?? `${chatKey(ev.chatId, enqThreadIdNum ?? null)}#synthetic-${startedAt}`;
79933
- const xTurnGateKey = turnId;
79934
- const consumedCrossTurnGate = pendingCrossTurnGate.get(xTurnGateKey);
79935
- if (consumedCrossTurnGate != null)
79936
- pendingCrossTurnGate.delete(xTurnGateKey);
79937
- const next = {
79938
- sessionChatId: ev.chatId,
79939
- sessionThreadId: enqThreadIdNum,
79940
- sourceMessageId: parseSourceMessageId(ev.messageId),
79941
- startedAt,
79942
- gatewayReceiveAt: startedAt,
79943
- role: deriveTurnRole(ev.rawContent),
79944
- ...consumedCrossTurnGate != null ? { crossTurnGate: consumedCrossTurnGate } : {},
79945
- replyCalled: false,
79946
- finalAnswerDelivered: false,
79947
- finalAnswerSubstantive: false,
79948
- finalAnswerEverDelivered: false,
79949
- answerDelivered: false,
79950
- flushedAnswerText: null,
79951
- endedAt: null,
79952
- firstPingAt: null,
79953
- firstPingWasSubstantive: false,
79954
- silentAnchorMessageId: null,
79955
- silentAnchorText: "",
79956
- capturedText: [],
79957
- capturedBlockMeta: [],
79958
- orphanedReplyTimeoutId: null,
79959
- answerReadyFlushTimeoutId: null,
79960
- liveness: new LivenessTracker(startedAt),
79961
- turnId,
79962
- registryKey: null,
79963
- noReplyDrainTimer: null,
79964
- lastAssistantMsgId: null,
79965
- lastAssistantDone: false,
79966
- toolCallCount: 0,
79967
- labeledToolCount: 0,
79968
- totalTokens: 0,
79969
- seenUsageMessageIds: new Set,
79970
- activityMessageId: null,
79971
- activityInFlight: null,
79972
- activityPendingRender: null,
79973
- activityLastSentRender: null,
79974
- activityEverOpened: false,
79975
- activityDrainFailures: 0,
79976
- mirrorLines: [],
79977
- narrativeGate: undefined,
79978
- lastReplyText: "",
79979
- foregroundSubAgents: new Map,
79980
- answerStream: null,
79981
- isDm: isDmChatId(ev.chatId),
79982
- emissionAuthority: new EmissionAuthority(statusKey(ev.chatId, enqThreadIdNum))
79983
- };
79984
- next.narrativeGate = makeNarrativeGate(next);
79985
- if (HANDBACK_PRETURN_ENABLED) {
79986
- const handbackAdoption = handbackPreturnSignal.tryAdopt(turnId);
79987
- if (handbackAdoption != null) {
79988
- if (handbackAdoption.activityMessageId != null) {
79989
- next.activityMessageId = handbackAdoption.activityMessageId;
79990
- next.activityEverOpened = true;
79991
- }
79992
- process.stderr.write(`telegram gateway: handback pre-turn adopted turnId=${turnId} ` + `key=${handbackAdoption.statusKey} card=${handbackAdoption.activityMessageId ?? "none"}
79993
- `);
79994
- }
79995
- }
79996
- startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
79997
- setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
79998
- scheduleEarlyLivenessOpen(next);
79999
- process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
80000
- `);
80001
- rememberRecentTurn(next);
80002
- promoteQueuedStatus(ev.chatId, enqThreadIdNum);
80003
80108
  if (DELIVERY_CONFIRM_ENABLED) {
80004
80109
  ackDelivery(deliveryQueue, chatKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId, ev.rawContent);
80005
80110
  }
80006
- shadowEmit({
80007
- kind: "turnStart",
80008
- key: statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : undefined),
80009
- at: startedAt
80010
- });
80011
- preambleSuppressor.reset();
80012
- clearSilentEndState2(statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null));
80013
- if (turnsDb != null) {
80014
- const evThreadIdNum = ev.threadId != null ? Number(ev.threadId) : null;
80015
- const turnKey3 = chatKeyWithSuffix(ev.chatId, evThreadIdNum, String(startedAt));
80016
- next.registryKey = turnKey3;
80017
- const userPromptPreview = extractUserPromptPreview(ev.rawContent);
80018
- try {
80019
- recordTurnStart(turnsDb, {
80020
- turnKey: turnKey3,
80021
- chatId: String(ev.chatId),
80022
- threadId: ev.threadId != null ? String(ev.threadId) : null,
80023
- lastUserMsgId: ev.messageId != null ? String(ev.messageId) : null,
80024
- userPromptPreview
80025
- });
80026
- } catch (err) {
80027
- process.stderr.write(`telegram gateway: recordTurnStart failed turnKey=${turnKey3}: ${err.message}
80028
- `);
80029
- }
80030
- writeTurnActiveMarker(STATE_DIR, {
80031
- turnKey: turnKey3,
80032
- chatId: String(ev.chatId),
80033
- threadId: ev.threadId != null ? String(ev.threadId) : null,
80034
- startedAt
80035
- });
80036
- }
80037
- const pending = getPendingPtyPartial();
80038
- if (pending != null) {
80039
- setPendingPtyPartial(null);
80040
- handlePtyPartial(pending);
80041
- }
80042
80111
  }
80112
+ pruneParkedTurnStarts(now);
80113
+ if (!ev.chatId) {
80114
+ beginTurn(deps, ev);
80115
+ return;
80116
+ }
80117
+ const live = getCurrentTurn();
80118
+ const sessionBusy = live != null && live.endedAt == null;
80119
+ if (!sessionBusy && parkedTurnStarts.length === 0) {
80120
+ beginTurn(deps, ev);
80121
+ return;
80122
+ }
80123
+ parkTurnStart({
80124
+ chatId: ev.chatId,
80125
+ messageId: ev.messageId,
80126
+ threadId: ev.threadId,
80127
+ rawContent: ev.rawContent
80128
+ }, now);
80129
+ process.stderr.write(`telegram gateway: turn-start parked (session busy) chat=${ev.chatId} ` + `thread=${enqThreadIdNum ?? "-"} msg=${ev.messageId ?? "-"} ` + `parked=${parkedTurnStarts.length}
80130
+ `);
80043
80131
  return;
80044
80132
  }
80045
- case "dequeue":
80133
+ case "dequeue": {
80134
+ pruneParkedTurnStarts(Date.now());
80135
+ const parked = takeParkedTurnStart();
80136
+ if (parked == null)
80137
+ return;
80138
+ beginTurn(deps, parked);
80046
80139
  return;
80140
+ }
80141
+ case "queue_remove": {
80142
+ pruneParkedTurnStarts(Date.now());
80143
+ discardParkedTurnStart(ev.rawContent);
80144
+ return;
80145
+ }
80047
80146
  case "model": {
80048
80147
  const turn = getCurrentTurn();
80049
80148
  if (turn != null) {
@@ -99070,10 +99169,10 @@ function startOutboxSweep(deps) {
99070
99169
  }
99071
99170
 
99072
99171
  // ../src/build-info.ts
99073
- var VERSION2 = "0.19.31";
99074
- var COMMIT_SHA = "d24ee3cc";
99075
- var COMMIT_DATE = "2026-07-28T23:06:42Z";
99076
- var LATEST_PR = 3961;
99172
+ var VERSION2 = "0.19.32";
99173
+ var COMMIT_SHA = "5ccce0a7";
99174
+ var COMMIT_DATE = "2026-07-28T23:48:52Z";
99175
+ var LATEST_PR = 3962;
99077
99176
  var COMMITS_AHEAD_OF_TAG = 0;
99078
99177
 
99079
99178
  // gateway/boot-version.ts
@@ -17551,6 +17551,9 @@ function projectTranscriptLine(line) {
17551
17551
  if (op === "dequeue") {
17552
17552
  return [{ kind: "dequeue" }];
17553
17553
  }
17554
+ if (op === "remove") {
17555
+ return [{ kind: "queue_remove", rawContent: obj.content ?? "" }];
17556
+ }
17554
17557
  return [];
17555
17558
  }
17556
17559
  if (type === "assistant") {