polygram 0.6.4 → 0.6.5
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/package.json +1 -1
- package/polygram.js +38 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
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
|
@@ -756,12 +756,6 @@ function dispatchHandleMessage(sessionKey, chatId, msg, bot) {
|
|
|
756
756
|
});
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
// drainQueuesForChat is retained as a no-op for backwards compat with
|
|
760
|
-
// call sites in /model, /effort, chat-migration, and abort handlers.
|
|
761
|
-
// Returns 0 always; a drain isn't meaningful in the concurrent model —
|
|
762
|
-
// callers that want to abort should rely on pm.killChat.
|
|
763
|
-
const drainQueuesForChat = (_chatId) => 0;
|
|
764
|
-
|
|
765
759
|
// Per-session lock ordering stdin writes. Module is I/O-pure.
|
|
766
760
|
const stdinLock = createAsyncLock();
|
|
767
761
|
|
|
@@ -1161,17 +1155,17 @@ async function handleConfigCallback(ctx) {
|
|
|
1161
1155
|
user: cmdUser, user_id: cmdUserId, source: 'inline-button',
|
|
1162
1156
|
}), `log ${setting} change`);
|
|
1163
1157
|
|
|
1164
|
-
// Graceful respawn
|
|
1165
|
-
//
|
|
1158
|
+
// Graceful respawn of the topic's session that the card is in. With
|
|
1159
|
+
// isolateTopics=false sessionKey is the chat (one shared session). With
|
|
1160
|
+
// isolateTopics=true sessionKey carries the topic, so other topics'
|
|
1161
|
+
// in-flight turns are not disturbed and the card update + button toast
|
|
1162
|
+
// only affect the user's own context. Mirrors the text-command flow in
|
|
1163
|
+
// handleMessage's requestRespawnForSession.
|
|
1164
|
+
const callbackThreadId = ctx.callbackQuery.message?.message_thread_id?.toString() || null;
|
|
1165
|
+
const callbackSessionKey = getSessionKey(chatId, callbackThreadId, chatConfig);
|
|
1166
1166
|
const reason = setting === 'model' ? 'model-change' : 'effort-change';
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1169
|
-
for (const key of pm.keys()) {
|
|
1170
|
-
if (key === prefix || key.startsWith(prefix + ':')) {
|
|
1171
|
-
const res = pm.requestRespawn(key, reason);
|
|
1172
|
-
if (!res.killed) anyActive = true;
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1167
|
+
const respawn = pm.requestRespawn(callbackSessionKey, reason);
|
|
1168
|
+
const anyActive = !respawn.killed;
|
|
1175
1169
|
|
|
1176
1170
|
// Re-render the card with updated ✓ + the same help text shown initially.
|
|
1177
1171
|
// Detect original card type (model-only / effort-only / both) by counting
|
|
@@ -1307,23 +1301,17 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1307
1301
|
await sendReply(info, { params: { reply_markup } });
|
|
1308
1302
|
return;
|
|
1309
1303
|
}
|
|
1310
|
-
//
|
|
1311
|
-
//
|
|
1312
|
-
//
|
|
1313
|
-
//
|
|
1314
|
-
//
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
const res = pm.requestRespawn(key, reason);
|
|
1322
|
-
totalQueued += res.queued;
|
|
1323
|
-
if (!res.killed) anyActive = true;
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
return { queued: totalQueued, anyActive };
|
|
1304
|
+
// Graceful respawn of the user's CURRENT session only. With
|
|
1305
|
+
// isolateTopics=false the sessionKey is just the chat (one shared
|
|
1306
|
+
// session for the whole chat — every topic respawns implicitly).
|
|
1307
|
+
// With isolateTopics=true each topic is a separate session, and a
|
|
1308
|
+
// /model in topic A should NOT disturb topic B's in-flight turn or
|
|
1309
|
+
// post a phantom "✓ Using sonnet now" in a topic that didn't ask.
|
|
1310
|
+
// Pre-0.6.5 this iterated pm.keys() by chat prefix and incorrectly
|
|
1311
|
+
// fanned out across all topics under isolateTopics=true.
|
|
1312
|
+
const requestRespawnForSession = (reason) => {
|
|
1313
|
+
const res = pm.requestRespawn(sessionKey, reason);
|
|
1314
|
+
return { queued: res.queued, anyActive: !res.killed };
|
|
1327
1315
|
};
|
|
1328
1316
|
|
|
1329
1317
|
if (botAllowsCommands && text.startsWith('/model ')) {
|
|
@@ -1337,7 +1325,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1337
1325
|
old_value: oldModel, new_value: newModel,
|
|
1338
1326
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
1339
1327
|
}), 'log model change');
|
|
1340
|
-
const { anyActive } =
|
|
1328
|
+
const { anyActive } = requestRespawnForSession('model-change');
|
|
1341
1329
|
const ver = MODEL_VERSIONS[newModel] || newModel;
|
|
1342
1330
|
const suffix = anyActive ? ` — I'll switch when I finish` : '';
|
|
1343
1331
|
await sendReply(`Model → ${newModel} (${ver})${suffix}`);
|
|
@@ -1357,7 +1345,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1357
1345
|
old_value: oldEffort, new_value: newEffort,
|
|
1358
1346
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
1359
1347
|
}), 'log effort change');
|
|
1360
|
-
const { anyActive } =
|
|
1348
|
+
const { anyActive } = requestRespawnForSession('effort-change');
|
|
1361
1349
|
const suffix = anyActive ? ` — I'll switch when I finish` : '';
|
|
1362
1350
|
await sendReply(`Effort → ${newEffort}${suffix}`);
|
|
1363
1351
|
} else {
|
|
@@ -1870,16 +1858,24 @@ function createBot(token) {
|
|
|
1870
1858
|
const threadId = msg.message_thread_id?.toString();
|
|
1871
1859
|
const sessionKey = getSessionKey(chatId, threadId, chatConfig);
|
|
1872
1860
|
const hadActive = pm.has(sessionKey) && !!pm.get(sessionKey)?.inFlight;
|
|
1873
|
-
const dropped = drainQueuesForChat(chatId);
|
|
1874
1861
|
// Mark BEFORE killing: the 'close' event fires almost immediately
|
|
1875
1862
|
// after SIGTERM, and processQueue's catch needs to see the flag to
|
|
1876
1863
|
// skip the generic error-reply. If we marked after, there'd be a
|
|
1877
1864
|
// race where the error-reply slips through.
|
|
1878
1865
|
if (hadActive) markSessionAborted(sessionKey);
|
|
1879
|
-
|
|
1866
|
+
// Kill ONLY the user's own session, not every topic in the chat.
|
|
1867
|
+
// Pre-0.6.5 this was pm.killChat(chatId) which fanned out across
|
|
1868
|
+
// all topics under isolateTopics=true: the user typed "stop" in
|
|
1869
|
+
// topic A and the bot tore down topic B's in-flight turn, surfacing
|
|
1870
|
+
// a 💥 reply to topic B's user (whose key was never marked aborted,
|
|
1871
|
+
// so the abort grace window didn't apply). With isolateTopics=false
|
|
1872
|
+
// the sessionKey is the chat itself, so killing one session is the
|
|
1873
|
+
// same as killing the chat — behavior unchanged for the common case.
|
|
1874
|
+
await pm.kill(sessionKey).catch((err) =>
|
|
1875
|
+
console.error(`[${BOT_NAME}] abort kill failed: ${err.message}`));
|
|
1880
1876
|
dbWrite(() => db.logEvent('abort-requested', {
|
|
1881
1877
|
chat_id: chatId, user_id: msg.from?.id || null,
|
|
1882
|
-
had_active: hadActive,
|
|
1878
|
+
had_active: hadActive,
|
|
1883
1879
|
trigger: cleanText.slice(0, 40),
|
|
1884
1880
|
}), 'log abort-requested');
|
|
1885
1881
|
// Reply in the same language the user aborted in. Cyrillic-detection
|
|
@@ -2088,8 +2084,10 @@ function createBot(token) {
|
|
|
2088
2084
|
config.chats[newChatId] = { ...config.chats[oldChatId] };
|
|
2089
2085
|
delete config.chats[oldChatId];
|
|
2090
2086
|
saveConfig();
|
|
2091
|
-
|
|
2092
|
-
|
|
2087
|
+
// Chat migration is the one legit chat-wide kill: every session
|
|
2088
|
+
// (every topic) under the old chat_id is stale and must restart
|
|
2089
|
+
// under the new chat_id. Other respawn/abort paths target a
|
|
2090
|
+
// single sessionKey, but here ALL sessions are invalid.
|
|
2093
2091
|
await pm.killChat(oldChatId);
|
|
2094
2092
|
}
|
|
2095
2093
|
});
|