switchroom 0.14.29 → 0.14.30

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.
@@ -49420,8 +49420,8 @@ var {
49420
49420
  } = import__.default;
49421
49421
 
49422
49422
  // src/build-info.ts
49423
- var VERSION = "0.14.29";
49424
- var COMMIT_SHA = "33f6300c";
49423
+ var VERSION = "0.14.30";
49424
+ var COMMIT_SHA = "84f50bd2";
49425
49425
 
49426
49426
  // src/cli/agent.ts
49427
49427
  init_source();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchroom",
3
- "version": "0.14.29",
3
+ "version": "0.14.30",
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": {
@@ -38932,7 +38932,7 @@ var EDIT_INTERVAL_MS = 60000;
38932
38932
  var POLL_INTERVAL_MS = 5000;
38933
38933
  var MAX_LIFETIME_MS = 30 * 60000;
38934
38934
  var TELEGRAM_MSG_CAP2 = 4000;
38935
- var SUFFIX_RE = /\n\n\u2014 still working \(\d+m\)$/;
38935
+ var SUFFIX_RE = /\n\n\u2014 still working \(\d+m\)( \u00b7 message me anytime, I'll keep you posted)?$/;
38936
38936
  var stateByKey = new Map;
38937
38937
  var timer2 = null;
38938
38938
  var activeDeps2 = null;
@@ -39044,7 +39044,7 @@ function tick2(now) {
39044
39044
  const minutes = Math.max(1, Math.round(elapsed / 60000));
39045
39045
  const suffix = `
39046
39046
 
39047
- \u2014 still working (${minutes}m)`;
39047
+ \u2014 still working (${minutes}m) \u00b7 message me anytime, I'll keep you posted`;
39048
39048
  const newText = s.anchorOriginalText + suffix;
39049
39049
  if (newText.length > TELEGRAM_MSG_CAP2) {
39050
39050
  s.lastEditAt = now;
@@ -51659,10 +51659,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
51659
51659
  }
51660
51660
 
51661
51661
  // ../src/build-info.ts
51662
- var VERSION = "0.14.29";
51663
- var COMMIT_SHA = "33f6300c";
51664
- var COMMIT_DATE = "2026-06-01T04:54:34Z";
51665
- var LATEST_PR = 2052;
51662
+ var VERSION = "0.14.30";
51663
+ var COMMIT_SHA = "84f50bd2";
51664
+ var COMMIT_DATE = "2026-06-01T06:07:00Z";
51665
+ var LATEST_PR = 2058;
51666
51666
  var COMMITS_AHEAD_OF_TAG = 0;
51667
51667
 
51668
51668
  // gateway/boot-version.ts
@@ -35,7 +35,11 @@
35
35
  * turn_end with pending+anchor → activate the timer for the key
36
36
  * tick (every 5s, edit every → editMessageText against the anchor
37
37
  * EDIT_INTERVAL_MS) appending/refreshing the suffix
38
- * " — still working (Nm)"
38
+ * " — still working (Nm) · message me
39
+ * anytime, I'll keep you posted"
40
+ * (the reachability clause signals the
41
+ * agent is still listening while a
42
+ * background worker runs — issue PR3)
39
43
  * inbound user message → clear (user re-engaged or moved on)
40
44
  * subagent_handback inject → clear (model about to re-engage)
41
45
  * MAX_LIFETIME_MS budget cap → clear (give up; 30 min default)
@@ -70,10 +74,13 @@ export const TELEGRAM_MSG_CAP = 4000
70
74
  /**
71
75
  * Regex matching the suffix we append. Used to strip a prior suffix
72
76
  * before appending the next one. The (\d+) covers "1m" / "12m" / etc.
77
+ * The reachability clause is optional so anchors carrying a pre-v0.14.30
78
+ * suffix (no clause) are still stripped during a rolling upgrade.
73
79
  * Kept anchored to end-of-string so it only matches OUR suffix, not
74
80
  * something the model happened to write.
75
81
  */
76
- const SUFFIX_RE = /\n\n— still working \(\d+m\)$/
82
+ const SUFFIX_RE =
83
+ /\n\n— still working \(\d+m\)( · message me anytime, I'll keep you posted)?$/
77
84
 
78
85
  export interface PendingProgressEditCtx {
79
86
  chatId: string
@@ -380,7 +387,7 @@ function tick(now: number): void {
380
387
  // user-visible counter reads honestly (we only edit at intervals
381
388
  // ≥ EDIT_INTERVAL_MS = 60s).
382
389
  const minutes = Math.max(1, Math.round(elapsed / 60_000))
383
- const suffix = `\n\n— still working (${minutes}m)`
390
+ const suffix = `\n\n— still working (${minutes}m) · message me anytime, I'll keep you posted`
384
391
  const newText = s.anchorOriginalText + suffix
385
392
 
386
393
  if (newText.length > TELEGRAM_MSG_CAP) {
@@ -139,7 +139,7 @@ describe('pending-work-progress', () => {
139
139
  expect(cap.edits).toHaveLength(1)
140
140
  expect(cap.edits[0].messageId).toBe(100)
141
141
  expect(cap.edits[0].newText).toBe(
142
- 'Background sleep running; awaiting completion.\n\n— still working (1m)',
142
+ "Background sleep running; awaiting completion.\n\n— still working (1m) · message me anytime, I'll keep you posted",
143
143
  )
144
144
 
145
145
  // Tick at 3 intervals total — second edit, "3m".
@@ -148,7 +148,7 @@ describe('pending-work-progress', () => {
148
148
  await flush()
149
149
  expect(cap.edits).toHaveLength(2)
150
150
  expect(cap.edits[1].newText).toBe(
151
- 'Background sleep running; awaiting completion.\n\n— still working (3m)',
151
+ "Background sleep running; awaiting completion.\n\n— still working (3m) · message me anytime, I'll keep you posted",
152
152
  )
153
153
  })
154
154
 
@@ -168,7 +168,25 @@ describe('pending-work-progress', () => {
168
168
  await flush()
169
169
  // The new edit should be based on 'worker dispatched' alone.
170
170
  expect(cap.edits[0].newText).toBe(
171
- 'worker dispatched\n\n— still working (1m)',
171
+ "worker dispatched\n\n— still working (1m) · message me anytime, I'll keep you posted",
172
+ )
173
+ })
174
+
175
+ it('strips a prior NEW-shape suffix (with reachability clause) too', async () => {
176
+ const cap = setup()
177
+ startTurn(KEY)
178
+ noteAsyncDispatch(KEY)
179
+ noteOutbound(KEY, {
180
+ messageId: 100,
181
+ text:
182
+ "worker dispatched\n\n— still working (12m) · message me anytime, I'll keep you posted",
183
+ })
184
+ noteTurnEnd(KEY)
185
+ cap.now = EDIT_INTERVAL_MS
186
+ __tickForTests(cap.now)
187
+ await flush()
188
+ expect(cap.edits[0].newText).toBe(
189
+ "worker dispatched\n\n— still working (1m) · message me anytime, I'll keep you posted",
172
190
  )
173
191
  })
174
192
 
@@ -338,7 +356,7 @@ describe('pending-work-progress', () => {
338
356
  expect(cap.edits).toHaveLength(1)
339
357
  expect(cap.edits[0].parseMode).toBe('HTML')
340
358
  expect(cap.edits[0].newText).toBe(
341
- '<b>Worker back.</b> Both blockers fixed.\n\n— still working (1m)',
359
+ "<b>Worker back.</b> Both blockers fixed.\n\n— still working (1m) · message me anytime, I'll keep you posted",
342
360
  )
343
361
  })
344
362
 
@@ -81,7 +81,8 @@ interface TrailEntry {
81
81
  text: string;
82
82
  }
83
83
 
84
- const SUFFIX_RE = /\n\n— still working \(\d+m\)$/;
84
+ const SUFFIX_RE =
85
+ /\n\n— still working \(\d+m\)( · message me anytime, I'll keep you posted)?$/;
85
86
 
86
87
  function pad(s: string, n: number): string {
87
88
  return s.length >= n ? s : s + " ".repeat(n - s.length);
@@ -40,7 +40,8 @@ const PROMPT =
40
40
  `the bash, send that one HTML reply, end your turn. When it finishes ` +
41
41
  `much later, reply with the single word "done".`;
42
42
 
43
- const SUFFIX_RE = /\n\n— still working \(\d+m\)$/;
43
+ const SUFFIX_RE =
44
+ /\n\n— still working \(\d+m\)( · message me anytime, I'll keep you posted)?$/;
44
45
 
45
46
  describe("uat: pending-progress edit preserves HTML formatting (#1698 regression gate)", () => {
46
47
  it(