polygram 0.17.5 → 0.17.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.
|
@@ -69,6 +69,22 @@ function createSessionFeedback({
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
// Stop the cycle's TYPING the moment it delivers its answer, decoupled from
|
|
73
|
+
// endCycle. endCycle fires on the Process 'idle' edge (= session idle), which a
|
|
74
|
+
// later turn delays — so without this the "typing…" indicator spun minutes past the
|
|
75
|
+
// delivered answer (field: Ivan DM 2026-06-26). The entry is LEFT in place so
|
|
76
|
+
// endCycle still clears the anchor 🤔 and tears the entry down. docs/typing-tracks-activity-spec.md
|
|
77
|
+
function stopCycleTyping(sessionKey) {
|
|
78
|
+
const entry = active.get(sessionKey);
|
|
79
|
+
if (!entry || entry.typingStopped) return;
|
|
80
|
+
entry.typingStopped = true;
|
|
81
|
+
try { entry.stop(); } catch { /* best-effort */ }
|
|
82
|
+
logEvent('autonomous-cycle-visuals', {
|
|
83
|
+
chat_id: entry.anchor?.chatId ?? getChatIdFromKey(sessionKey),
|
|
84
|
+
session_key: sessionKey, state: 'typing-stopped',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
72
88
|
function endCycle(sessionKey) {
|
|
73
89
|
const entry = active.get(sessionKey);
|
|
74
90
|
if (!entry) return;
|
|
@@ -85,7 +101,7 @@ function createSessionFeedback({
|
|
|
85
101
|
});
|
|
86
102
|
}
|
|
87
103
|
|
|
88
|
-
return { startAutonomousCycle, endCycle };
|
|
104
|
+
return { startAutonomousCycle, stopCycleTyping, endCycle };
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
module.exports = { createSessionFeedback };
|
|
@@ -133,8 +133,24 @@ function createDispatcher({
|
|
|
133
133
|
throw new Error('auto-resume turn produced no text');
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
// 4.
|
|
137
|
-
//
|
|
136
|
+
// 4. Deliver the continuation reply — UNLESS the resumed turn already
|
|
137
|
+
// delivered it itself. On the channels/cli backend Claude responds via the
|
|
138
|
+
// reply tool DURING the turn, so result.alreadyDelivered is set and the main
|
|
139
|
+
// dispatch path short-circuits its own deliver (cli-process.js ~2116). The
|
|
140
|
+
// resume path must honor it too, or the reply-tool send + this re-send
|
|
141
|
+
// double-post the SAME answer (field: shumabit@umi WhatsApp topic 2026-06-27,
|
|
142
|
+
// a bridge-disconnect resume sent "Fixed. ✅…" twice). SDK / genuine no-reply
|
|
143
|
+
// turns leave it falsy → deliver as before.
|
|
144
|
+
if (result.alreadyDelivered) {
|
|
145
|
+
logEvent('auto-resume-already-delivered', {
|
|
146
|
+
chat_id: chatId, session_key: sessionKey, msg_id: originalMsg.message_id,
|
|
147
|
+
text_len: result.text.length,
|
|
148
|
+
});
|
|
149
|
+
return result.text;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Send the continuation reply as regular Telegram messages, threaded under
|
|
153
|
+
// the original user message.
|
|
138
154
|
const chunks = chunkMarkdownText(result.text, chunkBudget);
|
|
139
155
|
await deliverReplies({
|
|
140
156
|
bot,
|
|
@@ -2040,12 +2040,32 @@ class CliProcess extends Process {
|
|
|
2040
2040
|
* sibling's text; otherwise leave the status (nothing more to send).
|
|
2041
2041
|
*/
|
|
2042
2042
|
_resolveTurnDelivery(pending, turnId) {
|
|
2043
|
+
const out = this._computeTurnDelivery(pending, turnId);
|
|
2044
|
+
// 0.17.8 characterize-first: the channels double-delivery (shumorobot Music,
|
|
2045
|
+
// 2026-06-28 — reply tool sent #2147, then the daemon re-sent result.text as
|
|
2046
|
+
// #2149) is a turn that resolved with alreadyDelivered=false despite a reply tool
|
|
2047
|
+
// delivery. Log the chosen branch + counts so the next occurrence pins WHY (the
|
|
2048
|
+
// leading hypothesis: an interrupted turn loses its recorded reply → the
|
|
2049
|
+
// zero-reply Stop-fallback re-delivers last_assistant_message).
|
|
2050
|
+
this._logEvent('cli-resolve-delivery', {
|
|
2051
|
+
turn_id: turnId, session_key: this.sessionKey, backend: this.backend,
|
|
2052
|
+
branch: out.branch,
|
|
2053
|
+
already_delivered: out.alreadyDelivered,
|
|
2054
|
+
reply_count: pending.replies.length,
|
|
2055
|
+
interim_count: pending._interimReplyCount || 0,
|
|
2056
|
+
has_stop_data: !!pending._stopHookData,
|
|
2057
|
+
text_len: (out.text || '').length,
|
|
2058
|
+
});
|
|
2059
|
+
return { text: out.text, alreadyDelivered: out.alreadyDelivered };
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
_computeTurnDelivery(pending, turnId) {
|
|
2043
2063
|
const norm = (s) => (s || '').trim();
|
|
2044
2064
|
const interimText = pending.replies.join('\n\n');
|
|
2045
2065
|
const fallbackText = pending._stopHookData?.lastAssistantMessage || '';
|
|
2046
2066
|
|
|
2047
2067
|
if (this._turnHasFinalReply(pending)) {
|
|
2048
|
-
return { text: interimText, alreadyDelivered: true };
|
|
2068
|
+
return { text: interimText, alreadyDelivered: true, branch: 'final-reply' };
|
|
2049
2069
|
}
|
|
2050
2070
|
if (pending.replies.length === 0) {
|
|
2051
2071
|
// 0.12 Phase 1.7 fallback: no reply tool call landed — use the Stop hook's
|
|
@@ -2068,7 +2088,7 @@ class CliProcess extends Process {
|
|
|
2068
2088
|
rescued_len: text.length, ack_len: norm(pending._consumedByText).length,
|
|
2069
2089
|
});
|
|
2070
2090
|
}
|
|
2071
|
-
return { text, alreadyDelivered };
|
|
2091
|
+
return { text, alreadyDelivered, branch: 'zero-reply' };
|
|
2072
2092
|
}
|
|
2073
2093
|
// Interim-only: the turn delivered ONLY status/progress promises ("give me a
|
|
2074
2094
|
// couple min") and never a final reply. If claude produced a substantive final
|
|
@@ -2084,9 +2104,9 @@ class CliProcess extends Process {
|
|
|
2084
2104
|
turn_id: turnId, session_key: this.sessionKey, backend: this.backend,
|
|
2085
2105
|
rescued_len: fallbackText.length, interim_count: pending.replies.length,
|
|
2086
2106
|
});
|
|
2087
|
-
return { text: fallbackText, alreadyDelivered: false };
|
|
2107
|
+
return { text: fallbackText, alreadyDelivered: false, branch: 'interim-rescue' };
|
|
2088
2108
|
}
|
|
2089
|
-
return { text: interimText, alreadyDelivered: true };
|
|
2109
|
+
return { text: interimText, alreadyDelivered: true, branch: 'interim-noop' };
|
|
2090
2110
|
}
|
|
2091
2111
|
|
|
2092
2112
|
_finalizeTurn(turnId) {
|
package/lib/sdk/callbacks.js
CHANGED
|
@@ -216,6 +216,10 @@ function createSdkCallbacks({
|
|
|
216
216
|
const text = (msg && typeof msg.text === 'string' && msg.text)
|
|
217
217
|
|| extractAssistantText(msg);
|
|
218
218
|
if (!text) return;
|
|
219
|
+
// 0.13 D3 fix: the cycle delivered its answer → stop its "typing…" NOW. endCycle
|
|
220
|
+
// (Process idle = SESSION idle) is delayed by any later turn, so typing otherwise
|
|
221
|
+
// spins minutes past the delivered answer. docs/typing-tracks-activity-spec.md
|
|
222
|
+
sessionFeedback?.stopCycleTyping?.(sessionKey);
|
|
219
223
|
const chatId = getChatIdFromKey(sessionKey);
|
|
220
224
|
const threadIdRaw = getThreadIdFromKey(sessionKey);
|
|
221
225
|
const threadId = threadIdRaw ? parseInt(threadIdRaw, 10) : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.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": {
|