polygram 0.12.0-rc.17 → 0.12.0-rc.19

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.
package/lib/claude-bin.js CHANGED
@@ -7,7 +7,14 @@ const fs = require('fs');
7
7
  // 0.12 Phase 4: moved from lib/process/tmux-process.js into the helper module
8
8
  // that consumes it, so the constant survives TmuxProcess deletion. CliProcess
9
9
  // + spike scripts + polygram boot all import from here now.
10
- const CLAUDE_CLI_PINNED_VERSION = '2.1.142';
10
+ // 0.12.0-rc.18: bumped 2.1.142 → 2.1.158 (latest installed). The dev-channels
11
+ // inbound-message delivery has an intermittent channel-bind race (the bridge
12
+ // pushes user_msg before claude's channel subscription is active → message
13
+ // silently dropped → stuck turn; see docs/0.12.0-known-issues.md). Trying a
14
+ // newer claude to see if the research-preview channels reliability improved,
15
+ // before building polygram-side recovery. Re-validate the channel flow on each
16
+ // bump via tests/e2e-channels-real-claude.test.js.
17
+ const CLAUDE_CLI_PINNED_VERSION = '2.1.158';
11
18
 
12
19
  /**
13
20
  * Resolve + verify the pinned claude CLI binary.
@@ -362,6 +362,29 @@ class CliProcess extends Process {
362
362
  this._startPingLoop();
363
363
  } catch (err) {
364
364
  await this._teardownOnStartFailure();
365
+ // Self-heal an unresumable stale session. A `--resume` spawn that dies
366
+ // during startup means the persisted session can't be resumed cleanly.
367
+ // The production case (shumorobot general chat, session minted 2026-05-27):
368
+ // claude resumes a week-old session → renders the aged-session "Resuming
369
+ // the full session… Resume from summary?" dialog → the gate confirms it →
370
+ // the /compact resume exits code 0 → the gate reports TMUX_SESSION_GONE.
371
+ // Retrying the SAME id loops forever (the chat sat dead for days). Clear it
372
+ // and respawn ONCE with a fresh session: a fresh spawn has no aged-session
373
+ // dialog and starts clean — exactly what made the Music topic healthy once
374
+ // its cwd change minted a new session. The `_freshRetry` guard means a
375
+ // genuine fresh-spawn failure throws instead of looping. onInit re-upserts
376
+ // the fresh id on success, so the dead id leaves the DB without extra wiring.
377
+ if (this._shouldRefreshAfterStartupFailure(err, opts)) {
378
+ this._logEvent('cli-stale-session-reset', {
379
+ stale_session_id: opts.existingSessionId,
380
+ code: err.code,
381
+ });
382
+ this.logger.warn?.(
383
+ `[${this.label}] channels: startup failed resuming ${opts.existingSessionId} `
384
+ + `(${err.code}) — clearing stale session, respawning fresh.`,
385
+ );
386
+ return this.start({ ...opts, existingSessionId: null, _freshRetry: true });
387
+ }
365
388
  throw err;
366
389
  }
367
390
 
@@ -415,6 +438,27 @@ class CliProcess extends Process {
415
438
  }
416
439
  }
417
440
 
441
+ /**
442
+ * Decide whether a start() failure warrants a one-shot fresh-session
443
+ * retry. True only when ALL hold:
444
+ * - we were RESUMING a session (`opts.existingSessionId` set) — a fresh
445
+ * spawn that fails is a real failure, not a poisoned resume;
446
+ * - we haven't already retried (`_freshRetry` guard) — prevents an
447
+ * infinite respawn loop if the fresh spawn also dies;
448
+ * - the failure is a startup-gate death: claude exited
449
+ * (`TMUX_SESSION_GONE`) or the dialog gate timed out
450
+ * (`CHANNELS_DIALOG_TIMEOUT`) — the signature of an unresumable aged
451
+ * session (the "Resume from summary?" /compact path that exits code 0).
452
+ * Any other error (bridge-handshake timeout on a healthy session, config
453
+ * error, etc.) throws as-is.
454
+ */
455
+ _shouldRefreshAfterStartupFailure(err, opts) {
456
+ const code = err && err.code;
457
+ return !!(opts && opts.existingSessionId)
458
+ && !(opts && opts._freshRetry)
459
+ && (code === 'TMUX_SESSION_GONE' || code === 'CHANNELS_DIALOG_TIMEOUT');
460
+ }
461
+
418
462
  /**
419
463
  * M1 refactor: socket-server lifecycle delegated to ChannelsBridgeServer.
420
464
  * This class wires the event surface (bridge-ready, bridge-message,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.12.0-rc.17",
3
+ "version": "0.12.0-rc.19",
4
4
  "description": "Telegram daemon for Claude Code that preserves the OpenClaw per-chat session model. Migration path for OpenClaw users moving to Claude Code.",
5
5
  "main": "lib/ipc/client.js",
6
6
  "bin": {