polygram 0.4.9 → 0.4.10

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.4.9",
4
+ "version": "0.4.10",
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",
@@ -54,11 +54,12 @@ class ProcessManager {
54
54
  db = null,
55
55
  logger = console,
56
56
  killTimeoutMs = DEFAULT_KILL_TIMEOUT_MS,
57
- onInit = null, // (sessionKey, event, entry) → void
58
- onResult = null, // (sessionKey, event, entry, pending) → void
59
- onClose = null, // (sessionKey, code, entry) → void
60
- onStreamChunk = null,// (sessionKey, partialText, entry) → void — routes to pendingQueue[0]
61
- onToolUse = null, // (sessionKey, toolName, entry) → void — routes to pendingQueue[0]
57
+ onInit = null, // (sessionKey, event, entry) → void
58
+ onResult = null, // (sessionKey, event, entry, pending) → void
59
+ onClose = null, // (sessionKey, code, entry) → void
60
+ onStreamChunk = null, // (sessionKey, partialText, entry) → void — routes to pendingQueue[0]
61
+ onToolUse = null, // (sessionKey, toolName, entry) → void — routes to pendingQueue[0]
62
+ onRespawn = null, // (sessionKey, reason, entry) → void — fires after graceful drain-and-kill
62
63
  } = {}) {
63
64
  if (!spawnFn) throw new Error('spawnFn required');
64
65
  this.cap = cap;
@@ -71,6 +72,7 @@ class ProcessManager {
71
72
  this.onClose = onClose;
72
73
  this.onStreamChunk = onStreamChunk;
73
74
  this.onToolUse = onToolUse;
75
+ this.onRespawn = onRespawn;
74
76
  this.procs = new Map();
75
77
  }
76
78
 
@@ -137,13 +139,22 @@ class ProcessManager {
137
139
  queued: entry.pendingQueue.length,
138
140
  });
139
141
  if (entry.pendingQueue.length === 0) {
140
- // Fire-and-forgetcaller doesn't need to await the kill.
141
- this.kill(sessionKey).catch(() => {});
142
+ // Queue empty kill immediately, fire onRespawn after close.
143
+ this._killAndNotifyRespawn(sessionKey, reason).catch(() => {});
142
144
  return { killed: true, queued: 0 };
143
145
  }
144
146
  return { killed: false, queued: entry.pendingQueue.length };
145
147
  }
146
148
 
149
+ async _killAndNotifyRespawn(sessionKey, reason) {
150
+ const entry = this.procs.get(sessionKey);
151
+ await this.kill(sessionKey);
152
+ if (this.onRespawn && entry) {
153
+ try { this.onRespawn(sessionKey, reason, entry); }
154
+ catch (err) { this.logger.error(`[pm] onRespawn: ${err.message}`); }
155
+ }
156
+ }
157
+
147
158
  async kill(sessionKey) {
148
159
  const entry = this.procs.get(sessionKey);
149
160
  if (!entry) return;
@@ -257,7 +268,8 @@ class ProcessManager {
257
268
  } else {
258
269
  entry.inFlight = false;
259
270
  // Graceful drain-and-respawn: if caller asked for a respawn
260
- // (e.g. /model change) and we just emptied the queue, kill now.
271
+ // (e.g. /model change) and we just emptied the queue, kill now
272
+ // and fire onRespawn so the caller can post confirmation.
261
273
  if (entry.needsRespawn) {
262
274
  const reason = entry.needsRespawn;
263
275
  entry.needsRespawn = null;
@@ -266,7 +278,7 @@ class ProcessManager {
266
278
  chat_id: entry.chatId,
267
279
  reason,
268
280
  });
269
- this.kill(sessionKey).catch(() => {});
281
+ this._killAndNotifyRespawn(sessionKey, reason).catch(() => {});
270
282
  }
271
283
  }
272
284
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
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
@@ -1904,6 +1904,25 @@ async function main() {
1904
1904
  const r = head?.context?.reactor;
1905
1905
  if (r) r.setState(classifyToolName(toolName));
1906
1906
  },
1907
+ // Fires after a graceful /model or /effort drain has actually
1908
+ // swapped to the new settings. Post a confirmation back to the
1909
+ // chat so the user knows the switch happened.
1910
+ onRespawn: (sessionKey, reason, entry) => {
1911
+ const chatId = entry.chatId;
1912
+ if (!chatId) return;
1913
+ const chatConfig = config.chats[chatId];
1914
+ if (!chatConfig) return;
1915
+ const text = reason === 'model-change'
1916
+ ? `✓ Using ${chatConfig.model} now.`
1917
+ : reason === 'effort-change'
1918
+ ? `✓ Effort is ${chatConfig.effort} now.`
1919
+ : `✓ Ready.`;
1920
+ const threadId = entry.threadId || undefined;
1921
+ tg(bot, 'sendMessage', {
1922
+ chat_id: chatId, text,
1923
+ ...(threadId && { message_thread_id: threadId }),
1924
+ }, { source: 'respawn-confirm', botName: BOT_NAME }).catch(() => {});
1925
+ },
1907
1926
  });
1908
1927
 
1909
1928
  console.log(`polygram (LRU cap=${cap}, SQLite source of truth)`);