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.
- package/.claude-plugin/plugin.json +1 -1
- package/lib/replay-window.js +53 -0
- package/package.json +1 -1
- package/polygram.js +24 -10
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
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`
|
|
4170
|
-
//
|
|
4171
|
-
//
|
|
4172
|
-
//
|
|
4173
|
-
//
|
|
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;
|