polygram 0.11.0-rc.5 → 0.11.0-rc.6

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.11.0-rc.5",
4
+ "version": "0.11.0-rc.6",
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",
@@ -186,6 +186,34 @@ function resolveSessionForSpawn(db, sessionKey, resolved = {}) {
186
186
  return before[f] !== after[f];
187
187
  });
188
188
 
189
+ // 0.11.0 channels boundary (2026-05-25): SDK ↔ tmux flips remain
190
+ // non-invalidating per rc.32's reasoning. But channels mode is
191
+ // genuinely different on the claude side:
192
+ //
193
+ // - bridge MCP server is mounted (--mcp-config + --strict-mcp-config)
194
+ // - additional --append-system-prompt directive enforces the
195
+ // reply-tool contract (claude must call mcp__polygram-bridge__reply
196
+ // instead of writing inline). Sessions created without this
197
+ // directive don't have the contract baked into their context.
198
+ // - agent behavior expectations differ — same agent file, but
199
+ // conversation flow is "user msg arrives via channel, reply via
200
+ // tool" rather than "user msg arrives inline, reply inline."
201
+ //
202
+ // Live shumorobot 2026-05-25 incident: Music topic resumed a
203
+ // pre-channels session id (4837f61a-…) that had been mid-soulseek-
204
+ // curation. The user's "how are you" got interpreted as a continuation
205
+ // of THAT task; claude responded with music release info, inline,
206
+ // never calling the reply tool. Every turn timed out at 3min.
207
+ //
208
+ // Rule: any transition TO or FROM channels drops the prior session.
209
+ // XOR — flips between channels and {sdk,tmux} invalidate; sdk↔tmux
210
+ // flips remain free.
211
+ const wasChannels = before.pm_backend === 'channels';
212
+ const willBeChannels = after.pm_backend === 'channels';
213
+ if (after.pm_backend != null && wasChannels !== willBeChannels) {
214
+ drifted.push('pm_backend');
215
+ }
216
+
189
217
  if (drifted.length === 0) {
190
218
  return { existingSessionId: row.claude_session_id, drift: null };
191
219
  }
@@ -370,6 +370,14 @@ class ChannelsProcess extends Process {
370
370
  // EXISTING conversation. Lazy-respawn after bridge-disconnect must use
371
371
  // --resume so conversation history is preserved. Mirrors
372
372
  // tmux-process.js:514-518.
373
+ //
374
+ // rc.6 note: pre-channels session ids (created by sdk/tmux backends and
375
+ // persisted in polygram's DB) are NOT cross-backend compatible — claude
376
+ // indexes session files under a cwd-derived projects dir that may not
377
+ // match what channels-mode passes, leading to silent "No conversation
378
+ // found" exit. The fix lives at the data layer, not here: operator
379
+ // clears pre-channels session ids from polygram's sessions table before
380
+ // flipping a chat to channels (see ops/migrate-to-channels.sql).
373
381
  if (opts.existingSessionId) {
374
382
  claudeArgs.push('--resume', opts.existingSessionId);
375
383
  } else {
@@ -413,6 +421,42 @@ class ChannelsProcess extends Process {
413
421
  // from leaking as user-visible messages (shumorobot 2026-05-15 incident).
414
422
  claudeArgs.push('--append-system-prompt', POLYGRAM_DISPLAY_HINT);
415
423
 
424
+ // rc.6 (2026-05-25 shumorobot diagnosis): channels-mode contract is
425
+ // NOT obvious to the agent. The bridge's MCP `instructions:` field tells
426
+ // claude to use the reply tool, but that's seen only during MCP init and
427
+ // gets buried under the agent's own system prompt. Observed live: the
428
+ // music-curator agent received user messages via the channel and wrote
429
+ // replies INLINE to its TUI instead of calling `mcp__polygram-bridge__reply`,
430
+ // so polygram never saw them and every turn timed out after 3 minutes.
431
+ //
432
+ // Append a SECOND, explicit directive that overrides the agent's default
433
+ // conversational behavior. Phrased as a hard rule so it survives whatever
434
+ // the agent's system prompt says. Future tuning may move this to a
435
+ // shared lib/telegram/channels-protocol-hint.js if it grows; for now
436
+ // inline is fine since it's tied to the channels backend implementation.
437
+ claudeArgs.push('--append-system-prompt', [
438
+ '## polygram channels mode — HARD CONTRACT',
439
+ '',
440
+ 'You are running inside polygram with the channels backend. Your stdout/TUI',
441
+ 'output is NOT seen by the user. The user is on Telegram.',
442
+ '',
443
+ 'To deliver ANY message to the user, you MUST call the MCP tool',
444
+ '`mcp__polygram-bridge__reply` with `chat_id` and `text` arguments.',
445
+ 'Pass the chat_id verbatim from the channel message you received.',
446
+ '',
447
+ 'Do NOT respond conversationally in-line. Do NOT assume any inline',
448
+ 'text will reach the user. If you have ANYTHING to say to the user —',
449
+ 'an answer, a question, a status update, an acknowledgement — it goes',
450
+ 'through `mcp__polygram-bridge__reply`. Period.',
451
+ '',
452
+ 'This applies to every turn, including the first message after',
453
+ '`/new` or `/reset`. Even a single-line "Hi" must be sent via the tool.',
454
+ '',
455
+ 'Internal tool calls (Bash, Edit, Write, Read, etc.) are fine to use',
456
+ 'as normal — only the FINAL user-visible message needs to go through',
457
+ 'the reply tool.',
458
+ ].join('\n'));
459
+
416
460
  // Parity audit P6: honor isolateUserConfig — mirrors tmux pattern at
417
461
  // lib/process/tmux-process.js:502-505,543-546. Channels ALWAYS uses
418
462
  // --strict-mcp-config (the bridge requires it), so the MCP half is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.11.0-rc.5",
3
+ "version": "0.11.0-rc.6",
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": {