polygram 0.9.0-rc.4 → 0.9.0-rc.5

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "polygram",
4
- "version": "0.9.0-rc.4",
4
+ "version": "0.9.0-rc.5",
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 plus history (transcript queries) and polygram-send (out-of-turn IPC sends with file-upload validation) skills.",
6
6
  "keywords": [
7
7
  "telegram",
package/lib/db.js CHANGED
@@ -521,6 +521,20 @@ function wrap(db) {
521
521
  `).run(status, chat_id, msg_id);
522
522
  },
523
523
 
524
+ // 0.9.0: True when a specific inbound msg is still being processed
525
+ // by the SDK turn loop (handler_status in dispatched/processing).
526
+ // Used by the edit-correction injector — only inject a typo-fix
527
+ // note when the SDK actually still has the turn in flight.
528
+ isInboundLive({ chat_id, msg_id }) {
529
+ const row = db.prepare(`
530
+ SELECT 1 FROM messages
531
+ WHERE chat_id = ? AND msg_id = ? AND direction = 'in'
532
+ AND handler_status IN ('dispatched', 'processing')
533
+ LIMIT 1
534
+ `).get(chat_id, msg_id);
535
+ return !!row;
536
+ },
537
+
524
538
  // Find inbound messages that were being processed when polygram stopped.
525
539
  // Scoped by bot_name via the chat_id → config mapping, so each bot only
526
540
  // replays its own turns on boot. Scoped by olderThanMs (default 3 min)
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Edit-correction injector.
5
+ *
6
+ * When the user edits a Telegram message that's still being processed
7
+ * by the SDK turn loop, inject a correction note into the active turn
8
+ * via the same hook channel autosteer uses (pm.injectUserMessage,
9
+ * priority: 'next'). Lets users fix typos mid-turn without /stop +
10
+ * resend.
11
+ *
12
+ * Skipped when the turn already finished — at that point the
13
+ * conversation has moved on and re-opening it would confuse Claude
14
+ * more than help. Also skipped when the SDK session has been
15
+ * LRU-evicted (no live target to inject into).
16
+ *
17
+ * Architectural note: there's no polygram-side "buffer" to replace.
18
+ * pm.send and pm.injectUserMessage push the message text directly into
19
+ * the SDK's inputController AsyncIterable; once pushed, polygram has
20
+ * no way to retract it. So edit handling is always "inject correction
21
+ * note as additional context" — Claude reconciles.
22
+ */
23
+
24
+ function createEditCorrectionInjector({
25
+ pm,
26
+ db,
27
+ getSessionKey,
28
+ config,
29
+ logEvent,
30
+ logger = console,
31
+ } = {}) {
32
+
33
+ return function maybeInjectEditCorrection(editedMsg) {
34
+ if (!editedMsg?.chat) return false;
35
+ const chatId = editedMsg.chat.id.toString();
36
+ const chatConfig = config.chats[chatId];
37
+ if (!chatConfig) return false;
38
+
39
+ // Per-chat / bot-level opt-out. Default on.
40
+ const optOut = chatConfig.editCorrection != null
41
+ ? chatConfig.editCorrection === false
42
+ : config.bot?.editCorrection === false;
43
+ if (optOut) return false;
44
+
45
+ const threadIdStr = editedMsg.message_thread_id?.toString() || null;
46
+ const sessionKey = getSessionKey(chatId, threadIdStr, chatConfig);
47
+
48
+ // Three skip gates — all must be true for an injection:
49
+ // 1. SDK session exists (not LRU-evicted)
50
+ // 2. session has a turn in flight
51
+ // 3. the edited msg is still in the dispatched/processing pipeline
52
+ if (!pm.has(sessionKey)) return false;
53
+ if (!pm.get(sessionKey)?.inFlight) return false;
54
+ if (!db.isInboundLive({ chat_id: chatId, msg_id: editedMsg.message_id })) return false;
55
+
56
+ const newText = editedMsg.text || editedMsg.caption || '';
57
+ if (!newText) return false;
58
+
59
+ const ok = pm.injectUserMessage(sessionKey, {
60
+ content: `[edit] I corrected my previous message — it now reads: ${newText}`,
61
+ priority: 'next',
62
+ });
63
+ if (!ok) {
64
+ logger.error?.(`[${chatConfig.name || chatId}] edit-correction inject failed`);
65
+ return false;
66
+ }
67
+ logEvent('message-edit-injected', {
68
+ chat_id: chatId,
69
+ msg_id: editedMsg.message_id,
70
+ session_key: sessionKey,
71
+ text_len: newText.length,
72
+ });
73
+ return true;
74
+ };
75
+ }
76
+
77
+ module.exports = { createEditCorrectionInjector };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.9.0-rc.4",
3
+ "version": "0.9.0-rc.5",
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
@@ -44,6 +44,7 @@ const { createDownloadAttachments } = require('./lib/handlers/download');
44
44
  const { createHandleConfigCallback } = require('./lib/handlers/config-callback');
45
45
  const { createHandleAbort } = require('./lib/handlers/abort');
46
46
  const { createAutosteerHandlers } = require('./lib/handlers/autosteer');
47
+ const { createEditCorrectionInjector } = require('./lib/handlers/edit-correction');
47
48
  const { createSlashCommands } = require('./lib/handlers/slash-commands');
48
49
  const { createApprovals } = require('./lib/handlers/approvals');
49
50
  const { canonicalizeToolInput } = require('./lib/canonical-json');
@@ -525,6 +526,7 @@ let handleConfigCallback = null;
525
526
  let handleAbortIfRequested = null;
526
527
  let autosteer = null;
527
528
  let dispatchSlashCommand = null;
529
+ let maybeInjectEditCorrection = null;
528
530
 
529
531
  // rc.20: approvalCardText + safeParse moved to lib/approvals/ui.js.
530
532
  // 0.9.0 commit 29: makeCanUseTool / handleApprovalCallback /
@@ -1702,6 +1704,18 @@ function createBot(token) {
1702
1704
  user_id: ctx.editedMessage.from?.id || null,
1703
1705
  });
1704
1706
  console.log(`[${BOT_NAME}] edited ${chatId}/${ctx.editedMessage.message_id}`);
1707
+
1708
+ // 0.9.0: typo-correction injection. If the SDK still has this turn
1709
+ // in flight (handler_status in dispatched/processing AND
1710
+ // pm.get(sk).inFlight), inject a `[edit] corrected: <NEW>` note
1711
+ // via the same channel autosteer uses. Lets users fix typos
1712
+ // mid-turn without /stop + resend. No-op when the turn already
1713
+ // completed.
1714
+ try {
1715
+ maybeInjectEditCorrection?.(ctx.editedMessage);
1716
+ } catch (err) {
1717
+ console.error(`[${BOT_NAME}] edit-correction injector error: ${err.message}`);
1718
+ }
1705
1719
  });
1706
1720
 
1707
1721
  bot.on('message:migrate_to_chat_id', async (ctx) => {
@@ -1955,6 +1969,9 @@ async function main() {
1955
1969
  autosteer = createAutosteerHandlers({
1956
1970
  config, pm, autosteeredRefs, logEvent,
1957
1971
  });
1972
+ maybeInjectEditCorrection = createEditCorrectionInjector({
1973
+ pm, db, getSessionKey, config, logEvent, logger: console,
1974
+ });
1958
1975
  recordInbound = createRecordInbound({
1959
1976
  db, dbWrite, config, botName: BOT_NAME, extractAttachments,
1960
1977
  });