polygram 0.33.3 → 0.33.5

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.33.3",
4
+ "version": "0.33.5",
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",
@@ -152,6 +152,10 @@ function listUnresolvedCodexAttempts(rawDb, input = {}) {
152
152
  location.telegram_message_id,
153
153
  unresolved.telegram_source_message_id
154
154
  )
155
+ WHERE COALESCE(
156
+ location.telegram_message_id,
157
+ unresolved.telegram_source_message_id
158
+ ) IS NOT NULL
155
159
  ORDER BY unresolved.ambiguous_ts,
156
160
  unresolved.attempt_id
157
161
  `).all(sessionKey, sessionKey);
@@ -59,7 +59,7 @@ function createDispatcher({
59
59
  // the historic 4096 for back-compat in synthetic test runs that pass
60
60
  // pre-formatted text.
61
61
  chunkBudget = 4096,
62
- // Delay before a silent startup auto-retry re-dispatches (TMUX_SESSION_GONE).
62
+ // Delay before a silent startup auto-retry re-dispatches a pre-delivery failure.
63
63
  // Injected so tests can drive it to 0; production uses STARTUP_RETRY_DELAY_MS.
64
64
  startupRetryDelayMs = STARTUP_RETRY_DELAY_MS,
65
65
  // AUTH_DISABLED dedupe/re-arm gate (docs/AUTH_DISABLED_HANDLING_SPEC.md).
@@ -306,6 +306,11 @@ function createDispatcher({
306
306
  chat_id: chatId, session_key: sessionKey,
307
307
  msg_id: msg?.message_id,
308
308
  error: err.message?.slice(0, 500),
309
+ code: err.code || undefined,
310
+ cause_code: err.cause?.code || undefined,
311
+ stderr_tail: err.code === 'TMUX_SPAWN_FAILED' && typeof err.stderr === 'string'
312
+ ? err.stderr.replace(/[\x00-\x08\x0b-\x1f\x7f]/g, '').slice(-500)
313
+ : undefined,
309
314
  stack: err.stack?.split('\n').slice(0, 5).join('\n'),
310
315
  aborted: wasAborted || undefined,
311
316
  replay: isReplay || undefined,
@@ -442,23 +447,17 @@ function createDispatcher({
442
447
  }
443
448
  }
444
449
  const provider = providerRecovery?.provider;
445
- // Startup auto-retry (option a, 2026-06-04). TMUX_SESSION_GONE = claude
446
- // exited INSIDE the startup gate, before the dev-channels channel went
447
- // live — so the user's message was NEVER delivered to claude. That makes
448
- // a re-send idempotent BY CONSTRUCTION (unlike a mid-turn drop, where
449
- // claude might still be slowly processing). The session_id was just
450
- // poison-cleared above, so re-dispatching the SAME message spawns a FRESH
451
- // session and delivers it. Silent: a transient startup flake (recurs
452
- // ~once/9h on the channels backend) never reaches the user — instead of
453
- // the "🔄 reset it, resend" papercut, polygram just retries. One-shot
454
- // (_startupRetried) so a host that genuinely can't start claude surfaces
455
- // the friendly reset reply (below) after EXACTLY one retry, never a loop.
456
- // Scoped to TMUX_SESSION_GONE only: CHANNELS_DIALOG_TIMEOUT is a real
457
- // blocking dialog (usage-limit / permission) a retry would just re-hit,
458
- // so it keeps its "please resend" copy.
459
- if (err.code === 'TMUX_SESSION_GONE' && !msg._startupRetried) {
450
+ // Retry once when startup fails before the bridge can deliver the user's
451
+ // message. TMUX_SESSION_GONE is poison-cleared above; TMUX_SPAWN_FAILED
452
+ // is already torn down by CliProcess's start-failure cleanup. Mid-turn
453
+ // failures and blocking dialogs are excluded because replay could
454
+ // duplicate work or immediately re-hit the same user decision.
455
+ const startupRetryable = err.code === 'TMUX_SESSION_GONE'
456
+ || err.code === 'TMUX_SPAWN_FAILED';
457
+ if (startupRetryable && !msg._startupRetried) {
460
458
  logEvent('startup-auto-retry', {
461
459
  chat_id: chatId, session_key: sessionKey, msg_id: msg?.message_id,
460
+ code: err.code,
462
461
  });
463
462
  // Re-dispatch a COPY carrying the one-shot marker — never mutate the
464
463
  // caller's msg (the boot-replay path shares/re-reads it). unref the
@@ -73,7 +73,7 @@ const INLINE_MEDIA_PARAGRAPHS = [
73
73
  '',
74
74
  '**Inline media or `files:`, not both.** A file you embed inline is already delivered; listing that same path in `files:` too sends it a second time as a separate message. Use inline media when the image belongs in the reply, and `files:` when you are handing over a document.',
75
75
  '',
76
- '**Media and live streaming do not mix.** A message that streams grows by editing, and an edited bubble can never gain media — images degrade to their caption text. When your answer includes media, keep the media OUT of the streamed report: stream the prose, and deliver the media in its own separate reply (with at least a sentence of text). Do not put media in a message you are streaming.',
76
+ '**Media and live streaming do not mix.** A message that streams grows by editing, and an edited bubble can never gain media — images degrade to their caption text. When your answer includes media (inline images, a collage, a slideshow), skip streaming for that answer entirely: compose it and send ONE reply, with the media rendering inline in the rich document, pictures in place. Do not stream the prose first, and do not split the media into a separate message.',
77
77
  '',
78
78
  ];
79
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.33.3",
3
+ "version": "0.33.5",
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": {
@@ -63,7 +63,7 @@
63
63
  "dependencies": {
64
64
  "@anthropic-ai/claude-agent-sdk": "0.2.123",
65
65
  "@modelcontextprotocol/sdk": "^1.29.0",
66
- "@shumkov/orchestra": "0.10.9",
66
+ "@shumkov/orchestra": "0.10.10",
67
67
  "better-sqlite3": "^12.9.0",
68
68
  "grammy": "^1.42.0",
69
69
  "markdown-it": "^14.1.1",