polygram 0.12.0-rc.39 → 0.12.0-rc.40
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/handlers/config-callback.js +18 -5
- package/lib/handlers/slash-commands.js +17 -6
- package/lib/session-key.js +20 -0
- package/package.json +1 -1
- package/polygram.js +2 -2
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
'use strict';
|
|
18
18
|
|
|
19
19
|
const { toTelegramHtml } = require('../telegram/format');
|
|
20
|
-
const { getTopicConfig } = require('../session-key');
|
|
20
|
+
const { getTopicConfig, getConfigWriteScope } = require('../session-key');
|
|
21
21
|
|
|
22
22
|
const MODEL_OPTIONS = ['opus', 'sonnet', 'haiku'];
|
|
23
23
|
const EFFORT_OPTIONS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
@@ -30,6 +30,7 @@ function createHandleConfigCallback({
|
|
|
30
30
|
getSessionKey,
|
|
31
31
|
formatConfigInfoText,
|
|
32
32
|
buildConfigKeyboard,
|
|
33
|
+
saveConfig = () => {},
|
|
33
34
|
botName,
|
|
34
35
|
logger = console,
|
|
35
36
|
} = {}) {
|
|
@@ -58,17 +59,29 @@ function createHandleConfigCallback({
|
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
// Write to the scope the card belongs to: a topic card targets THAT topic
|
|
63
|
+
// (so Music ≠ General), a chat-level card the chat root. Resolving the
|
|
64
|
+
// thread BEFORE the already-set check so "Already X" compares against the
|
|
65
|
+
// topic's effective value, not the chat root (2026-06-12 bug).
|
|
66
|
+
const callbackThreadIdEarly = ctx.callbackQuery.message?.message_thread_id?.toString() || null;
|
|
67
|
+
const { scope: writeScope, threadId: writeThreadId } =
|
|
68
|
+
getConfigWriteScope(chatConfig, callbackThreadIdEarly);
|
|
69
|
+
const oldValue = writeScope[setting] != null ? writeScope[setting] : chatConfig[setting];
|
|
62
70
|
if (oldValue === value) {
|
|
63
71
|
await ctx.answerCallbackQuery({ text: `Already ${value}` }).catch(() => {});
|
|
64
72
|
return;
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
writeScope[setting] = value;
|
|
76
|
+
// Persist to config.json so the change survives a restart — without this,
|
|
77
|
+
// every /model tap was lost on the next deploy (2026-06-12). Best-effort:
|
|
78
|
+
// never let a disk hiccup swallow the in-memory change + live application.
|
|
79
|
+
try { saveConfig(); }
|
|
80
|
+
catch (err) { logger.error?.(`[${botName}] config-callback saveConfig failed: ${err.message}`); }
|
|
68
81
|
const cmdUserId = ctx.callbackQuery.from?.id || null;
|
|
69
82
|
const cmdUser = ctx.callbackQuery.from?.first_name || ctx.callbackQuery.from?.username || null;
|
|
70
83
|
dbWrite(() => db.logConfigChange({
|
|
71
|
-
chat_id: chatId, thread_id:
|
|
84
|
+
chat_id: chatId, thread_id: writeThreadId, field: setting,
|
|
72
85
|
old_value: oldValue, new_value: value,
|
|
73
86
|
user: cmdUser, user_id: cmdUserId, source: 'inline-button',
|
|
74
87
|
}), `log ${setting} change`);
|
|
@@ -77,7 +90,7 @@ function createHandleConfigCallback({
|
|
|
77
90
|
// via setModel / applyFlagSettings; chatConfig is already updated
|
|
78
91
|
// on disk above so a missing live session still picks up the new
|
|
79
92
|
// value on its next cold spawn.
|
|
80
|
-
const callbackThreadId =
|
|
93
|
+
const callbackThreadId = callbackThreadIdEarly;
|
|
81
94
|
const callbackSessionKey = getSessionKey(chatId, callbackThreadId, chatConfig);
|
|
82
95
|
let applied = false;
|
|
83
96
|
if (setting === 'effort') {
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
|
|
28
28
|
'use strict';
|
|
29
29
|
|
|
30
|
+
const { getConfigWriteScope } = require('../session-key');
|
|
31
|
+
|
|
30
32
|
function createSlashCommands({
|
|
31
33
|
config,
|
|
32
34
|
db,
|
|
@@ -40,6 +42,7 @@ function createSlashCommands({
|
|
|
40
42
|
getOrSpawnForChat,
|
|
41
43
|
parsePairCodeArgs,
|
|
42
44
|
modelVersionsDesc,
|
|
45
|
+
saveConfig = () => {},
|
|
43
46
|
botName,
|
|
44
47
|
logEvent,
|
|
45
48
|
logger = console,
|
|
@@ -214,10 +217,15 @@ function createSlashCommands({
|
|
|
214
217
|
if (botAllowsCommands && text.startsWith('/model ')) {
|
|
215
218
|
const newModel = text.slice(7).trim();
|
|
216
219
|
if (['opus', 'sonnet', 'haiku'].includes(newModel)) {
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
// Write to the topic when in one (so Music ≠ General) and persist to
|
|
221
|
+
// config.json so it survives restarts — both were missing (2026-06-12).
|
|
222
|
+
const { scope: wScope, threadId: wThread } = getConfigWriteScope(chatConfig, threadIdStr);
|
|
223
|
+
const oldModel = wScope.model != null ? wScope.model : chatConfig.model;
|
|
224
|
+
wScope.model = newModel;
|
|
225
|
+
try { saveConfig(); }
|
|
226
|
+
catch (err) { logger.error?.(`[${botName}] /model saveConfig failed: ${err.message}`); }
|
|
219
227
|
dbWrite(() => db.logConfigChange({
|
|
220
|
-
chat_id: chatId, thread_id:
|
|
228
|
+
chat_id: chatId, thread_id: wThread, field: 'model',
|
|
221
229
|
old_value: oldModel, new_value: newModel,
|
|
222
230
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
223
231
|
}), 'log model change');
|
|
@@ -234,10 +242,13 @@ function createSlashCommands({
|
|
|
234
242
|
if (botAllowsCommands && text.startsWith('/effort ')) {
|
|
235
243
|
const newEffort = text.slice(8).trim();
|
|
236
244
|
if (['low', 'medium', 'high', 'xhigh', 'max'].includes(newEffort)) {
|
|
237
|
-
const
|
|
238
|
-
|
|
245
|
+
const { scope: wScope, threadId: wThread } = getConfigWriteScope(chatConfig, threadIdStr);
|
|
246
|
+
const oldEffort = wScope.effort != null ? wScope.effort : chatConfig.effort;
|
|
247
|
+
wScope.effort = newEffort;
|
|
248
|
+
try { saveConfig(); }
|
|
249
|
+
catch (err) { logger.error?.(`[${botName}] /effort saveConfig failed: ${err.message}`); }
|
|
239
250
|
dbWrite(() => db.logConfigChange({
|
|
240
|
-
chat_id: chatId, thread_id:
|
|
251
|
+
chat_id: chatId, thread_id: wThread, field: 'effort',
|
|
241
252
|
old_value: oldEffort, new_value: newEffort,
|
|
242
253
|
user: cmdUser, user_id: cmdUserId, source: 'command',
|
|
243
254
|
}), 'log effort change');
|
package/lib/session-key.js
CHANGED
|
@@ -106,10 +106,30 @@ function getTopicConfig(chatConfig, threadId) {
|
|
|
106
106
|
return overrides;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the config object a per-chat/topic setting (model/effort) should be
|
|
111
|
+
* WRITTEN to, given where the command/card was used. When in a topic, return
|
|
112
|
+
* (creating if needed) that topic's override entry — so a /model in the Music
|
|
113
|
+
* topic targets Music alone and matches what the per-topic card displays,
|
|
114
|
+
* instead of leaking to the chat root and every other topic that inherits it
|
|
115
|
+
* (the 2026-06-12 "/model in Music does nothing" bug). At the chat level
|
|
116
|
+
* (no thread), the writable scope is the chat config itself.
|
|
117
|
+
*
|
|
118
|
+
* @returns {{ scope: object, threadId: (string|null) }}
|
|
119
|
+
*/
|
|
120
|
+
function getConfigWriteScope(chatConfig, threadId) {
|
|
121
|
+
const tid = (threadId == null || threadId === '') ? null : String(threadId);
|
|
122
|
+
if (!tid) return { scope: chatConfig, threadId: null };
|
|
123
|
+
chatConfig.topics = chatConfig.topics || {};
|
|
124
|
+
chatConfig.topics[tid] = chatConfig.topics[tid] || {};
|
|
125
|
+
return { scope: chatConfig.topics[tid], threadId: tid };
|
|
126
|
+
}
|
|
127
|
+
|
|
109
128
|
module.exports = {
|
|
110
129
|
getSessionKey,
|
|
111
130
|
getChatIdFromKey,
|
|
112
131
|
getThreadIdFromKey,
|
|
113
132
|
getTopicName,
|
|
114
133
|
getTopicConfig,
|
|
134
|
+
getConfigWriteScope,
|
|
115
135
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.12.0-rc.
|
|
3
|
+
"version": "0.12.0-rc.40",
|
|
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
|
@@ -2459,7 +2459,7 @@ async function main() {
|
|
|
2459
2459
|
});
|
|
2460
2460
|
handleConfigCallback = createHandleConfigCallback({
|
|
2461
2461
|
config, db, dbWrite, pm, getSessionKey,
|
|
2462
|
-
formatConfigInfoText, buildConfigKeyboard,
|
|
2462
|
+
formatConfigInfoText, buildConfigKeyboard, saveConfig,
|
|
2463
2463
|
botName: BOT_NAME, logger: console,
|
|
2464
2464
|
});
|
|
2465
2465
|
handleAbortIfRequested = createHandleAbort({
|
|
@@ -2532,7 +2532,7 @@ async function main() {
|
|
|
2532
2532
|
config, db, dbWrite, pm, pairings, parsePairingTtl,
|
|
2533
2533
|
contextHintShown, formatContextReply, getClaudeSessionId,
|
|
2534
2534
|
getOrSpawnForChat, parsePairCodeArgs,
|
|
2535
|
-
modelVersionsDesc: MODEL_VERSIONS_DESC,
|
|
2535
|
+
modelVersionsDesc: MODEL_VERSIONS_DESC, saveConfig,
|
|
2536
2536
|
botName: BOT_NAME, logEvent, logger: console,
|
|
2537
2537
|
});
|
|
2538
2538
|
console.log('[polygram] using SDK ProcessManager');
|