polygram 0.4.9 → 0.4.11
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/lib/process-manager.js +26 -9
- package/package.json +1 -1
- package/polygram.js +25 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "polygram",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.11",
|
|
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/lib/process-manager.js
CHANGED
|
@@ -54,11 +54,12 @@ class ProcessManager {
|
|
|
54
54
|
db = null,
|
|
55
55
|
logger = console,
|
|
56
56
|
killTimeoutMs = DEFAULT_KILL_TIMEOUT_MS,
|
|
57
|
-
onInit = null,
|
|
58
|
-
onResult = null,
|
|
59
|
-
onClose = null,
|
|
60
|
-
onStreamChunk = null
|
|
61
|
-
onToolUse = null,
|
|
57
|
+
onInit = null, // (sessionKey, event, entry) → void
|
|
58
|
+
onResult = null, // (sessionKey, event, entry, pending) → void
|
|
59
|
+
onClose = null, // (sessionKey, code, entry) → void
|
|
60
|
+
onStreamChunk = null, // (sessionKey, partialText, entry) → void — routes to pendingQueue[0]
|
|
61
|
+
onToolUse = null, // (sessionKey, toolName, entry) → void — routes to pendingQueue[0]
|
|
62
|
+
onRespawn = null, // (sessionKey, reason, entry) → void — fires after graceful drain-and-kill
|
|
62
63
|
} = {}) {
|
|
63
64
|
if (!spawnFn) throw new Error('spawnFn required');
|
|
64
65
|
this.cap = cap;
|
|
@@ -71,6 +72,7 @@ class ProcessManager {
|
|
|
71
72
|
this.onClose = onClose;
|
|
72
73
|
this.onStreamChunk = onStreamChunk;
|
|
73
74
|
this.onToolUse = onToolUse;
|
|
75
|
+
this.onRespawn = onRespawn;
|
|
74
76
|
this.procs = new Map();
|
|
75
77
|
}
|
|
76
78
|
|
|
@@ -137,13 +139,22 @@ class ProcessManager {
|
|
|
137
139
|
queued: entry.pendingQueue.length,
|
|
138
140
|
});
|
|
139
141
|
if (entry.pendingQueue.length === 0) {
|
|
140
|
-
//
|
|
141
|
-
this.
|
|
142
|
+
// Queue empty — kill immediately, fire onRespawn after close.
|
|
143
|
+
this._killAndNotifyRespawn(sessionKey, reason).catch(() => {});
|
|
142
144
|
return { killed: true, queued: 0 };
|
|
143
145
|
}
|
|
144
146
|
return { killed: false, queued: entry.pendingQueue.length };
|
|
145
147
|
}
|
|
146
148
|
|
|
149
|
+
async _killAndNotifyRespawn(sessionKey, reason) {
|
|
150
|
+
const entry = this.procs.get(sessionKey);
|
|
151
|
+
await this.kill(sessionKey);
|
|
152
|
+
if (this.onRespawn && entry) {
|
|
153
|
+
try { this.onRespawn(sessionKey, reason, entry); }
|
|
154
|
+
catch (err) { this.logger.error(`[pm] onRespawn: ${err.message}`); }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
147
158
|
async kill(sessionKey) {
|
|
148
159
|
const entry = this.procs.get(sessionKey);
|
|
149
160
|
if (!entry) return;
|
|
@@ -257,7 +268,8 @@ class ProcessManager {
|
|
|
257
268
|
} else {
|
|
258
269
|
entry.inFlight = false;
|
|
259
270
|
// Graceful drain-and-respawn: if caller asked for a respawn
|
|
260
|
-
// (e.g. /model change) and we just emptied the queue, kill now
|
|
271
|
+
// (e.g. /model change) and we just emptied the queue, kill now
|
|
272
|
+
// and fire onRespawn so the caller can post confirmation.
|
|
261
273
|
if (entry.needsRespawn) {
|
|
262
274
|
const reason = entry.needsRespawn;
|
|
263
275
|
entry.needsRespawn = null;
|
|
@@ -266,7 +278,7 @@ class ProcessManager {
|
|
|
266
278
|
chat_id: entry.chatId,
|
|
267
279
|
reason,
|
|
268
280
|
});
|
|
269
|
-
this.
|
|
281
|
+
this._killAndNotifyRespawn(sessionKey, reason).catch(() => {});
|
|
270
282
|
}
|
|
271
283
|
}
|
|
272
284
|
}
|
|
@@ -398,6 +410,11 @@ class ProcessManager {
|
|
|
398
410
|
maxTurnMs,
|
|
399
411
|
);
|
|
400
412
|
pending.maxTimer = maxTimer;
|
|
413
|
+
// Give callers a hook so they can transition user-visible state
|
|
414
|
+
// (e.g. status reaction "👀 queued" → "🤔 thinking") the moment
|
|
415
|
+
// Claude actually starts this pending, not the moment it arrived.
|
|
416
|
+
try { context?.onActivate?.(); }
|
|
417
|
+
catch (err) { this.logger.error(`[${entry.label}] onActivate: ${err.message}`); }
|
|
401
418
|
};
|
|
402
419
|
|
|
403
420
|
pending.resetIdleTimer = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
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
|
@@ -1246,7 +1246,11 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1246
1246
|
},
|
|
1247
1247
|
logError: (m) => console.error(`[${label}] ${m}`),
|
|
1248
1248
|
});
|
|
1249
|
-
|
|
1249
|
+
// Start at QUEUED (👀) so user sees their message was received but
|
|
1250
|
+
// not yet being worked on. pm calls context.onActivate when this
|
|
1251
|
+
// pending becomes the queue head (Claude is actually starting it),
|
|
1252
|
+
// at which point we flip to THINKING (🤔).
|
|
1253
|
+
reactor.setState('QUEUED');
|
|
1250
1254
|
|
|
1251
1255
|
try {
|
|
1252
1256
|
// Pass streamer + reactor as per-turn context. pm's callbacks pick
|
|
@@ -1254,6 +1258,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
1254
1258
|
// get routed to their own streamer/reactor.
|
|
1255
1259
|
const result = await sendToProcess(sessionKey, prompt, {
|
|
1256
1260
|
streamer, reactor, sourceMsgId: msg.message_id,
|
|
1261
|
+
onActivate: () => reactor.setState('THINKING'),
|
|
1257
1262
|
});
|
|
1258
1263
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
|
1259
1264
|
|
|
@@ -1904,6 +1909,25 @@ async function main() {
|
|
|
1904
1909
|
const r = head?.context?.reactor;
|
|
1905
1910
|
if (r) r.setState(classifyToolName(toolName));
|
|
1906
1911
|
},
|
|
1912
|
+
// Fires after a graceful /model or /effort drain has actually
|
|
1913
|
+
// swapped to the new settings. Post a confirmation back to the
|
|
1914
|
+
// chat so the user knows the switch happened.
|
|
1915
|
+
onRespawn: (sessionKey, reason, entry) => {
|
|
1916
|
+
const chatId = entry.chatId;
|
|
1917
|
+
if (!chatId) return;
|
|
1918
|
+
const chatConfig = config.chats[chatId];
|
|
1919
|
+
if (!chatConfig) return;
|
|
1920
|
+
const text = reason === 'model-change'
|
|
1921
|
+
? `✓ Using ${chatConfig.model} now.`
|
|
1922
|
+
: reason === 'effort-change'
|
|
1923
|
+
? `✓ Effort is ${chatConfig.effort} now.`
|
|
1924
|
+
: `✓ Ready.`;
|
|
1925
|
+
const threadId = entry.threadId || undefined;
|
|
1926
|
+
tg(bot, 'sendMessage', {
|
|
1927
|
+
chat_id: chatId, text,
|
|
1928
|
+
...(threadId && { message_thread_id: threadId }),
|
|
1929
|
+
}, { source: 'respawn-confirm', botName: BOT_NAME }).catch(() => {});
|
|
1930
|
+
},
|
|
1907
1931
|
});
|
|
1908
1932
|
|
|
1909
1933
|
console.log(`polygram (LRU cap=${cap}, SQLite source of truth)`);
|