polygram 0.8.0-rc.21 β†’ 0.8.0-rc.23

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.21",
4
+ "version": "0.8.0-rc.23",
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.8.0-rc.21",
3
+ "version": "0.8.0-rc.23",
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
@@ -1987,6 +1987,51 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
1987
1987
  }
1988
1988
  return;
1989
1989
  }
1990
+ // 0.8.0-rc.22: /compact <preserve text> β€” manual SDK compaction with
1991
+ // user-supplied preservation instructions. The SDK's CLI binary
1992
+ // recognises "/compact" as a slash command via streamInput.push
1993
+ // (verified by scripts/spikes/compact-via-streaminput.mjs: PreCompact
1994
+ // hook fires with trigger:'manual', compact_boundary event lands).
1995
+ // We push the raw text "/compact <instructions>" through the SDK's
1996
+ // input controller; the SDK handles parsing + compaction internally.
1997
+ if (botAllowsCommands && text.startsWith('/compact')) {
1998
+ if (!pm.isSdkFor(sessionKey)) {
1999
+ await sendReply('πŸ—œοΈ /compact requires the SDK pm. This chat is on the CLI pm path.');
2000
+ return;
2001
+ }
2002
+ if (!pm.has(sessionKey)) {
2003
+ await sendReply('πŸ—œοΈ No active session β€” /compact only works once a turn has started.');
2004
+ return;
2005
+ }
2006
+ const entry = pm.get(sessionKey);
2007
+ if (!entry?.inputController?.push) {
2008
+ await sendReply('πŸ—œοΈ Session not ready for /compact (no input controller).');
2009
+ return;
2010
+ }
2011
+ // Push the literal "/compact ..." text into the input stream.
2012
+ // The SDK parses leading "/" as a slash command and triggers
2013
+ // manual compaction; user's preserve instructions land in
2014
+ // PreCompactHookInput.custom_instructions.
2015
+ try {
2016
+ entry.inputController.push({
2017
+ type: 'user',
2018
+ message: { role: 'user', content: text },
2019
+ parent_tool_use_id: null,
2020
+ });
2021
+ logEvent('compact-command', {
2022
+ chat_id: chatId, text_len: text.length,
2023
+ user: cmdUser, user_id: cmdUserId,
2024
+ });
2025
+ const hasHint = text.length > '/compact'.length + 1;
2026
+ await sendReply(hasHint
2027
+ ? 'πŸ—œοΈ Compacting with your hint…'
2028
+ : 'πŸ—œοΈ Compacting…');
2029
+ } catch (err) {
2030
+ console.error(`[${label}] /compact push: ${err.message}`);
2031
+ await sendReply(`πŸ—œοΈ Couldn't trigger compact: ${err.message}`);
2032
+ }
2033
+ return;
2034
+ }
1990
2035
  if (botAllowsCommands && (text === '/new' || text === '/reset')) {
1991
2036
  let drained = 0;
1992
2037
  const target = pm.pickFor(sessionKey);
@@ -2603,9 +2648,22 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
2603
2648
  // context (and fired below the intended 85% threshold).
2604
2649
  const pct = usage?.percentage ?? 0;
2605
2650
  if (pct < 85) return;
2651
+ // rc.22: three-choice hint. The original "send /new"
2652
+ // message implied the only path forward was a hard
2653
+ // reset. Now offer all three options the user actually
2654
+ // has β€” start fresh, compact with their preserve
2655
+ // instructions, or keep going (auto-compact eventually
2656
+ // fires).
2657
+ const text = [
2658
+ `πŸ“š Context window ${pct.toFixed(0)}% full. Three options:`,
2659
+ '',
2660
+ 'β€’ `/new` β€” start fresh; this conversation ends.',
2661
+ 'β€’ `/compact` β€” summarise older messages. Add a hint after the command (e.g. `/compact keep the Q3 commission decisions`) and that becomes the compactor\'s guidance.',
2662
+ 'β€’ Keep chatting β€” I\'ll auto-compact when needed; key context is preserved automatically.',
2663
+ ].join('\n');
2606
2664
  return tg(bot, 'sendMessage', {
2607
2665
  chat_id: chatId,
2608
- text: `πŸ“š Context window ${pct.toFixed(0)}% full. Send /new to start fresh β€” older messages will start dropping soon.`,
2666
+ text,
2609
2667
  ...(threadId ? { message_thread_id: threadId } : {}),
2610
2668
  }, { source: 'context-full-hint', botName: BOT_NAME });
2611
2669
  }).catch((err) => {
@@ -2868,7 +2926,7 @@ function createBot(token) {
2868
2926
  // Cached once @botUsername is known β€” was recompiling per inbound msg.
2869
2927
  let mentionRe = null;
2870
2928
  // Hoisted admin-command matcher; was re-allocated per message.
2871
- const ADMIN_CMD_RE = /^\/(model|effort|config|pair-code|pairings|unpair|new|reset|context)(\s|$)/;
2929
+ const ADMIN_CMD_RE = /^\/(model|effort|config|pair-code|pairings|unpair|new|reset|context|compact)(\s|$)/;
2872
2930
  const PAIR_CLAIM_RE = /^\/pair\s+\S+/;
2873
2931
 
2874
2932
  // The filter in main() guarantees config.chats only contains chats owned