polygram 0.5.8 → 0.5.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.
- package/lib/process-manager.js +13 -4
- package/package.json +1 -1
- package/polygram.js +18 -5
package/lib/process-manager.js
CHANGED
|
@@ -168,6 +168,12 @@ class ProcessManager {
|
|
|
168
168
|
* If the queue is empty, kill now; otherwise mark the entry so it kills
|
|
169
169
|
* itself when the last pending resolves. Next send() respawns fresh
|
|
170
170
|
* with whatever config spawnFn reads at that moment.
|
|
171
|
+
*
|
|
172
|
+
* onRespawn fires with `wasDrained=true` ONLY when we waited for an
|
|
173
|
+
* in-flight turn to finish before swapping. The immediate-kill case
|
|
174
|
+
* (queue empty at request time) calls onRespawn with `wasDrained=false`
|
|
175
|
+
* so callers can decide whether to post a user-visible confirmation
|
|
176
|
+
* (which is redundant noise when the user wasn't waiting on a turn).
|
|
171
177
|
*/
|
|
172
178
|
requestRespawn(sessionKey, reason = 'config-change') {
|
|
173
179
|
const entry = this.procs.get(sessionKey);
|
|
@@ -181,17 +187,17 @@ class ProcessManager {
|
|
|
181
187
|
});
|
|
182
188
|
if (entry.pendingQueue.length === 0) {
|
|
183
189
|
// Queue empty — kill immediately, fire onRespawn after close.
|
|
184
|
-
this._killAndNotifyRespawn(sessionKey, reason).catch(() => {});
|
|
190
|
+
this._killAndNotifyRespawn(sessionKey, reason, false).catch(() => {});
|
|
185
191
|
return { killed: true, queued: 0 };
|
|
186
192
|
}
|
|
187
193
|
return { killed: false, queued: entry.pendingQueue.length };
|
|
188
194
|
}
|
|
189
195
|
|
|
190
|
-
async _killAndNotifyRespawn(sessionKey, reason) {
|
|
196
|
+
async _killAndNotifyRespawn(sessionKey, reason, wasDrained) {
|
|
191
197
|
const entry = this.procs.get(sessionKey);
|
|
192
198
|
await this.kill(sessionKey);
|
|
193
199
|
if (this.onRespawn && entry) {
|
|
194
|
-
try { this.onRespawn(sessionKey, reason, entry); }
|
|
200
|
+
try { this.onRespawn(sessionKey, reason, entry, wasDrained); }
|
|
195
201
|
catch (err) { this.logger.error(`[pm] onRespawn: ${err.message}`); }
|
|
196
202
|
}
|
|
197
203
|
}
|
|
@@ -321,7 +327,10 @@ class ProcessManager {
|
|
|
321
327
|
chat_id: entry.chatId,
|
|
322
328
|
reason,
|
|
323
329
|
});
|
|
324
|
-
this
|
|
330
|
+
// wasDrained=true: this path runs after the queue emptied
|
|
331
|
+
// naturally (an in-flight turn finished), so the user was
|
|
332
|
+
// waiting and the confirmation message is meaningful.
|
|
333
|
+
this._killAndNotifyRespawn(sessionKey, reason, true).catch(() => {});
|
|
325
334
|
}
|
|
326
335
|
}
|
|
327
336
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.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
|
@@ -1626,9 +1626,17 @@ function shouldHandle(msg, chatConfig, botUsername) {
|
|
|
1626
1626
|
const text = msg.text || msg.caption || '';
|
|
1627
1627
|
const isReplyToBot = msg.reply_to_message?.from?.username === botUsername;
|
|
1628
1628
|
const hasMention = text.includes(`@${botUsername}`);
|
|
1629
|
-
//
|
|
1630
|
-
//
|
|
1631
|
-
|
|
1629
|
+
// A reply targeting some other user (not the bot) is a strong signal
|
|
1630
|
+
// "this message is for that person, not me". Paired users normally
|
|
1631
|
+
// bypass requireMention, but not in this case — without the guard a
|
|
1632
|
+
// paired user saying "Gotcha!" to a teammate gets processed by the
|
|
1633
|
+
// bot just because the user is paired, which is what bit us in
|
|
1634
|
+
// UMI Group on 0.5.9 (bot leaked reasoning as a reply to "Gotcha!").
|
|
1635
|
+
const repliesToOtherUser = !!msg.reply_to_message
|
|
1636
|
+
&& msg.reply_to_message.from?.username !== botUsername;
|
|
1637
|
+
// Paired users bypass requireMention — operator-trusted, no @ needed
|
|
1638
|
+
// every time. Skipped when they're replying to a non-bot user (above).
|
|
1639
|
+
const paired = !repliesToOtherUser && pairings && msg.from?.id
|
|
1632
1640
|
? pairings.hasLivePairing({ bot_name: BOT_NAME, user_id: msg.from.id, chat_id: chatId })
|
|
1633
1641
|
: false;
|
|
1634
1642
|
if (!isReplyToBot && !hasMention && !paired) return false;
|
|
@@ -2174,8 +2182,13 @@ async function main() {
|
|
|
2174
2182
|
},
|
|
2175
2183
|
// Fires after a graceful /model or /effort drain has actually
|
|
2176
2184
|
// swapped to the new settings. Post a confirmation back to the
|
|
2177
|
-
// chat
|
|
2178
|
-
|
|
2185
|
+
// chat ONLY when wasDrained=true — the user actively waited for an
|
|
2186
|
+
// in-flight turn to finish before the switch took effect, so the
|
|
2187
|
+
// explicit "switched" message is meaningful. When the kill was
|
|
2188
|
+
// immediate (queue empty), the inline-card update + button toast
|
|
2189
|
+
// already convey "done", and a separate message is just noise.
|
|
2190
|
+
onRespawn: (sessionKey, reason, entry, wasDrained) => {
|
|
2191
|
+
if (!wasDrained) return;
|
|
2179
2192
|
const chatId = entry.chatId;
|
|
2180
2193
|
if (!chatId) return;
|
|
2181
2194
|
const chatConfig = config.chats[chatId];
|