32 Commits

Author SHA1 Message Date
wassname a2cfde2b25 Translate dashes to underscores for Telegram bot commands
Pi command names like compact-now were silently dropped from
setMyCommands because Telegram requires names to match
^[a-z0-9_]{1,32}$ - no dashes. They showed up in /help (which
iterates pi.getCommands() directly) but were untypable via
autocomplete and, even when typed manually, fell through to the
agent as plain text rather than dispatching.

Now: register the underscore-translated form with Telegram, show the
underscore form in /help so it matches what the user sees in the
menu, and on dispatch rewrite the message text from /foo_bar back to
/foo-bar before enqueueing so the harness's slash-command parser
finds the registered command.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 18:58:32 +08:00
wassname b2444fd3cd Stop fetch retry from killing the polling loop
Two real bugs surfaced after the original retry helper:

1. Healthy long-polls were tripping our 15s per-attempt timeout. The
   getUpdates request asks Telegram for a 30s server-side long-poll, so
   our internal timeout aborted every healthy connection and turned it
   into ABORT_ERR -> retry -> exhausted -> "disconnected", with no
   auto-reconnect. The fix: long-poll bypasses the retry helper and uses
   a 60s per-attempt timeout, since the poll loop already retries by
   re-entering after sleep().

2. Our own internal AbortController timeout produced a DOMException
   AbortError indistinguishable from a caller-abort. The poll loop's
   shouldStopTelegramPolling treated that as "user wants to stop" and
   exited. Now fetchWithRetry normalizes its own timeout into a tagged
   Error with code ATTEMPT_TIMEOUT, so only real caller-aborts surface
   as AbortError upstream.

Also: per-attempt timeout default dropped 15s -> 5s, retry budget
dropped from [500, 2000] to [500] (so 2 attempts, not 3) for outbound
sends, since they serialize and a long retry tail makes the bridge feel
hung.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 18:51:35 +08:00
wassname 66898e02b4 Make /help dynamic and re-sync the bot menu
/help now re-runs setMyCommands so late-registered pi commands (skills,
prompts, extensions that registered after /start) become discoverable
without restarting the bridge, and prints the live pi.getCommands() list
so the user can see what's actually wired up right now.

Addresses the symptom where the Telegram command menu sometimes shows
only the bridge-local commands - a registration-order race against the
initial setMyCommands at /start.
2026-04-29 22:48:08 +08:00
wassname b866807112 Survive transient Telegram fetch failures
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.
2026-04-29 22:34:59 +08:00
wassname 4677aea56b Show tool results in Telegram trace 2026-04-24 22:24:12 +08:00
wassname 3ce880d5bc Register pi commands in Telegram menu 2026-04-24 21:56:07 +08:00
wassname 14798607c6 Fix Telegram stop recovery and trace output 2026-04-24 21:23:50 +08:00
wassname 39da73ce3c feat: improve model selection UX and fix queue/status behaviors
- 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
2026-04-23 12:42:35 +08:00
wassname 64f9d0242f fix: defer non-text block emission until message completes
Tool call blocks (and thinking) arrive in streaming updates before their
args are populated — emitting immediately gave `Tool call bash {}`.

Buffer non-text blocks during onMessageUpdate; flush them in
onMessageStart (previous message now complete, args fully populated)
and onAgentEnd (single-message turns that never trigger onMessageStart).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 16:15:31 +08:00
wassname 29ac175e79 refactor: per-block Telegram emission, displayMode replaces traceVisible
- 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>
2026-04-22 15:29:05 +08:00
wassname 15fa661b7a security: require pre-configured allowedUserId, remove auto-pair
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>
2026-04-22 06:04:56 +08:00
wassname c28436503f slash 2026-04-21 18:09:09 +08:00
wassname 079a7a8dbb misc 2026-04-19 18:21:44 +08:00
wassname c2d25b31fe wip 2026-04-19 16:08:23 +08:00
wassname 5aa37b7a99 tool verbose 2026-04-19 15:41:15 +08:00
LLB f6194693e5 0.2.5: model menu updates fix 2026-04-11 12:45:49 +04:00
LLB 8dcf761937 0.2.0: refactor into domain modules 2026-04-11 11:32:44 +04:00
LLB 233b20b089 bot token from env 2026-04-11 02:21:28 +04:00
LLB a2bb588c3b agents & in-flight model switching 2026-04-11 01:56:28 +04:00
LLB e2bb0799c7 markdown & queue hardening 2026-04-10 14:49:20 +04:00
LLB 1e35559e22 "message is not modified" fix 2026-04-10 10:47:08 +04:00
LLB b0c630a973 markdown fix 2026-04-10 02:01:20 +04:00
LLB 105cbb49f1 queue upgrade 2026-04-10 01:49:22 +04:00
LLB 506ef83a69 refactoring 2026-04-09 11:44:02 +04:00
LLB 8d227c53e9 simplify model select 2026-04-09 10:39:02 +04:00
LLB 038000605a markdown support 2026-04-09 09:52:36 +04:00
LLB ce6ef93220 register commands 2026-04-09 09:07:03 +04:00
LLB da81d26c60 model switch 2026-04-09 08:50:21 +04:00
LLB 2360037777 preparation 2026-04-09 08:49:45 +04:00
Mario Zechner aed722c52b fix: handle telegram compact command 2026-04-04 03:50:13 +02:00
Mario Zechner caf3617747 feat: add telegram compact and status commands 2026-04-04 03:48:07 +02:00
Mario Zechner 27bd379159 feat: add pi telegram extension package 2026-04-04 03:42:02 +02:00