polygram 0.10.0-rc.30 → 0.10.0-rc.31

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "polygram",
4
- "version": "0.10.0-rc.30",
4
+ "version": "0.10.0-rc.31",
5
5
  "description": "Telegram integration for Claude Code that preserves the OpenClaw per-chat session model. Migration target for OpenClaw users. Multi-bot, multi-chat, per-topic isolation; SQLite transcripts; inline-keyboard approvals. Bundles /polygram:status|logs|pair-code|approvals admin commands plus history (transcript queries) and polygram-send (out-of-turn IPC sends with file-upload validation) skills.",
6
6
  "keywords": [
7
7
  "telegram",
@@ -496,6 +496,54 @@ class TmuxProcess extends Process {
496
496
  throw Object.assign(new Error(binCheck.reason), { code: 'CLAUDE_BIN_MISSING' });
497
497
  }
498
498
 
499
+ // Spawn-time reconcile (shumorobot 2026-05-20 18:51 incident).
500
+ // A tmux session with our name may already exist on the host
501
+ // BEFORE we spawn — sources:
502
+ // - pm_backend drift: chat config flipped tmux → sdk → tmux;
503
+ // polygram dropped its in-memory handle but the previous
504
+ // daemon's tmux session is still running headless.
505
+ // - Boot-sweep raced a concurrent operator (already noted in
506
+ // lib/tmux/orphan-sweep.js header).
507
+ // - Crash mid-spawn: the spawn() call landed but the sessionCreated
508
+ // teardown below was bypassed (SIGKILL).
509
+ // The orphan TUI inside is headless — its parent daemon (or any
510
+ // previous attach) is dead, its --debug-file isn't being tailed,
511
+ // and any JSONL it writes is unrouted. The live claude session
512
+ // ID inside is NOT recoverable from outside without probing the
513
+ // pane (flaky) — and we don't need to: claude's per-session JSONL
514
+ // lives in ~/.claude/projects/.../<sid>.jsonl independent of
515
+ // tmux, so a fresh --resume into a clean pane recovers the
516
+ // conversation cleanly.
517
+ //
518
+ // Strategy: detect → kill → re-check → spawn. Always-kill is
519
+ // safer than try-to-reuse — we have no living claim to the orphan.
520
+ // If kill fails (server gone, races a concurrent kill), the
521
+ // subsequent spawn will surface a clear TMUX_SPAWN_FAILED with
522
+ // the underlying tmux error for the operator.
523
+ if (typeof this.runner.sessionExists === 'function'
524
+ && await this.runner.sessionExists(this.tmuxName)) {
525
+ this.logger.warn?.(
526
+ `[${this.label}] orphan tmux session ${this.tmuxName} present at spawn — killing`,
527
+ );
528
+ this.emit('spawn-reconcile', {
529
+ tmux_name: this.tmuxName,
530
+ phase: 'kill-orphan',
531
+ backend: 'tmux',
532
+ });
533
+ try {
534
+ await this.runner.killSession(this.tmuxName);
535
+ } catch (killErr) {
536
+ this.logger.warn?.(
537
+ `[${this.label}] spawn-reconcile killSession failed (continuing): ${killErr.message}`,
538
+ );
539
+ }
540
+ // Brief settle window so the tmux server releases the name
541
+ // before we re-spawn. tmux's kill-session is synchronous in
542
+ // the client but the server's session-name release can race
543
+ // a tight kill→new-session pair (observed on busy hosts).
544
+ await new Promise((r) => setTimeout(r, 50));
545
+ }
546
+
499
547
  // R2-F8: spawn errors must fail loud, not silent-catch.
500
548
  await this.runner.spawn({
501
549
  name: this.tmuxName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.10.0-rc.30",
3
+ "version": "0.10.0-rc.31",
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": {