polygram 0.12.0-rc.27 → 0.12.0-rc.29

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.
@@ -690,6 +690,22 @@ class CliProcess extends Process {
690
690
  'as normal — only the FINAL user-visible message needs to go through',
691
691
  'the reply tool.',
692
692
  '',
693
+ // TEMPORARY mitigation (2026-06-08 Shumabit@UMI wedge): AskUserQuestion opens
694
+ // a blocking TUI selection widget the channel can't answer → the session
695
+ // parks until manually Esc'd. REMOVE this whole rule when the rich
696
+ // question→Telegram-keyboard feature ships (see docs design); claude should
697
+ // then use the native question tool again. Tracked so it isn't forgotten.
698
+ '### Asking the user a question / offering choices — HARD RULE',
699
+ '',
700
+ 'NEVER use the AskUserQuestion tool or any interactive menu / selection',
701
+ 'widget. They open a blocking terminal prompt the user on Telegram CANNOT',
702
+ 'see or navigate — it silently wedges the entire session until it is manually',
703
+ 'cleared. (Rich tap-to-answer choices are coming; until then this is a hard rule.)',
704
+ '',
705
+ 'To ask a multiple-choice question, write the options as a numbered list',
706
+ 'inside your `mcp__polygram-bridge__reply` text and ask the user to reply with',
707
+ 'the number (or free text). Do the same for yes/no and any selection.',
708
+ '',
693
709
  '### Sending FILES (tracks, images, docs) to the user',
694
710
  '',
695
711
  'The `mcp__polygram-bridge__reply` tool takes an optional `files` array of',
@@ -1207,6 +1223,42 @@ class CliProcess extends Process {
1207
1223
  }
1208
1224
  }
1209
1225
 
1226
+ /**
1227
+ * Keep a turn alive while claude is still TOOL-working (2026-06-08 Shumabit@UMI
1228
+ * WA-topic incident). The per-turn reply-quiet window (`quietTimer`, armed in
1229
+ * _recordReplyForPendingTurn) is reset ONLY by reply tool calls. So a turn that
1230
+ * replied and then kept working with NON-reply tools — claude read a follow-up
1231
+ * screenshot, ran a Bash API check, then replied again 90s later, all inside ONE
1232
+ * claude turn (no Stop hook until the very end) — resolved on the reply-quiet
1233
+ * window WHILE claude was still active. Its reactor + typing tore down at the
1234
+ * premature resolve, and the late reply fell through to the orphan
1235
+ * autonomous-assistant-message path (no pending turn).
1236
+ *
1237
+ * claude's Stop hook is the AUTHORITATIVE turn-end; until it lands, a PreToolUse/
1238
+ * PostToolUse hook means claude is still working — so push the reply-quiet window
1239
+ * (and the idle ceiling) out. The absoluteTimer (30-min runaway cap) is left
1240
+ * untouched as the hard backstop, and Stop still resolves normally. Turns that
1241
+ * reply then truly stop (no intervening tool hooks) are unaffected — there's no
1242
+ * tool activity to extend on.
1243
+ */
1244
+ _extendQuietOnToolActivity() {
1245
+ for (const [turnId, pending] of this.pendingTurns) {
1246
+ if (pending._stopGracePending) continue; // already finalizing — don't revive
1247
+ // Idle ceiling: tool activity IS activity (mirrors the per-reply reset at
1248
+ // _recordReplyForPendingTurn so a long tool-heavy turn isn't idle-killed).
1249
+ if (pending.hardTimer) {
1250
+ clearTimeout(pending.hardTimer);
1251
+ pending.hardTimer = setTimeout(() => pending._fireTimeout?.('idle'), this.turnTimeoutMs);
1252
+ }
1253
+ // Reply-quiet window: only armed AFTER the first reply — that's exactly the
1254
+ // post-reply-still-tooling case this fixes.
1255
+ if (pending.quietTimer) {
1256
+ clearTimeout(pending.quietTimer);
1257
+ pending.quietTimer = setTimeout(() => this._resolveTurn(turnId), this.turnQuietMs);
1258
+ }
1259
+ }
1260
+ }
1261
+
1210
1262
  // 0.12 Phase 1.7 (Finding 0.1.A): two-step turn resolution.
1211
1263
  // _resolveTurn — entry point called by channel-result OR quiet-window
1212
1264
  // expiry. Schedules a stopGraceMs window during which
@@ -1799,6 +1851,15 @@ class CliProcess extends Process {
1799
1851
  });
1800
1852
  }
1801
1853
 
1854
+ // 2026-06-08 (WA-topic incident): a tool hook means claude is STILL working,
1855
+ // so keep its turn(s) alive — the reply-quiet window must not resolve a turn
1856
+ // mid-work just because the last *reply* was a few seconds ago. See
1857
+ // _extendQuietOnToolActivity. Reply tool calls are handled separately (their
1858
+ // own reset in _recordReplyForPendingTurn); covering them here too is a no-op.
1859
+ if (ev.type === 'PreToolUse' || ev.type === 'PostToolUse') {
1860
+ this._extendQuietOnToolActivity();
1861
+ }
1862
+
1802
1863
  switch (ev.type) {
1803
1864
  case 'UserPromptSubmit':
1804
1865
  this.emit('turn-start', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.12.0-rc.27",
3
+ "version": "0.12.0-rc.29",
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": {