polygram 0.8.0-rc.6 → 0.8.0-rc.8
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-sdk.js +20 -1
- package/package.json +1 -1
- package/polygram.js +41 -3
|
@@ -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.8",
|
|
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",
|
|
@@ -470,6 +470,7 @@ class ProcessManagerSdk {
|
|
|
470
470
|
entry.inputController.push({
|
|
471
471
|
type: 'user',
|
|
472
472
|
message: { role: 'user', content: head.prompt },
|
|
473
|
+
parent_tool_use_id: null,
|
|
473
474
|
});
|
|
474
475
|
} catch (err) {
|
|
475
476
|
entry.pendingQueue.shift();
|
|
@@ -655,6 +656,7 @@ class ProcessManagerSdk {
|
|
|
655
656
|
entry.inputController.push({
|
|
656
657
|
type: 'user',
|
|
657
658
|
message: { role: 'user', content: prompt },
|
|
659
|
+
parent_tool_use_id: null,
|
|
658
660
|
});
|
|
659
661
|
} catch (err) {
|
|
660
662
|
const idx = entry.pendingQueue.indexOf(pending);
|
|
@@ -754,13 +756,30 @@ class ProcessManagerSdk {
|
|
|
754
756
|
* Returns true if push succeeded; false if session not found or
|
|
755
757
|
* input controller closed.
|
|
756
758
|
*/
|
|
757
|
-
steer(sessionKey, text, { shouldQuery =
|
|
759
|
+
steer(sessionKey, text, { shouldQuery = false } = {}) {
|
|
758
760
|
const entry = this.procs.get(sessionKey);
|
|
759
761
|
if (!entry || entry.closed) return false;
|
|
760
762
|
try {
|
|
763
|
+
// 0.8.0-rc.7 (per v4 plan §0 row 9 + Phase 2 step 1's original
|
|
764
|
+
// shape): push with `shouldQuery: false` so the SDK appends to
|
|
765
|
+
// the transcript without trying to terminate the in-flight turn.
|
|
766
|
+
// The previous default `shouldQuery: true` triggered the CLI
|
|
767
|
+
// binary's `m87` gate (transcript well-formedness check) which
|
|
768
|
+
// emitted `result.subtype = error_during_execution` whenever a
|
|
769
|
+
// plain-text user message arrived while the assistant was mid-
|
|
770
|
+
// tool-use. With shouldQuery=false the message merges into the
|
|
771
|
+
// next natural user turn — the in-flight tools complete first,
|
|
772
|
+
// then the assistant sees the steered context.
|
|
773
|
+
//
|
|
774
|
+
// parent_tool_use_id is required by SDKUserMessage type
|
|
775
|
+
// (sdk.d.ts:3479-3498). The SDK runtime checks `!== null` in
|
|
776
|
+
// multiple places; omitting it falls through to wrong handling
|
|
777
|
+
// branches. The SDK's own `mz.send()` and `pz` replay set it
|
|
778
|
+
// to null explicitly.
|
|
761
779
|
entry.inputController.push({
|
|
762
780
|
type: 'user',
|
|
763
781
|
message: { role: 'user', content: text },
|
|
782
|
+
parent_tool_use_id: null,
|
|
764
783
|
priority: 'now',
|
|
765
784
|
shouldQuery,
|
|
766
785
|
});
|
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.8",
|
|
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
|
@@ -2412,9 +2412,15 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2412
2412
|
// chatConfig.autosteer === false). CLI pm always falls through
|
|
2413
2413
|
// to the queue-FIFO path (no steer primitive on stream-json).
|
|
2414
2414
|
//
|
|
2415
|
-
// The steered message gets a
|
|
2415
|
+
// The steered message gets a ✍ reaction so the user knows it
|
|
2416
2416
|
// landed; no separate reply is generated (the in-flight turn's
|
|
2417
2417
|
// response covers both messages, OpenClaw-style).
|
|
2418
|
+
//
|
|
2419
|
+
// Reaction emoji must be from Telegram's curated allowlist
|
|
2420
|
+
// (~60 standard emoji per core.telegram.org/bots/api#availablereactions).
|
|
2421
|
+
// 🛞 (steering wheel) is NOT on it — Telegram returns
|
|
2422
|
+
// 400: REACTION_INVALID. ✍ ("writing/noting") is on the list and
|
|
2423
|
+
// conveys "incorporating this".
|
|
2418
2424
|
const chatAutosteer = chatConfig.autosteer != null
|
|
2419
2425
|
? chatConfig.autosteer
|
|
2420
2426
|
: config.bot?.autosteer;
|
|
@@ -2432,7 +2438,7 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2432
2438
|
tg(bot, 'setMessageReaction', {
|
|
2433
2439
|
chat_id: chatId,
|
|
2434
2440
|
message_id: msg.message_id,
|
|
2435
|
-
reaction: [{ type: 'emoji', emoji: '
|
|
2441
|
+
reaction: [{ type: 'emoji', emoji: '✍' }],
|
|
2436
2442
|
}, { source: 'autosteer-ack', botName: BOT_NAME }).catch((err) => {
|
|
2437
2443
|
console.error(`[${label}] autosteer reaction: ${err.message}`);
|
|
2438
2444
|
});
|
|
@@ -2441,7 +2447,13 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2441
2447
|
text_len: prompt?.length ?? 0,
|
|
2442
2448
|
});
|
|
2443
2449
|
stopTyping();
|
|
2444
|
-
|
|
2450
|
+
// 0.8.0-rc.8: clear() instead of stop() so the THINKING/QUEUED
|
|
2451
|
+
// 👀 reaction set by the reactor at QUEUED-state actually
|
|
2452
|
+
// disappears from the user's message. reactor.stop() only
|
|
2453
|
+
// cancels timers; the visible emoji persists indefinitely
|
|
2454
|
+
// without an explicit clear() — that's why production showed
|
|
2455
|
+
// 👀 stuck on every steered follow-up under rc.6/rc.7.
|
|
2456
|
+
await reactor.clear().catch(() => {});
|
|
2445
2457
|
markReplied();
|
|
2446
2458
|
return;
|
|
2447
2459
|
}
|
|
@@ -2565,6 +2577,26 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2565
2577
|
// those still markReplied silently.
|
|
2566
2578
|
if (result.text === 'NO_REPLY') { markReplied(); return; }
|
|
2567
2579
|
if (!result.text) {
|
|
2580
|
+
// 0.8.0-rc.7: tool-only completion is NOT an error. Under SDK
|
|
2581
|
+
// pm, a turn that ends after running tools (no closing text
|
|
2582
|
+
// block) leaves result.text empty even though the bot DID
|
|
2583
|
+
// respond — via tool side effects the user already saw. Don't
|
|
2584
|
+
// post a "No response generated" apology in that case; it's
|
|
2585
|
+
// confusing and it spams the chat. Just clear the reactor
|
|
2586
|
+
// (otherwise 👀 stays stuck — reactor.stop() doesn't remove
|
|
2587
|
+
// the emoji visually) and silently mark replied.
|
|
2588
|
+
const toolOnlyTurn = (result.metrics?.numToolUses ?? 0) > 0
|
|
2589
|
+
&& (result.metrics?.numAssistantMessages ?? 0) > 0;
|
|
2590
|
+
if (toolOnlyTurn) {
|
|
2591
|
+
await reactor.clear().catch(() => {});
|
|
2592
|
+
logEvent('tool-only-completion', {
|
|
2593
|
+
chat_id: chatId, msg_id: msg.message_id, bot: BOT_NAME,
|
|
2594
|
+
num_tool_uses: result.metrics?.numToolUses,
|
|
2595
|
+
num_assistant_messages: result.metrics?.numAssistantMessages,
|
|
2596
|
+
});
|
|
2597
|
+
markReplied();
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2568
2600
|
// 0.7.1: if the fallback send itself fails, throw rather than
|
|
2569
2601
|
// silently markReplied — the user gets nothing AND the inbound
|
|
2570
2602
|
// is marked replied so boot replay won't redispatch. Same
|
|
@@ -2590,6 +2622,12 @@ async function handleMessage(sessionKey, chatId, msg, bot) {
|
|
|
2590
2622
|
logEvent('telegram-empty-response-fallback', {
|
|
2591
2623
|
chat_id: chatId, msg_id: msg.message_id, bot: BOT_NAME,
|
|
2592
2624
|
});
|
|
2625
|
+
// 0.8.0-rc.7: clear the THINKING/QUEUED emoji on the user's
|
|
2626
|
+
// message so 👀 doesn't stay stuck after the apology lands.
|
|
2627
|
+
// reactor.stop() (in the finally block) only kills timers; it
|
|
2628
|
+
// does NOT remove the visible emoji. Without this clear, the
|
|
2629
|
+
// user sees 👀 next to their message indefinitely.
|
|
2630
|
+
await reactor.clear().catch(() => {});
|
|
2593
2631
|
markReplied();
|
|
2594
2632
|
return;
|
|
2595
2633
|
}
|