polygram 0.6.7 → 0.6.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/polygram.js +33 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.6.7",
3
+ "version": "0.6.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
@@ -11,7 +11,7 @@
11
11
  * → sends to persistent claude process via stdin (stream-json)
12
12
  * → reads response from stdout (stream-json)
13
13
  * → sends reply to Telegram
14
- * → writes every in/out message to bridge.db (Phase 1: parallel write)
14
+ * → writes every in/out message to per-bot SQLite (source of truth)
15
15
  *
16
16
  * Chat commands: /model <model>, /effort <level>, /config
17
17
  */
@@ -191,7 +191,7 @@ async function readSessionContext(sessionKey, cwd) {
191
191
  } catch { return ''; }
192
192
  }
193
193
 
194
- // ─── DB writes (Phase 1 — best-effort, never throws) ────────────────
194
+ // ─── DB writes (best-effort wrapper, never throws) ──────────────────
195
195
 
196
196
  function dbWrite(fn, context) {
197
197
  if (!db) return;
@@ -238,11 +238,15 @@ function recordInbound(msg) {
238
238
  const messageId = db.getInboundMessageId({ chat_id: chatId, msg_id: msg.message_id });
239
239
  if (!messageId) return;
240
240
  // Edit-safe insert: Telegram edited_message events re-fire
241
- // recordInbound with the same (chat_id, msg_id). Telegram doesn't
242
- // permit replacing media in an edit (only text/caption), so if rows
243
- // already exist for this message_id they're correct as-is
244
- // re-inserting would (a) duplicate them, (b) reset download_status
245
- // back to 'pending' and lose the local_path we already fetched.
241
+ // recordInbound with the same (chat_id, msg_id). polygram doesn't
242
+ // currently handle media-edit cases (Bot API does support
243
+ // editMessageMedia, but we don't process it specially the typical
244
+ // edit is text/caption). If rows already exist for this message_id
245
+ // they're correct as-is re-inserting would (a) duplicate them,
246
+ // (b) reset download_status back to 'pending' and lose the
247
+ // local_path we already fetched. If we add media-edit support
248
+ // later, this guard needs to compare file_unique_id and replace
249
+ // selectively rather than skipping wholesale.
246
250
  if (db.getAttachmentsByMessage(messageId).length > 0) return;
247
251
  for (const att of attachments) {
248
252
  db.insertAttachment({
@@ -1250,13 +1254,18 @@ function startApprovalSweeper(intervalMs = 30_000) {
1250
1254
  id: row.id, bot: BOT_NAME, tool: row.tool_name,
1251
1255
  }), 'log approval-timeout');
1252
1256
  resolveApprovalWaiter(row.id, 'timeout', 'swept');
1253
- // Best-effort: edit the card to show the timeout.
1257
+ // Best-effort: edit the card to show the timeout. Routed through
1258
+ // tg() so the edit gets the same plain-text formatting policy as
1259
+ // the original card post (no parse_mode injection from tool input)
1260
+ // AND lands in the transcript like every other outbound. Pre-0.6.8
1261
+ // this called bot.api.editMessageText directly and bypassed both.
1254
1262
  if (bot && row.approver_msg_id) {
1255
- bot.api.editMessageText(
1256
- row.approver_chat_id,
1257
- row.approver_msg_id,
1258
- approvalCardText(approvals.getById(row.id), { resolvedBy: '⏰ Timed out' }),
1259
- ).catch(() => {});
1263
+ tg(bot, 'editMessageText', {
1264
+ chat_id: row.approver_chat_id,
1265
+ message_id: row.approver_msg_id,
1266
+ text: approvalCardText(approvals.getById(row.id), { resolvedBy: '⏰ Timed out' }),
1267
+ }, { source: 'approval-card-timeout', botName: BOT_NAME, plainText: true })
1268
+ .catch((err) => console.error(`[${BOT_NAME}] approval-card-timeout edit: ${err.message}`));
1260
1269
  }
1261
1270
  }
1262
1271
  }, intervalMs);
@@ -1812,7 +1821,17 @@ function createBot(token) {
1812
1821
  async function onboardPairedChat(ctx, code) {
1813
1822
  const chatId = ctx.chat.id.toString();
1814
1823
  const userId = ctx.message.from?.id;
1815
- const send = (text) => bot.api.sendMessage(chatId, text).catch(() => {});
1824
+ // Route through tg() so onboarding replies (success notice + error
1825
+ // messages) get the standard write-before-send DB row, log on
1826
+ // failure, and the same formatting policy as every other outbound.
1827
+ // Pre-0.6.8 this was bot.api.sendMessage(...).catch(() => {}) which
1828
+ // silently dropped failures: the user typed /pair, the code was
1829
+ // claimed (DB mutated), but if the "Paired" reply failed to send
1830
+ // they'd assume it didn't work and try the now-invalid code again.
1831
+ const send = (text) => tg(bot, 'sendMessage', {
1832
+ chat_id: chatId, text,
1833
+ }, { source: 'pair-onboarding', botName: BOT_NAME }).catch((err) =>
1834
+ console.error(`[${BOT_NAME}] pair-onboarding reply: ${err.message}`));
1816
1835
 
1817
1836
  if (!userId) {
1818
1837
  await send('No user id on request.');