polygram 0.12.0 → 0.12.1
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/process/cli-process.js +51 -7
- package/package.json +1 -1
|
@@ -1159,7 +1159,10 @@ class CliProcess extends Process {
|
|
|
1159
1159
|
// happens at the chat_id guard below.
|
|
1160
1160
|
const chatIdMatches = this.chatId == null || String(args.chat_id) === String(this.chatId);
|
|
1161
1161
|
if (chatIdMatches && Array.isArray(args.consumed_turn_ids) && args.consumed_turn_ids.length) {
|
|
1162
|
-
this._ledgerAckConsumed(
|
|
1162
|
+
this._ledgerAckConsumed(
|
|
1163
|
+
args.consumed_turn_ids.filter((x) => typeof x === 'string'),
|
|
1164
|
+
typeof args.text === 'string' ? args.text : '',
|
|
1165
|
+
);
|
|
1163
1166
|
} else if (chatIdMatches && msg.name === 'reply' && 'consumed_turn_ids' in args) {
|
|
1164
1167
|
this._lastAckFieldAt = Date.now(); // field present but empty — contract observed
|
|
1165
1168
|
}
|
|
@@ -1534,8 +1537,16 @@ class CliProcess extends Process {
|
|
|
1534
1537
|
if (e._watchdogTimer) { clearTimeout(e._watchdogTimer); e._watchdogTimer = null; }
|
|
1535
1538
|
}
|
|
1536
1539
|
|
|
1537
|
-
/**
|
|
1538
|
-
|
|
1540
|
+
/**
|
|
1541
|
+
* Tier 2C: a reply carried consumed_turn_ids — acknowledge every known id.
|
|
1542
|
+
* `consumingText` is the text the consuming reply actually delivered; it is
|
|
1543
|
+
* recorded on each consumed pending so _finalizeTurn can tell a genuine fold
|
|
1544
|
+
* (the answer rode the sibling reply) from an early ack that DIDN'T carry the
|
|
1545
|
+
* answer (prod 2026-06-13: a 294-char "Researching now…" ack, then the real
|
|
1546
|
+
* answer arrived as Stop-fallback text — which must NOT be suppressed).
|
|
1547
|
+
* See docs/0.13-consumed-ack-stop-fallback-drop-spec.md.
|
|
1548
|
+
*/
|
|
1549
|
+
_ledgerAckConsumed(ids, consumingText = '') {
|
|
1539
1550
|
this._lastAckFieldAt = Date.now();
|
|
1540
1551
|
for (const id of ids) {
|
|
1541
1552
|
const e = this.inputLedger.get(id);
|
|
@@ -1552,6 +1563,14 @@ class CliProcess extends Process {
|
|
|
1552
1563
|
const pending = this.pendingTurns.get(id);
|
|
1553
1564
|
if (pending) {
|
|
1554
1565
|
pending._consumedAcked = true;
|
|
1566
|
+
// Remember WHAT the consuming reply delivered for this turn. The last
|
|
1567
|
+
// ack wins; a longer subsequent ack is the safer record (more likely to
|
|
1568
|
+
// actually contain the answer). _finalizeTurn only suppresses a
|
|
1569
|
+
// Stop-fallback finalize when its text matches this.
|
|
1570
|
+
if (typeof consumingText === 'string'
|
|
1571
|
+
&& consumingText.length > (pending._consumedByText?.length || 0)) {
|
|
1572
|
+
pending._consumedByText = consumingText;
|
|
1573
|
+
}
|
|
1555
1574
|
// The ack itself flips rung-2 eligibility on — arm now. (The turn's
|
|
1556
1575
|
// last _noteActivity ran BEFORE this flag was set, so without this
|
|
1557
1576
|
// a quiet tail would never re-arm and the turn would sit until a
|
|
@@ -1890,10 +1909,33 @@ class CliProcess extends Process {
|
|
|
1890
1909
|
// the turn without calling mcp__polygram-bridge__reply), use the Stop
|
|
1891
1910
|
// hook's last_assistant_message as the text. Same rescue pattern rc.41
|
|
1892
1911
|
// H4 uses on tmux backend when JSONL stream is broken.
|
|
1893
|
-
|
|
1912
|
+
const usedStopFallback = !text && !!pending._stopHookData?.lastAssistantMessage;
|
|
1913
|
+
if (usedStopFallback) {
|
|
1894
1914
|
text = pending._stopHookData.lastAssistantMessage;
|
|
1895
1915
|
this.logger.warn?.(`[${this.label}] cli: turn finalized via stop-hook fallback (no reply tool call); text_len=${text.length}`);
|
|
1896
1916
|
}
|
|
1917
|
+
// A _consumedAcked turn is "already delivered" ONLY when the consuming
|
|
1918
|
+
// sibling reply actually carried THIS text — not merely an ack. Prod
|
|
1919
|
+
// 2026-06-13 (Shumabit@UMI/37): the consuming reply was a 294-char
|
|
1920
|
+
// "Researching now…" ack, then the real answer arrived as Stop-fallback
|
|
1921
|
+
// text. Suppressing it (alreadyDelivered=true → polygram.js short-circuit)
|
|
1922
|
+
// dropped the answer silently for 5h20m. Only suppress when the rescued
|
|
1923
|
+
// text matches what the sibling delivered.
|
|
1924
|
+
// docs/0.13-consumed-ack-stop-fallback-drop-spec.md
|
|
1925
|
+
const norm = (s) => (s || '').trim();
|
|
1926
|
+
const consumedCoversFallback = !usedStopFallback || norm(text) === norm(pending._consumedByText);
|
|
1927
|
+
const alreadyDelivered = hadReplyToolCalls
|
|
1928
|
+
|| (pending._consumedAcked === true && consumedCoversFallback);
|
|
1929
|
+
if (usedStopFallback && pending._consumedAcked === true && !consumedCoversFallback) {
|
|
1930
|
+
this.logger.warn?.(`[${this.label}] cli: consumed-ack did NOT cover the Stop-fallback answer — delivering rescued text (len=${text.length})`);
|
|
1931
|
+
this._logEvent('cli-consumed-ack-fallback-rescued', {
|
|
1932
|
+
turn_id: turnId,
|
|
1933
|
+
session_key: this.sessionKey,
|
|
1934
|
+
backend: this.backend,
|
|
1935
|
+
rescued_len: text.length,
|
|
1936
|
+
ack_len: norm(pending._consumedByText).length,
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1897
1939
|
const duration = Date.now() - pending.startedAt;
|
|
1898
1940
|
// Review AC4: cost=null + metrics-tokens=null signal "unmeasured-subscription"
|
|
1899
1941
|
// (channels protocol doesn't expose per-turn cost or token breakdowns).
|
|
@@ -1910,9 +1952,11 @@ class CliProcess extends Process {
|
|
|
1910
1952
|
// would resolve the turn silently and the user still sees nothing. So
|
|
1911
1953
|
// only claim already-delivered when reply tool calls actually fired —
|
|
1912
1954
|
// or when claude ACKED consuming this turn in a sibling reply
|
|
1913
|
-
// (consumed_turn_ids; the fold-id-echo case)
|
|
1914
|
-
//
|
|
1915
|
-
|
|
1955
|
+
// (consumed_turn_ids; the fold-id-echo case) AND that sibling actually
|
|
1956
|
+
// delivered this text — re-sending the Stop fallback there would
|
|
1957
|
+
// duplicate. A consumed-ack whose ack did NOT carry the Stop-fallback
|
|
1958
|
+
// answer must still deliver (see consumedCoversFallback above).
|
|
1959
|
+
alreadyDelivered,
|
|
1916
1960
|
sessionId: this.claudeSessionId,
|
|
1917
1961
|
cost: null, // Channels protocol doesn't expose per-turn cost
|
|
1918
1962
|
duration,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
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": {
|