polygram 0.8.0-rc.64 β†’ 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.
@@ -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.64",
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.64",
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
@@ -2220,6 +2220,12 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
2220
2220
  logEvent('compact-command', {
2221
2221
  chat_id: chatId, thread_id: threadIdStr, session_key: sessionKey,
2222
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,
2223
2229
  user: cmdUser, user_id: cmdUserId,
2224
2230
  });
2225
2231
  const hasHint = text.length > '/compact'.length + 1;
@@ -4420,28 +4426,77 @@ async function main() {
4420
4426
  console.error(`[replay] boot replay failed: ${err.message}`);
4421
4427
  }
4422
4428
 
4423
- // rc.61: surface compact-command events that were never paired with
4424
- // a compact-boundary (i.e. interrupted by deploy / crash). The SDK
4425
- // accepts /compact via stream input but compaction itself runs at a
4426
- // turn boundary; if polygram restarts before the SDK gets a chance
4427
- // to actually compact, the work is lost silently. Pre-rc.61 the
4428
- // user only saw "πŸ—œοΈ Compacting with your hint…" then nothing β€”
4429
- // and on the next over-threshold turn, the contextHint reminded
4430
- // them context was still full. Now: detect orphaned compact-command
4431
- // events on boot, post a "compaction was interrupted" message to
4432
- // the chat so the user knows to re-run.
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.
4433
4436
  //
4434
- // Scan window matches replayWindowMs β€” same logic: anything older
4435
- // than the bot's expected long-turn ceiling is stale and not worth
4436
- // surfacing.
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.
4443
+ //
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.
4437
4450
  try {
4438
- const orphans = db.findOrphanedCompactCommands({
4451
+ const orphansAll = db.findOrphanedCompactCommands({
4439
4452
  olderThanMs: resolveReplayWindowMs(config) ?? 30 * 60 * 1000,
4440
4453
  });
4441
- for (const o of orphans) {
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()) {
4442
4462
  const chatCfg = config.chats[o.chat_id];
4443
- if (!chatCfg) continue; // chat not owned by this bot
4463
+ if (!chatCfg) continue;
4444
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.
4445
4500
  try {
4446
4501
  await tg(bot, 'sendMessage', {
4447
4502
  chat_id: o.chat_id,
@@ -4455,16 +4510,18 @@ async function main() {
4455
4510
  original_ts: o.ts,
4456
4511
  user: o.user,
4457
4512
  user_id: o.user_id,
4513
+ reason: o.text ? 'spawn-failed' : 'pre-rc65-event-no-text',
4458
4514
  });
4515
+ surfacedFallback += 1;
4459
4516
  } catch (err) {
4460
4517
  console.error(`[compact-orphan-surface] ${o.session_key}: ${err.message}`);
4461
4518
  }
4462
4519
  }
4463
- if (orphans.length > 0) {
4464
- console.log(`[compact-orphan] surfaced ${orphans.length} interrupted /compact events`);
4520
+ if (replayed + surfacedFallback > 0) {
4521
+ console.log(`[compact-orphan] silent-replayed=${replayed}, surfaced-fallback=${surfacedFallback}`);
4465
4522
  }
4466
4523
  } catch (err) {
4467
- console.error(`[compact-orphan-surface] failed: ${err.message}`);
4524
+ console.error(`[compact-orphan-handler] failed: ${err.message}`);
4468
4525
  }
4469
4526
 
4470
4527
  console.log(`[${BOT_NAME}] Starting...`);