polygram 0.8.0-rc.63 β 0.8.0-rc.65
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/db.js +2 -1
- package/package.json +1 -1
- package/polygram.js +106 -23
|
@@ -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.65",
|
|
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",
|
package/lib/db.js
CHANGED
|
@@ -360,7 +360,8 @@ function wrap(db) {
|
|
|
360
360
|
json_extract(detail_json, '$.session_key') AS session_key,
|
|
361
361
|
json_extract(detail_json, '$.user') AS user,
|
|
362
362
|
json_extract(detail_json, '$.user_id') AS user_id,
|
|
363
|
-
json_extract(detail_json, '$.text_len') AS text_len
|
|
363
|
+
json_extract(detail_json, '$.text_len') AS text_len,
|
|
364
|
+
json_extract(detail_json, '$.text') AS text
|
|
364
365
|
FROM events
|
|
365
366
|
WHERE kind = 'compact-command'
|
|
366
367
|
AND ts > ?
|
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.65",
|
|
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
|
@@ -2172,11 +2172,37 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2172
2172
|
await sendReply('ποΈ /compact requires the SDK pm. This chat is on the CLI pm path.');
|
|
2173
2173
|
return;
|
|
2174
2174
|
}
|
|
2175
|
-
if (
|
|
2176
|
-
|
|
2177
|
-
|
|
2175
|
+
// rc.64: if the in-memory session was evicted (LRU cap pressure)
|
|
2176
|
+
// but there's a saved Claude session_id in DB, auto-spawn the
|
|
2177
|
+
// Query with --resume so /compact has something to work with.
|
|
2178
|
+
// Pre-rc.64 we returned "ποΈ No active session" β confusing
|
|
2179
|
+
// because the user just had a conversation 5 minutes ago, the
|
|
2180
|
+
// session went idle, LRU evicted it, and "No active session"
|
|
2181
|
+
// reads as "I never knew you" instead of "I unloaded your
|
|
2182
|
+
// session, hold on."
|
|
2183
|
+
let entry = pm.get(sessionKey);
|
|
2184
|
+
if (!entry) {
|
|
2185
|
+
const savedSessionId = getClaudeSessionId(db, sessionKey);
|
|
2186
|
+
if (!savedSessionId) {
|
|
2187
|
+
await sendReply('ποΈ No conversation to compact yet. Send a message first, then /compact.');
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
try {
|
|
2191
|
+
entry = await getOrSpawnForChat(sessionKey);
|
|
2192
|
+
} catch (err) {
|
|
2193
|
+
console.error(`[${label}] /compact spawn-resume: ${err.message}`);
|
|
2194
|
+
await sendReply(`ποΈ Couldn't load session for compaction: ${err.message}`);
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
if (!entry) {
|
|
2198
|
+
await sendReply('ποΈ Session not loadable (config missing).');
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
logEvent('compact-spawn-resumed', {
|
|
2202
|
+
chat_id: chatId, thread_id: threadIdStr, session_key: sessionKey,
|
|
2203
|
+
resumed_session_id: savedSessionId,
|
|
2204
|
+
});
|
|
2178
2205
|
}
|
|
2179
|
-
const entry = pm.get(sessionKey);
|
|
2180
2206
|
if (!entry?.inputController?.push) {
|
|
2181
2207
|
await sendReply('ποΈ Session not ready for /compact (no input controller).');
|
|
2182
2208
|
return;
|
|
@@ -2194,6 +2220,12 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2194
2220
|
logEvent('compact-command', {
|
|
2195
2221
|
chat_id: chatId, thread_id: threadIdStr, session_key: sessionKey,
|
|
2196
2222
|
text_len: text.length,
|
|
2223
|
+
// rc.65: store the full /compact text so boot-time orphan
|
|
2224
|
+
// recovery can silently re-push it after a deploy
|
|
2225
|
+
// interrupted compaction. Agent-supplied hints (e.g.
|
|
2226
|
+
// "/compact keep the Q3 commission decisions") need to be
|
|
2227
|
+
// preserved verbatim for the retry to summarize correctly.
|
|
2228
|
+
text,
|
|
2197
2229
|
user: cmdUser, user_id: cmdUserId,
|
|
2198
2230
|
});
|
|
2199
2231
|
const hasHint = text.length > '/compact'.length + 1;
|
|
@@ -4394,28 +4426,77 @@ async function main() {
|
|
|
4394
4426
|
console.error(`[replay] boot replay failed: ${err.message}`);
|
|
4395
4427
|
}
|
|
4396
4428
|
|
|
4397
|
-
// rc.61:
|
|
4398
|
-
// a compact-boundary (
|
|
4399
|
-
//
|
|
4400
|
-
//
|
|
4401
|
-
//
|
|
4402
|
-
//
|
|
4403
|
-
//
|
|
4404
|
-
//
|
|
4405
|
-
//
|
|
4406
|
-
//
|
|
4429
|
+
// rc.61 + rc.65: handle compact-command events that were never
|
|
4430
|
+
// paired with a compact-boundary (deploy / crash interrupted them
|
|
4431
|
+
// before the SDK processed). rc.65 changes behaviour from
|
|
4432
|
+
// "post a 'please retry' message and ask the user" to
|
|
4433
|
+
// "silently retry by re-pushing the same /compact text to a
|
|
4434
|
+
// freshly-spawned (resumed) Query." Same pattern as boot-replay
|
|
4435
|
+
// for inbound messages β recovery should be invisible.
|
|
4436
|
+
//
|
|
4437
|
+
// Requirements for a silent retry:
|
|
4438
|
+
// 1. The compact-command event was logged with full `text`
|
|
4439
|
+
// (rc.65+ does this; pre-rc.65 events have only text_len β
|
|
4440
|
+
// we fall back to the old "please retry" message for those).
|
|
4441
|
+
// 2. The chat is still configured (config.chats[chat_id] exists).
|
|
4442
|
+
// 3. The session has a saved claude_session_id we can resume.
|
|
4407
4443
|
//
|
|
4408
|
-
//
|
|
4409
|
-
//
|
|
4410
|
-
//
|
|
4444
|
+
// Dedupe: if multiple orphans for the same session_key (e.g. user
|
|
4445
|
+
// ran /compact twice in quick succession before a deploy), retry
|
|
4446
|
+
// only the MOST RECENT β older ones are obsolete.
|
|
4447
|
+
//
|
|
4448
|
+
// Scan window matches replayWindowMs β anything older than the
|
|
4449
|
+
// bot's expected long-turn ceiling is stale.
|
|
4411
4450
|
try {
|
|
4412
|
-
const
|
|
4451
|
+
const orphansAll = db.findOrphanedCompactCommands({
|
|
4413
4452
|
olderThanMs: resolveReplayWindowMs(config) ?? 30 * 60 * 1000,
|
|
4414
4453
|
});
|
|
4415
|
-
|
|
4454
|
+
// Dedupe per-session_key, keep the most recent (highest ts).
|
|
4455
|
+
const orphansLatest = new Map();
|
|
4456
|
+
for (const o of orphansAll) {
|
|
4457
|
+
orphansLatest.set(o.session_key, o);
|
|
4458
|
+
}
|
|
4459
|
+
let replayed = 0;
|
|
4460
|
+
let surfacedFallback = 0;
|
|
4461
|
+
for (const o of orphansLatest.values()) {
|
|
4416
4462
|
const chatCfg = config.chats[o.chat_id];
|
|
4417
|
-
if (!chatCfg) continue;
|
|
4463
|
+
if (!chatCfg) continue;
|
|
4418
4464
|
const threadId = o.thread_id ? Number(o.thread_id) : null;
|
|
4465
|
+
const savedSessionId = getClaudeSessionId(db, o.session_key);
|
|
4466
|
+
|
|
4467
|
+
// Silent retry path: only when we have BOTH the original text
|
|
4468
|
+
// (rc.65+) AND a session_id to resume into.
|
|
4469
|
+
if (o.text && savedSessionId) {
|
|
4470
|
+
try {
|
|
4471
|
+
const entry = await pm.getOrSpawn(o.session_key, buildSpawnContext(o.session_key));
|
|
4472
|
+
if (!entry?.inputController?.push) {
|
|
4473
|
+
throw new Error('input controller not ready post-spawn');
|
|
4474
|
+
}
|
|
4475
|
+
entry.inputController.push({
|
|
4476
|
+
type: 'user',
|
|
4477
|
+
message: { role: 'user', content: o.text },
|
|
4478
|
+
parent_tool_use_id: null,
|
|
4479
|
+
});
|
|
4480
|
+
logEvent('compact-replay', {
|
|
4481
|
+
chat_id: o.chat_id,
|
|
4482
|
+
thread_id: o.thread_id,
|
|
4483
|
+
session_key: o.session_key,
|
|
4484
|
+
original_ts: o.ts,
|
|
4485
|
+
text_len: o.text.length,
|
|
4486
|
+
user: o.user,
|
|
4487
|
+
user_id: o.user_id,
|
|
4488
|
+
});
|
|
4489
|
+
replayed += 1;
|
|
4490
|
+
continue;
|
|
4491
|
+
} catch (err) {
|
|
4492
|
+
console.error(`[compact-replay] ${o.session_key}: ${err.message} β falling back to surface`);
|
|
4493
|
+
// fall through to surface fallback below
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
|
|
4497
|
+
// Fallback: surface the legacy "please retry" message. Only
|
|
4498
|
+
// happens for pre-rc.65 events (no `text` field) or when
|
|
4499
|
+
// the silent-retry spawn failed.
|
|
4419
4500
|
try {
|
|
4420
4501
|
await tg(bot, 'sendMessage', {
|
|
4421
4502
|
chat_id: o.chat_id,
|
|
@@ -4429,16 +4510,18 @@ async function main() {
|
|
|
4429
4510
|
original_ts: o.ts,
|
|
4430
4511
|
user: o.user,
|
|
4431
4512
|
user_id: o.user_id,
|
|
4513
|
+
reason: o.text ? 'spawn-failed' : 'pre-rc65-event-no-text',
|
|
4432
4514
|
});
|
|
4515
|
+
surfacedFallback += 1;
|
|
4433
4516
|
} catch (err) {
|
|
4434
4517
|
console.error(`[compact-orphan-surface] ${o.session_key}: ${err.message}`);
|
|
4435
4518
|
}
|
|
4436
4519
|
}
|
|
4437
|
-
if (
|
|
4438
|
-
console.log(`[compact-orphan] surfaced
|
|
4520
|
+
if (replayed + surfacedFallback > 0) {
|
|
4521
|
+
console.log(`[compact-orphan] silent-replayed=${replayed}, surfaced-fallback=${surfacedFallback}`);
|
|
4439
4522
|
}
|
|
4440
4523
|
} catch (err) {
|
|
4441
|
-
console.error(`[compact-orphan-
|
|
4524
|
+
console.error(`[compact-orphan-handler] failed: ${err.message}`);
|
|
4442
4525
|
}
|
|
4443
4526
|
|
|
4444
4527
|
console.log(`[${BOT_NAME}] Starting...`);
|