polygram 0.6.10 → 0.6.12
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 +37 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.12",
|
|
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
|
@@ -201,12 +201,12 @@ function dbWrite(fn, context) {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
// Convenience for the most common dbWrite pattern: log an event.
|
|
204
|
-
// Pre-0.6.9 every call site was logEvent(KIND, {...}),
|
|
204
|
+
// Pre-0.6.9 every call site was dbWrite(() => db.logEvent(KIND, {...}),
|
|
205
205
|
// `log ${KIND}`) — three repeated lines for one logical operation.
|
|
206
206
|
// This collapses them to logEvent(KIND, {...}). Same best-effort
|
|
207
207
|
// semantics; never throws.
|
|
208
208
|
function logEvent(kind, detail) {
|
|
209
|
-
logEvent(kind, detail), `log ${kind}`);
|
|
209
|
+
dbWrite(() => db.logEvent(kind, detail), `log ${kind}`);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
function recordInbound(msg) {
|
|
@@ -777,13 +777,23 @@ function dispatchHandleMessage(sessionKey, chatId, msg, bot) {
|
|
|
777
777
|
const wasAborted = isSessionRecentlyAborted(sessionKey);
|
|
778
778
|
const isReplay = msg._isReplay === true;
|
|
779
779
|
console.error(`[${sessionKey}] Error:`, err.message);
|
|
780
|
-
// Mark the row
|
|
781
|
-
//
|
|
782
|
-
//
|
|
780
|
+
// Mark the row terminal so the right thing happens on next boot:
|
|
781
|
+
// - aborted: user explicitly stopped → 'aborted' (not replayable)
|
|
782
|
+
// - shutting down: the error is "Process exited" / "Process killed"
|
|
783
|
+
// from the SIGINT/SIGTERM tearing down claude mid-turn. Mark
|
|
784
|
+
// 'replay-pending' so the next boot picks it up via
|
|
785
|
+
// getReplayCandidates. Pre-0.6.12 we marked 'failed' here, which
|
|
786
|
+
// excluded the row from replay — a clean restart between user
|
|
787
|
+
// send and reply silently dropped the turn forever.
|
|
788
|
+
// - everything else: 'failed' (genuine claude crash / timeout etc).
|
|
789
|
+
const status = wasAborted
|
|
790
|
+
? 'aborted'
|
|
791
|
+
: isShuttingDown
|
|
792
|
+
? 'replay-pending'
|
|
793
|
+
: 'failed';
|
|
783
794
|
dbWrite(() => db.setInboundHandlerStatus({
|
|
784
|
-
chat_id: chatId, msg_id: msg.message_id,
|
|
785
|
-
|
|
786
|
-
}), 'set handler_status=failed/aborted');
|
|
795
|
+
chat_id: chatId, msg_id: msg.message_id, status,
|
|
796
|
+
}), `set handler_status=${status}`);
|
|
787
797
|
logEvent('handler-error', {
|
|
788
798
|
chat_id: chatId, session_key: sessionKey,
|
|
789
799
|
msg_id: msg?.message_id,
|
|
@@ -1211,7 +1221,7 @@ async function handleConfigCallback(ctx) {
|
|
|
1211
1221
|
chat_id: chatId, thread_id: null, field: setting,
|
|
1212
1222
|
old_value: oldValue, new_value: value,
|
|
1213
1223
|
user: cmdUser, user_id: cmdUserId, source: 'inline-button',
|
|
1214
|
-
});
|
|
1224
|
+
}), `log ${setting} change`);
|
|
1215
1225
|
|
|
1216
1226
|
// Graceful respawn of the topic's session that the card is in. With
|
|
1217
1227
|
// isolateTopics=false sessionKey is the chat (one shared session). With
|
|
@@ -1387,7 +1397,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1387
1397
|
chat_id: chatId, thread_id: threadIdStr, field: 'model',
|
|
1388
1398
|
old_value: oldModel, new_value: newModel,
|
|
1389
1399
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
1390
|
-
});
|
|
1400
|
+
}), 'log model change');
|
|
1391
1401
|
const { anyActive } = requestRespawnForSession('model-change');
|
|
1392
1402
|
const ver = MODEL_VERSIONS[newModel] || newModel;
|
|
1393
1403
|
const suffix = anyActive ? ` — I'll switch when I finish` : '';
|
|
@@ -1407,7 +1417,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1407
1417
|
chat_id: chatId, thread_id: threadIdStr, field: 'effort',
|
|
1408
1418
|
old_value: oldEffort, new_value: newEffort,
|
|
1409
1419
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
1410
|
-
});
|
|
1420
|
+
}), 'log effort change');
|
|
1411
1421
|
const { anyActive } = requestRespawnForSession('effort-change');
|
|
1412
1422
|
const suffix = anyActive ? ` — I'll switch when I finish` : '';
|
|
1413
1423
|
await sendReply(`Effort → ${newEffort}${suffix}`);
|
|
@@ -1965,29 +1975,31 @@ function createBot(token) {
|
|
|
1965
1975
|
});
|
|
1966
1976
|
// Reply in the same language the user aborted in. Cyrillic-detection
|
|
1967
1977
|
// is crude but reliable for ru/en (the only two cue sets we ship).
|
|
1978
|
+
// 0.6.12 fix: pre-0.6.5 the message included a "queue cleared (N)"
|
|
1979
|
+
// suffix when N > 0; that came from drainQueuesForChat which always
|
|
1980
|
+
// returned 0 in the concurrent model and was deleted in 0.6.5.
|
|
1981
|
+
// The reference to `dropped` here was missed, so EVERY abort hit
|
|
1982
|
+
// a ReferenceError that the surrounding `catch {}` swallowed —
|
|
1983
|
+
// leaving the user with no ack at all. Reduced to "stopped vs
|
|
1984
|
+
// nothing-to-stop" since the queue-cleared variant was already
|
|
1985
|
+
// dead.
|
|
1968
1986
|
const lang = /[а-яё]/i.test(cleanText) ? 'ru' : 'en';
|
|
1969
1987
|
const strs = {
|
|
1970
|
-
en: {
|
|
1971
|
-
|
|
1972
|
-
withDropped: (n) => `Stopped. Cleared ${n} queued message${n === 1 ? '' : 's'}.`,
|
|
1973
|
-
nothing: 'Nothing to stop.',
|
|
1974
|
-
},
|
|
1975
|
-
ru: {
|
|
1976
|
-
stopped: 'Остановлено.',
|
|
1977
|
-
withDropped: (n) => `Остановлено. Очередь очищена (${n}).`,
|
|
1978
|
-
nothing: 'Нечего останавливать.',
|
|
1979
|
-
},
|
|
1988
|
+
en: { stopped: 'Stopped.', nothing: 'Nothing to stop.' },
|
|
1989
|
+
ru: { stopped: 'Остановлено.', nothing: 'Нечего останавливать.' },
|
|
1980
1990
|
}[lang];
|
|
1981
|
-
const reply = hadActive
|
|
1982
|
-
? (dropped ? strs.withDropped(dropped) : strs.stopped)
|
|
1983
|
-
: strs.nothing;
|
|
1991
|
+
const reply = hadActive ? strs.stopped : strs.nothing;
|
|
1984
1992
|
try {
|
|
1985
1993
|
await tg(bot, 'sendMessage', {
|
|
1986
1994
|
chat_id: chatId, text: reply,
|
|
1987
1995
|
reply_parameters: { message_id: msg.message_id, allow_sending_without_reply: true },
|
|
1988
1996
|
...(threadId && { message_thread_id: threadId }),
|
|
1989
1997
|
}, { source: 'abort-ack', botName: BOT_NAME });
|
|
1990
|
-
} catch {
|
|
1998
|
+
} catch (err) {
|
|
1999
|
+
// Don't swallow silently — if the ack itself fails, the operator
|
|
2000
|
+
// needs to know (the user typed stop and saw nothing).
|
|
2001
|
+
console.error(`[${BOT_NAME}] abort-ack send failed: ${err.message}`);
|
|
2002
|
+
}
|
|
1991
2003
|
return;
|
|
1992
2004
|
}
|
|
1993
2005
|
|