polygram 0.8.0-rc.56 → 0.8.0-rc.57

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.8.0-rc.56",
4
+ "version": "0.8.0-rc.57",
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 and a history skill.",
6
6
  "keywords": [
7
7
  "telegram",
@@ -0,0 +1,53 @@
1
+ /**
2
+ * rc.57: pure resolver for the boot-replay window in milliseconds.
3
+ *
4
+ * Lifted out of polygram.js's main() so the derivation rule can be
5
+ * unit-tested without spinning up the daemon.
6
+ *
7
+ * Precedence:
8
+ * 1. config.bot.replayWindowMs — explicit operator override (any
9
+ * positive integer in ms).
10
+ * 2. Auto-derive from max(maxTurn) × 1.2 across all configured chats
11
+ * (and defaults.maxTurn). Reasoning: if a chat allows turns up to
12
+ * maxTurn seconds, an interrupted turn could be that old when
13
+ * polygram restarts; replay window should outlast it. ×1.2 adds
14
+ * buffer.
15
+ * 3. If no maxTurn is configured anywhere, return undefined (db.js
16
+ * uses its 3-min default).
17
+ *
18
+ * Floor at 3 min (legacy default — never tighter than what we shipped
19
+ * before). Cap at 2h (sanity bound — replaying anything older is
20
+ * almost certainly stale work the user already moved on from).
21
+ *
22
+ * Discovery: msg 151 in Shumabit@UMI thread :24 (chat -1003369922517)
23
+ * was sent 2026-05-05 01:55:14, polygram restarted for rc.56 at
24
+ * 02:17 (22 min later). Pre-rc.57 the 3-min default discarded msg 151
25
+ * as too old; the agent's 7-hour Xero-template-build task was
26
+ * abandoned silently. Shumabit@UMI has maxTurn=3600 (60 min); 1.2×
27
+ * = 72 min replay window now keeps long turns alive across deploys.
28
+ */
29
+
30
+ 'use strict';
31
+
32
+ const FLOOR_MS = 3 * 60 * 1000; // 3 min
33
+ const CAP_MS = 2 * 60 * 60 * 1000; // 2 h
34
+ const BUFFER = 1.2; // ×
35
+
36
+ function resolveReplayWindowMs(config) {
37
+ const explicit = Number(config?.bot?.replayWindowMs);
38
+ if (Number.isInteger(explicit) && explicit > 0) return explicit;
39
+ const chatMaxes = Object.values(config?.chats || {})
40
+ .map((c) => Number(c?.maxTurn) || 0);
41
+ const defaultMax = Number(config?.defaults?.maxTurn) || 0;
42
+ const maxTurnSec = Math.max(0, ...chatMaxes, defaultMax);
43
+ if (maxTurnSec === 0) return undefined;
44
+ const derivedMs = Math.round(maxTurnSec * BUFFER * 1000);
45
+ return Math.max(FLOOR_MS, Math.min(CAP_MS, derivedMs));
46
+ }
47
+
48
+ module.exports = {
49
+ resolveReplayWindowMs,
50
+ FLOOR_MS,
51
+ CAP_MS,
52
+ BUFFER,
53
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.8.0-rc.56",
3
+ "version": "0.8.0-rc.57",
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": {
package/polygram.js CHANGED
@@ -66,6 +66,7 @@ const { createReactionManager, classifyToolName } = require('./lib/status-reacti
66
66
  const { createMediaGroupBuffer } = require('./lib/media-group-buffer');
67
67
  const { classify: classifyError, isTransientHttpError } = require('./lib/error-classify');
68
68
  const { createAutoResumeTracker, isAutoResumable } = require('./lib/auto-resume');
69
+ const { resolveReplayWindowMs } = require('./lib/replay-window');
69
70
  const {
70
71
  createStore: createApprovalsStore,
71
72
  matchesAnyPattern: matchesApprovalPattern,
@@ -3654,7 +3655,13 @@ async function pollBot(bot) {
3654
3655
  const chatId = m.chat.id.toString();
3655
3656
  const chatConfig = config.chats[chatId];
3656
3657
  const threadId = m.message_thread_id?.toString();
3657
- const topicName = threadId && chatConfig?.topics?.[threadId] ? chatConfig.topics[threadId] : threadId;
3658
+ // rc.57: use getTopicName() helper which handles BOTH legacy string
3659
+ // form and rc.48 object form. Pre-rc.57 the direct lookup
3660
+ // `chatConfig.topics[threadId]` template-literal'd into "[object Object]"
3661
+ // because rc.48 topics are objects like {name:"Music",agent:"...",...}.
3662
+ const topicName = threadId
3663
+ ? (chatConfig ? getTopicName(chatConfig, threadId) : threadId)
3664
+ : null;
3658
3665
  const chatLabel = chatConfig?.name || chatId;
3659
3666
  const label = topicName ? `${chatLabel}/${topicName}` : chatLabel;
3660
3667
  console.log(`[${BOT_NAME}] ← ${label}: ${(m.text || m.caption || '(media)').slice(0, 60)}`);
@@ -4166,18 +4173,25 @@ async function main() {
4166
4173
  // Boot replay: re-dispatch any inbound turns that were interrupted by
4167
4174
  // the previous polygram's shutdown or crash. These are rows marked
4168
4175
  // 'dispatched', 'processing', or 'replay-pending' (set by the SIGTERM
4169
- // handler) — all within the last `replayWindowMs` (default 3 min) so
4170
- // we don't resurrect ancient work. Override via
4171
- // `config.bot.replayWindowMs` for ops tuning. Dedupe against
4172
- // already-sent outbound replies in case the previous instance DID
4173
- // answer before dying.
4176
+ // handler) — all within the last `replayWindowMs` so we don't
4177
+ // resurrect ancient work. Dedupe against already-sent outbound
4178
+ // replies in case the previous instance DID answer before dying.
4179
+ //
4180
+ // rc.57: auto-derive replayWindowMs from max(maxTurn) * 1.2 when not
4181
+ // explicitly set. Pre-rc.57 the default was 3 min — but chats with
4182
+ // long agent tasks (Shumabit@UMI maxTurn=3600 = 60 min) would have
4183
+ // their interrupted turns silently dropped because the turn was
4184
+ // typically older than 3 min when polygram restarted. Discovery
4185
+ // context: msg 151 in Shumabit@UMI thread :24 on 2026-05-05 was
4186
+ // sent at 01:55:14, polygram restarted for rc.56 at 02:17 (22 min
4187
+ // later). msg 151 was 'replay-pending' but boot-replay's 3-min
4188
+ // window discarded it; the agent's 7-hour Xero task was abandoned.
4189
+ // Auto-derive: 1.2 × max(chatConfig.maxTurn) across all chats,
4190
+ // floored at 3 min (legacy default), capped at 2 hours (sanity).
4174
4191
  try {
4175
4192
  const chatIds = Object.keys(config.chats);
4176
4193
  if (chatIds.length > 0) {
4177
- const replayWindowMs = (() => {
4178
- const v = Number(config.bot?.replayWindowMs);
4179
- return (Number.isInteger(v) && v > 0) ? v : undefined; // undefined → use db.js default
4180
- })();
4194
+ const replayWindowMs = resolveReplayWindowMs(config);
4181
4195
  const candidates = db.getReplayCandidates({ chatIds, ...(replayWindowMs && { olderThanMs: replayWindowMs }) });
4182
4196
  let replayed = 0;
4183
4197
  let skipped = 0;