Wrap fetch calls in callTelegram, callTelegramMultipart, and
downloadTelegramFile with bounded retry (max 3 attempts, 500ms/2000ms
backoff with jitter) on transient network codes and a per-attempt 15s
AbortController timeout so a stuck connection can't wedge the bridge
indefinitely. HTTP 4xx/5xx still surface as before.
Add a process-level unhandledRejection handler so a stray rejection from
a fire-and-forget void f() (e.g. timer-driven preview flush) can't crash
the host under Node 22's default unhandledRejection=throw - which is how
a remote session got dropped. Also tighten the two timer-callback voids
that can fire after their turn's try/catch is gone.
- Add search/filtering to the `/model` command with multi-word matching
- Finalize partial stream previews (e.g. thinking blocks) on turn abort instead of clearing them
- Dynamically format low-cost `$ value` metrics up to 5 decimal places in status outputs
- Update queue tests to expect text-turn plans for aborted turns with partial text
- Each pi output block (thinking, tool_call, tool_result) emits a
separate Telegram message as it arrives; text content still streams
in a single edit-in-place preview message
- Replace traceVisible bool with displayMode: "full" | "compact" | "text"
(/trace cycles through all three; compact is default)
- Add renderBlockMessage() to lib/rendering.ts; delete old transcript
aggregation helpers (buildTelegramAssistantTranscriptMarkdown, etc.)
- registerTelegramBotCommands now pulls extension commands from
pi.getCommands() in addition to local bot commands
- Update rendering tests for new block rendering API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The first-DM auto-pair behavior combined with ! shell passthrough meant
the first account to DM the bot gained arbitrary shell access. This
removes that footgun entirely.
- allowedUserId must be set before polling starts; missing config blocks
polling with a TUI warning rather than silently accepting any sender
- TELEGRAM_ALLOWED_USER_ID env var is read on session start and
overwrites the saved config value, so rotating the allowed user is a
restart away
- /telegram-setup now prompts for a numeric user ID after the bot token
if one is not already configured
- Denied senders receive an auth error reply; their numeric ID is also
logged to the pi TUI as a warning so operators can identify themselves
on a fresh install without needing @userinfobot
- Dropped the {kind: "pair"} authorization state entirely; undefined
allowedUserId now produces deny, not pair
- Removed pairTelegramUserIfNeeded, shouldPair, shouldNotifyPaired
Existing installs with allowedUserId already in telegram.json are
unaffected. Fresh installs require explicit configuration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>