polygram 0.8.0-rc.2 → 0.8.0-rc.4
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/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/polygram.js +64 -21
|
@@ -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.
|
|
4
|
+
"version": "0.8.0-rc.4",
|
|
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.
|
|
3
|
+
"version": "0.8.0-rc.4",
|
|
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
|
@@ -1709,16 +1709,34 @@ async function handleConfigCallback(ctx) {
|
|
|
1709
1709
|
user: cmdUser, user_id: cmdUserId, source: 'inline-button',
|
|
1710
1710
|
}), `log ${setting} change`);
|
|
1711
1711
|
|
|
1712
|
-
// Graceful
|
|
1712
|
+
// Graceful application of the change to the topic's session. With
|
|
1713
1713
|
// isolateTopics=false sessionKey is the chat (one shared session). With
|
|
1714
1714
|
// isolateTopics=true sessionKey carries the topic, so other topics'
|
|
1715
1715
|
// in-flight turns are not disturbed and the card update + button toast
|
|
1716
|
-
// only affect the user's own context.
|
|
1717
|
-
//
|
|
1716
|
+
// only affect the user's own context.
|
|
1717
|
+
//
|
|
1718
|
+
// CLI pm: requestRespawn drains pending turns then kills the process;
|
|
1719
|
+
// the next user message spawns fresh with the updated chatConfig.
|
|
1720
|
+
// SDK pm: applies live to the running Query via setModel /
|
|
1721
|
+
// applyFlagSettings — no respawn needed, change takes effect for the
|
|
1722
|
+
// rest of the in-flight turn AND all future ones. Falls back to
|
|
1723
|
+
// {killed: false} if neither method is available, leaving the new
|
|
1724
|
+
// chatConfig value to be picked up by the next cold spawn.
|
|
1718
1725
|
const callbackThreadId = ctx.callbackQuery.message?.message_thread_id?.toString() || null;
|
|
1719
1726
|
const callbackSessionKey = getSessionKey(chatId, callbackThreadId, chatConfig);
|
|
1720
1727
|
const reason = setting === 'model' ? 'model-change' : 'effort-change';
|
|
1721
|
-
|
|
1728
|
+
let respawn;
|
|
1729
|
+
if (typeof pm.requestRespawn === 'function') {
|
|
1730
|
+
respawn = pm.requestRespawn(callbackSessionKey, reason);
|
|
1731
|
+
} else if (setting === 'effort' && typeof pm.applyFlagSettings === 'function') {
|
|
1732
|
+
const ok = await pm.applyFlagSettings(callbackSessionKey, { effortLevel: value });
|
|
1733
|
+
respawn = { killed: ok };
|
|
1734
|
+
} else if (setting === 'model' && typeof pm.setModel === 'function') {
|
|
1735
|
+
const ok = await pm.setModel(callbackSessionKey, value);
|
|
1736
|
+
respawn = { killed: ok };
|
|
1737
|
+
} else {
|
|
1738
|
+
respawn = { killed: false };
|
|
1739
|
+
}
|
|
1722
1740
|
const anyActive = !respawn.killed;
|
|
1723
1741
|
|
|
1724
1742
|
// Re-render the card with updated ✓ + the same help text shown initially.
|
|
@@ -1885,13 +1903,18 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1885
1903
|
}
|
|
1886
1904
|
try {
|
|
1887
1905
|
const u = await q.getContextUsage();
|
|
1888
|
-
|
|
1906
|
+
// SDK returns percentage in 0-100 scale (verified rc.3 prod
|
|
1907
|
+
// — saw "77" for a 77%-used context). Display directly.
|
|
1908
|
+
const pct = (u?.percentage ?? 0).toFixed(0);
|
|
1889
1909
|
const total = (u?.totalTokens ?? 0).toLocaleString();
|
|
1890
1910
|
const max = (u?.maxTokens ?? 0).toLocaleString();
|
|
1891
1911
|
const lines = [`📚 Context: ${total} / ${max} tokens (${pct}%)`];
|
|
1892
1912
|
if (u?.model) lines.push(`Model: ${u.model}`);
|
|
1893
1913
|
if (u?.isAutoCompactEnabled && u?.autoCompactThreshold) {
|
|
1894
|
-
|
|
1914
|
+
// autoCompactThreshold scale is currently unverified; assume
|
|
1915
|
+
// matches percentage (0-100). If it turns out to be 0-1 we'll
|
|
1916
|
+
// see something like "Auto-compact at 0%" and can flip back.
|
|
1917
|
+
const thrPct = u.autoCompactThreshold.toFixed(0);
|
|
1895
1918
|
lines.push(`Auto-compact at ${thrPct}%.`);
|
|
1896
1919
|
}
|
|
1897
1920
|
// Top-3 categories by token cost so the user knows where the
|
|
@@ -1969,17 +1992,33 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1969
1992
|
}
|
|
1970
1993
|
return;
|
|
1971
1994
|
}
|
|
1972
|
-
// Graceful
|
|
1973
|
-
// isolateTopics=false the sessionKey is just the
|
|
1974
|
-
// session for the whole chat — every topic
|
|
1975
|
-
// With isolateTopics=true each topic is a
|
|
1976
|
-
// /model in topic A should NOT disturb
|
|
1977
|
-
// post a phantom "✓ Using sonnet now"
|
|
1978
|
-
//
|
|
1979
|
-
//
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1995
|
+
// Graceful application of a model/effort change to the user's CURRENT
|
|
1996
|
+
// session only. With isolateTopics=false the sessionKey is just the
|
|
1997
|
+
// chat (one shared session for the whole chat — every topic
|
|
1998
|
+
// respawns implicitly). With isolateTopics=true each topic is a
|
|
1999
|
+
// separate session, and a /model in topic A should NOT disturb
|
|
2000
|
+
// topic B's in-flight turn or post a phantom "✓ Using sonnet now"
|
|
2001
|
+
// in a topic that didn't ask.
|
|
2002
|
+
//
|
|
2003
|
+
// CLI pm: requestRespawn drains pending turns then kills the process;
|
|
2004
|
+
// the next user message spawns fresh with the updated chatConfig.
|
|
2005
|
+
// SDK pm: applies live to the running Query via setModel /
|
|
2006
|
+
// applyFlagSettings — no respawn needed, change takes effect for
|
|
2007
|
+
// the rest of the in-flight turn AND all future ones.
|
|
2008
|
+
const applyConfigChange = async (reason, setting, value) => {
|
|
2009
|
+
if (typeof pm.requestRespawn === 'function') {
|
|
2010
|
+
const res = pm.requestRespawn(sessionKey, reason);
|
|
2011
|
+
return { queued: res.queued, anyActive: !res.killed };
|
|
2012
|
+
}
|
|
2013
|
+
if (setting === 'effort' && typeof pm.applyFlagSettings === 'function') {
|
|
2014
|
+
const ok = await pm.applyFlagSettings(sessionKey, { effortLevel: value });
|
|
2015
|
+
return { queued: 0, anyActive: !ok };
|
|
2016
|
+
}
|
|
2017
|
+
if (setting === 'model' && typeof pm.setModel === 'function') {
|
|
2018
|
+
const ok = await pm.setModel(sessionKey, value);
|
|
2019
|
+
return { queued: 0, anyActive: !ok };
|
|
2020
|
+
}
|
|
2021
|
+
return { queued: 0, anyActive: false };
|
|
1983
2022
|
};
|
|
1984
2023
|
|
|
1985
2024
|
if (botAllowsCommands && text.startsWith('/model ')) {
|
|
@@ -1993,7 +2032,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1993
2032
|
old_value: oldModel, new_value: newModel,
|
|
1994
2033
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
1995
2034
|
}), 'log model change');
|
|
1996
|
-
const { anyActive } =
|
|
2035
|
+
const { anyActive } = await applyConfigChange('model-change', 'model', newModel);
|
|
1997
2036
|
const ver = MODEL_VERSIONS[newModel] || newModel;
|
|
1998
2037
|
const suffix = anyActive ? ` — I'll switch when I finish` : '';
|
|
1999
2038
|
await sendReply(`Model → ${newModel} (${ver})${suffix}`);
|
|
@@ -2013,7 +2052,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2013
2052
|
old_value: oldEffort, new_value: newEffort,
|
|
2014
2053
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
2015
2054
|
}), 'log effort change');
|
|
2016
|
-
const { anyActive } =
|
|
2055
|
+
const { anyActive } = await applyConfigChange('effort-change', 'effort', newEffort);
|
|
2017
2056
|
const suffix = anyActive ? ` — I'll switch when I finish` : '';
|
|
2018
2057
|
await sendReply(`Effort → ${newEffort}${suffix}`);
|
|
2019
2058
|
} else {
|
|
@@ -2489,11 +2528,15 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2489
2528
|
const q = entry?.query;
|
|
2490
2529
|
if (q && typeof q.getContextUsage === 'function') {
|
|
2491
2530
|
q.getContextUsage().then((usage) => {
|
|
2531
|
+
// SDK returns percentage in 0-100 scale, not 0-1.
|
|
2532
|
+
// Pre-rc.4 we treated it as a 0-1 ratio and multiplied
|
|
2533
|
+
// by 100, which displayed "7700% full" for a 77%-used
|
|
2534
|
+
// context (and fired below the intended 85% threshold).
|
|
2492
2535
|
const pct = usage?.percentage ?? 0;
|
|
2493
|
-
if (pct <
|
|
2536
|
+
if (pct < 85) return;
|
|
2494
2537
|
return tg(bot, 'sendMessage', {
|
|
2495
2538
|
chat_id: chatId,
|
|
2496
|
-
text: `📚 Context window ${
|
|
2539
|
+
text: `📚 Context window ${pct.toFixed(0)}% full. Send /new to start fresh — older messages will start dropping soon.`,
|
|
2497
2540
|
...(threadId ? { message_thread_id: threadId } : {}),
|
|
2498
2541
|
}, { source: 'context-full-hint', botName: BOT_NAME });
|
|
2499
2542
|
}).catch((err) => {
|