@llblab/pi-telegram 0.2.8 β 0.2.10
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.
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +15 -0
- package/README.md +16 -13
- package/docs/architecture.md +26 -16
- package/index.ts +199 -251
- package/lib/api.ts +277 -42
- package/lib/commands.ts +87 -0
- package/lib/media.ts +70 -1
- package/lib/polling.ts +25 -5
- package/lib/preview.ts +239 -0
- package/lib/rendering.ts +686 -49
- package/lib/replies.ts +2 -181
- package/lib/turns.ts +86 -0
- package/lib/types.ts +137 -0
- package/lib/updates.ts +64 -2
- package/package.json +1 -1
- package/tests/api.test.ts +243 -1
- package/tests/commands.test.ts +85 -0
- package/tests/media.test.ts +90 -1
- package/tests/menu.test.ts +46 -15
- package/tests/polling.test.ts +73 -0
- package/tests/preview.test.ts +480 -0
- package/tests/queue.test.ts +3 -0
- package/tests/rendering.test.ts +175 -2
- package/tests/replies.test.ts +2 -222
- package/tests/turns.test.ts +115 -0
- package/tests/updates.test.ts +72 -7
package/AGENTS.md
CHANGED
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
- Keep comments and user-facing docs in English unless the surrounding file already follows another convention
|
|
58
58
|
- Each project `.ts` file should start with a short multi-line responsibility header comment that explains the file boundary to future maintainers
|
|
59
59
|
- Name extracted `/lib` modules and mirrored `/tests` suites by bare domain when the repository already supplies the Telegram scope; prefer `api.ts`, `queue.ts`, `updates.ts`, and `queue.test.ts` over redundant `telegram-*` filename prefixes
|
|
60
|
-
- Prefer targeted edits, keeping `index.ts` as the orchestration layer and moving reusable logic into flat `/lib` domain modules when a subsystem becomes large enough to earn extraction; current extracted domains include queueing/runtime decisions, replies, polling, updates, attachments, registration and lifecycle-hook binding, Telegram API/config support, turn-building, media extraction, setup, rendering, status rendering, menu/model-resolution/UI support, and model-switch support
|
|
60
|
+
- Prefer targeted edits, keeping `index.ts` as the orchestration layer and moving reusable logic into flat `/lib` domain modules when a subsystem becomes large enough to earn extraction; current extracted domains include queueing/runtime decisions, preview streaming, replies, polling, updates, attachments, registration and lifecycle-hook binding, Telegram API/config support, Telegram Bot API transport types, command parsing/action routing, turn-building, media extraction/media-group coalescing, setup, rendering, status rendering, menu/model-resolution/UI support, and model-switch support
|
|
61
|
+
- Keep preview appearance logic in the rendering domain and preview transport/lifecycle logic in the preview domain so richer streaming strategies can evolve without entangling Telegram delivery state with Markdown formatting rules
|
|
61
62
|
|
|
62
63
|
## 7. Operational Conventions
|
|
63
64
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Current
|
|
4
4
|
|
|
5
|
+
- `[Turns]` Preserved existing attachment-path blocks and aborted-turn history context when a still-queued Telegram message is edited. Impact: caption edits no longer make queued prompts lose their downloaded file references or prior-message context.
|
|
6
|
+
- `[Polling]` Persisted Telegram long-poll offsets only after each update is handled successfully. Impact: a handler failure no longer marks an unprocessed Telegram update as consumed, reducing the chance of silently dropping inbound messages.
|
|
7
|
+
- `[Telegram API]` Added HTTP-status-aware Bot API response parsing, malformed-success handling, retry/backoff for 429 and 5xx Bot API responses, streaming Telegram downloads with size-limit checks, file-backed multipart upload blobs, partial-download cleanup on limit failures, startup cleanup for stale Telegram temp files, and UUID-based sanitized temp filenames. Impact: Telegram transport failures now report clearer status/description details, transient Telegram throttling/server failures get retried automatically, oversized inbound files are rejected before or during download, outbound multipart sends avoid preloading files into memory, partial and stale temp files are removed, and downloaded files are less prone to timestamp collisions or unsafe local names.
|
|
8
|
+
- `[Rendering]` Escaped generated HTML attributes separately, sanitized code-fence language classes, and chunked raw HTML-mode output below Telegram length limits with balanced tag reopening across raw HTML chunks. Impact: generated Telegram HTML is harder to malformed through link or fence metadata and long raw-HTML replies no longer exceed Telegram's message size limit or break active tags across chunk boundaries.
|
|
9
|
+
- `[Preview]` Serialized overlapping preview flushes through a single in-flight flush chain. Impact: rapid streaming updates no longer allow concurrent Telegram edit calls to overwrite newer preview text with stale snapshots.
|
|
10
|
+
- `[Polling]` Added a bounded poisoned-update policy for repeatedly failing Telegram updates. Impact: one malformed or consistently failing update can no longer stall the long-poll loop forever; after the retry threshold, the bridge records and advances past it.
|
|
11
|
+
- `[Menu]` Added short-lived model-menu input caching plus TTL/LRU cleanup for stored inline menu state. Impact: repeated `/status` and `/model` interactions do less settings/model-registry work, while old Telegram inline keyboards expire predictably instead of accumulating for the whole session.
|
|
12
|
+
- `[Updates]` Routed Telegram `edited_message` updates separately from new messages and applied edits to matching queued turns. Impact: editing a still-queued Telegram message updates the pending prompt instead of enqueueing a duplicate turn.
|
|
13
|
+
- `[Refactor]` Moved shared Telegram Bot API transport shapes out of `index.ts` into `lib/types.ts`. Impact: the entrypoint is smaller and future runtime extraction can reuse one type boundary instead of keeping local duplicate interfaces.
|
|
14
|
+
- `[Refactor]` Moved Telegram media-group debounce and pending-group removal into the existing media domain with mirrored tests. Impact: album coalescing and reaction/delete cleanup are easier to validate without reading the full extension entrypoint, while avoiding an unnecessary extra domain file.
|
|
15
|
+
- `[Refactor]` Extracted Telegram slash-command parsing and command-action routing into `lib/commands.ts` with mirrored tests, and moved queued-turn text replacement for edited messages into the turn domain. Impact: command normalization/execution planning and queued edit mutations are reusable, while the entrypoint keeps less local runtime state logic.
|
|
16
|
+
- `[Rendering]` Hardened link rendering so absolute links stay clickable, markdown-heavy link labels reduce to plain clickable labels, tooltip titles are ignored safely, balanced-parenthesis URLs stay intact, and unsupported link forms degrade without broken anchors. Impact: Telegram replies now keep more links usable while avoiding malformed output for relative, reference-style, or footnote-like link syntax.
|
|
17
|
+
- `[Preview]` Evolved rich streaming from first-chunk snapshots to stable-block previews with a conservative plain tail fallback, while preserving original blank-line spacing between rendered blocks and keeping headings visually separated from following blocks. Impact: closed top-level Markdown blocks now stream as rich Telegram HTML before finalization, incomplete fences, quotes, lists, and other trailing work remain readable without producing broken rich formatting, preview/final block spacing no longer collapses extra empty lines, and headings no longer visually merge into following code blocks when source Markdown omits a blank line.
|
|
18
|
+
- `[Refactor]` Split preview concerns so runtime transport and finalization live in the preview domain while preview snapshot derivation lives in the rendering domain. Impact: rich streaming can evolve independently from final reply delivery while keeping preview appearance decisions closer to the Telegram renderer.
|
|
19
|
+
- `[Streaming]` Switched Telegram previews from plain draft-first text to rich first-chunk message editing, so formatting appears during generation instead of only after finalization. Impact: users now see richer streamed output earlier, while final replies still replace the preview with fully rendered Telegram HTML.
|
|
5
20
|
- `[Rendering]` Preserved leading indentation on the first Markdown line, kept numeric markers for ordered task lists in both preview and final Telegram rendering, and stopped reinterpreting standalone `[x]` or `[ ]` prose as inline checkboxes. Impact: nested content no longer flattens when a message starts with indentation, numbered checklists keep their ordered semantics, and literal checklist-like prose stays literal.
|
|
6
21
|
- `[Queue UI]` Marked liked high-priority queued Telegram turns with `β¬` in the pi status-bar queue preview. Impact: operators can now distinguish reaction-promoted turns from normal queued prompts at a glance.
|
|
7
22
|
- `[Docs]` Added short responsibility header comments to every project `.ts` file. Impact: file boundaries are easier to understand while navigating the growing `/lib` split.
|
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Telegram DM bridge for pi.
|
|
6
|
-
|
|
7
5
|
This repository is an actively maintained fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008460b6c1ca036d92322f69d87f626be0fc`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
|
|
8
6
|
|
|
9
7
|
## Start Here
|
|
@@ -19,9 +17,9 @@ This repository is an actively maintained fork of [`badlogic/pi-telegram`](https
|
|
|
19
17
|
- **Interactive UI**: Manage your session directly from Telegram. Inline buttons allow you to switch models and adjust reasoning (thinking) levels on the fly.
|
|
20
18
|
- **In-flight Model Switching**: Change the active model mid-generation. The agent gracefully pauses, applies the new model, and restarts its response without losing context.
|
|
21
19
|
- **Smart Message Queue**: Messages sent while the agent is busy are queued and previewed in the pi status bar, and queued turns can be reprioritized or removed with Telegram reactions.
|
|
22
|
-
- **Mobile-Optimized Rendering**: Tables and lists are formatted for narrow screens. Markdown is correctly parsed and split to fit Telegram's limits without breaking HTML structures or code blocks.
|
|
20
|
+
- **Mobile-Optimized Rendering**: Tables and lists are formatted for narrow screens. Markdown is correctly parsed and split to fit Telegram's limits without breaking HTML structures or code blocks, block spacing stays faithful to the original Markdown with readable heading separation, supported absolute links stay clickable, and unsupported link forms degrade safely.
|
|
23
21
|
- **File Handling & Attachments**: Send images and files to the agent, or ask it to generate and return artifacts. Outbound files are delivered automatically via the `telegram_attach` tool.
|
|
24
|
-
- **Streaming Responses**:
|
|
22
|
+
- **Streaming Responses**: Closed Markdown blocks stream back as rich Telegram HTML while pi is generating, and the still-growing tail stays readable until the final fully rendered reply lands.
|
|
25
23
|
|
|
26
24
|
## Install
|
|
27
25
|
|
|
@@ -80,7 +78,7 @@ Once paired, simply chat with your bot in Telegram. All text, images, and files
|
|
|
80
78
|
- **`/status`**: View session stats, cost, and use inline buttons to change models.
|
|
81
79
|
- **`/model`**: Open the interactive model selector.
|
|
82
80
|
- **`/compact`**: Start session compaction (only works when the session is idle).
|
|
83
|
-
- **`/stop
|
|
81
|
+
- **`/stop`**: Abort the active run.
|
|
84
82
|
- **`/telegram-disconnect`** (in pi): Stop polling in the current session.
|
|
85
83
|
- **`/telegram-status`** (in pi): Check bridge status.
|
|
86
84
|
|
|
@@ -90,30 +88,35 @@ Once paired, simply chat with your bot in Telegram. All text, images, and files
|
|
|
90
88
|
- `π` moves a waiting turn into the priority block. Removing `π` sends it back to its normal queue position, and adding `π` again gives it a fresh priority position.
|
|
91
89
|
- `π` removes a waiting turn from the queue. Telegram Bot API does not expose ordinary DM message-deletion events through the polling path used here, so queue removal is bound to the dislike reaction.
|
|
92
90
|
- For media groups, a reaction on any message in the group applies to the whole queued turn.
|
|
93
|
-
-
|
|
91
|
+
- If you edit a Telegram message while it is still waiting in the queue, the queued turn is updated instead of creating a duplicate prompt. Edits after a turn has already started may not affect the active run.
|
|
92
|
+
- Inbound images, albums, and files are saved to `~/.pi/agent/tmp/telegram`, local file paths are included in the prompt, and inbound images are forwarded to pi as image inputs.
|
|
94
93
|
- Queue reactions depend on Telegram delivering `message_reaction` updates for your bot and chat type.
|
|
95
94
|
|
|
96
95
|
### Requesting Files
|
|
97
96
|
|
|
98
97
|
If you ask pi for a file or generated artifact (e.g., _"generate a shell script and attach it"_), pi will call the `telegram_attach` tool, and the extension will send the file alongside its next Telegram reply.
|
|
99
98
|
|
|
100
|
-
|
|
99
|
+
## Streaming
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
- `generate a shell script and attach it`
|
|
101
|
+
The extension streams assistant previews back to Telegram while pi is generating.
|
|
104
102
|
|
|
105
|
-
|
|
103
|
+
Rich previews are sent through editable messages because Telegram drafts are text-only. Closed top-level Markdown blocks can appear with formatting before the answer finishes, while the still-growing tail remains conservative and readable until the preview is replaced with the fully rendered Telegram HTML reply.
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
## Status bar
|
|
108
106
|
|
|
109
|
-
|
|
107
|
+
The pi status bar shows queued Telegram turns as compact previews, for example:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
+3: [β¬ write a shell scriptβ¦, summarize this imageβ¦, π 2 attachments]
|
|
111
|
+
```
|
|
110
112
|
|
|
111
113
|
## Notes
|
|
112
114
|
|
|
113
115
|
- Only one pi session should be connected to the bot at a time
|
|
114
116
|
- Replies are sent as normal Telegram messages, not quote-replies
|
|
115
|
-
- Long replies are split below Telegram's 4096 character limit
|
|
117
|
+
- Long replies are split below Telegram's 4096 character limit without intentionally breaking Telegram HTML formatting
|
|
116
118
|
- Outbound files are sent via `telegram_attach`
|
|
119
|
+
- Temporary inbound Telegram files are cleaned up on later session starts
|
|
117
120
|
|
|
118
121
|
## License
|
|
119
122
|
|
package/docs/architecture.md
CHANGED
|
@@ -19,18 +19,21 @@ Naming rule: because the repository already scopes this codebase to Telegram, ex
|
|
|
19
19
|
|
|
20
20
|
Current runtime areas include:
|
|
21
21
|
|
|
22
|
-
- Telegram API types
|
|
22
|
+
- Telegram Bot API transport shape types in `/lib/types.ts`, with local bridge state still composed in `index.ts`
|
|
23
23
|
- Queueing and queue-runtime helpers in `/lib/queue.ts`
|
|
24
|
-
-
|
|
24
|
+
- Preview transport-selection, preview-finalization, and preview-runtime helpers in `/lib/preview.ts`
|
|
25
|
+
- Reply-transport and rendered-message delivery helpers in `/lib/replies.ts`
|
|
26
|
+
- Preview appearance and snapshot derivation stay in `/lib/rendering.ts`, while `/lib/preview.ts` owns transport and lifecycle decisions, so richer preview strategies can evolve without entangling Markdown formatting with Telegram delivery state
|
|
25
27
|
- Polling request, stop-condition, and long-poll loop helpers in `/lib/polling.ts`
|
|
26
28
|
- Telegram API/config helpers and lazy bot-token client wrappers in `/lib/api.ts`
|
|
27
29
|
- Telegram turn-building helpers in `/lib/turns.ts`
|
|
28
|
-
- Telegram media/text extraction helpers in `/lib/media.ts`
|
|
30
|
+
- Telegram media/text extraction, file-info normalization, and media-group debounce helpers in `/lib/media.ts`
|
|
31
|
+
- Telegram slash-command parsing and command-action routing helpers in `/lib/commands.ts`
|
|
29
32
|
- Telegram updates extraction, authorization, flow, execution-planning, direct execute-from-update routing, and runtime helpers in `/lib/updates.ts`
|
|
30
33
|
- Telegram attachment queueing and delivery helpers in `/lib/attachments.ts`
|
|
31
34
|
- Telegram tool, command, and lifecycle-hook registration helpers in `/lib/registration.ts`
|
|
32
35
|
- Setup/token prompt helpers in `/lib/setup.ts`
|
|
33
|
-
- Markdown and Telegram message rendering helpers in `/lib/rendering.ts`
|
|
36
|
+
- Markdown, preview-snapshot, and Telegram message rendering helpers in `/lib/rendering.ts`
|
|
34
37
|
- Status rendering helpers in `/lib/status.ts`
|
|
35
38
|
- Menu/model-resolution, menu-state construction, pure menu-page derivation, pure menu render-payload builders, menu-message runtime, callback parsing, callback entry handling, callback mutation helpers, full model-callback planning and execution, interface-polished callback effect ports, status-thinking callback handling, and UI helpers in `/lib/menu.ts`
|
|
36
39
|
- Model-switch guard, continuation, and restart helpers in `/lib/model-switch.ts`
|
|
@@ -53,11 +56,13 @@ Because `ctx.ui.input()` only exposes placeholder text, the bridge uses `ctx.ui.
|
|
|
53
56
|
### Inbound Path
|
|
54
57
|
|
|
55
58
|
1. Telegram updates are polled through `getUpdates`
|
|
56
|
-
2.
|
|
57
|
-
3.
|
|
58
|
-
4.
|
|
59
|
-
5.
|
|
60
|
-
6.
|
|
59
|
+
2. Each update offset is persisted only after the update handler succeeds; repeated handler failures are bounded so one poisoned update cannot stall polling forever
|
|
60
|
+
3. The bridge filters to the paired private user
|
|
61
|
+
4. Media groups are coalesced into a single Telegram turn when needed
|
|
62
|
+
5. Files are streamed into `~/.pi/agent/tmp/telegram` with size-limit checks, partial-download cleanup on failures, and stale temp cleanup on session start
|
|
63
|
+
6. A `PendingTelegramTurn` is created and queued locally
|
|
64
|
+
7. Telegram `edited_message` updates are routed separately and update a matching queued turn when the original message has not been dispatched yet
|
|
65
|
+
8. The queue dispatcher sends the turn into pi only when dispatch is safe
|
|
61
66
|
|
|
62
67
|
### Queue Safety Model
|
|
63
68
|
|
|
@@ -94,13 +99,15 @@ Key rules:
|
|
|
94
99
|
|
|
95
100
|
- Rich text should render cleanly in Telegram chats
|
|
96
101
|
- Real code blocks must remain literal and escaped
|
|
102
|
+
- Supported absolute HTTP(S) and mailto links should stay clickable, with generated HTML attributes escaped separately from text content, while unsupported link forms such as unresolved references, footnotes, or relative links without a known base should degrade safely instead of producing broken Telegram anchors
|
|
97
103
|
- Markdown tables should keep their internal separators but drop the outer left and right borders when rendered as monospace blocks so narrow Telegram clients keep more usable width
|
|
98
104
|
- Unordered Markdown lists should render with a monospace `-` marker and ordered Markdown lists should render with monospace numeric markers so list indentation stays more predictable on narrow Telegram clients
|
|
99
105
|
- Real Markdown task-list items should render with checkbox markers, while standalone `[x]` and `[ ]` prose should stay literal instead of being reinterpreted as checklists
|
|
100
106
|
- Nested Markdown quotes should flatten into one Telegram blockquote with added non-breaking-space indentation because Telegram does not render nested blockquotes reliably
|
|
101
|
-
-
|
|
107
|
+
- Original blank-line spacing between Markdown blocks should stay intact in both preview and final rendering instead of being collapsed to one generic block separator, while headings should still keep readable separation from following blocks such as code fences even when source Markdown omits a blank line
|
|
108
|
+
- Long replies, including raw HTML-mode replies used by interactive/status flows, must be split below Telegram's 4096-character limit
|
|
102
109
|
- Chunking should avoid breaking HTML structure where possible
|
|
103
|
-
- Preview rendering
|
|
110
|
+
- Preview rendering uses stable top-level Markdown blocks for rich Telegram HTML and appends the still-growing tail conservatively as readable plain text so the preview stays valid even when the answer is incomplete
|
|
104
111
|
|
|
105
112
|
The renderer is a Telegram-specific formatter, not a general Markdown engine, so rendering changes should be treated as regression-prone.
|
|
106
113
|
|
|
@@ -110,11 +117,14 @@ During generation, the bridge streams previews back to Telegram.
|
|
|
110
117
|
|
|
111
118
|
Preferred order:
|
|
112
119
|
|
|
113
|
-
1.
|
|
114
|
-
2.
|
|
115
|
-
3.
|
|
120
|
+
1. Re-render the current Markdown buffer into a preview snapshot that renders closed top-level blocks as rich Telegram HTML and keeps the unstable tail conservative and readable
|
|
121
|
+
2. Send or update that preview through `sendMessage` plus `editMessageText`, because `sendMessageDraft` is text-only for rich previews
|
|
122
|
+
3. Serialize overlapping preview flushes so older Telegram edit calls cannot race newer streamed snapshots
|
|
123
|
+
4. Replace the preview with the final rendered reply when generation ends
|
|
116
124
|
|
|
117
|
-
|
|
125
|
+
Draft streaming can remain as a plain-text fallback path, but rich Telegram previews are driven through editable messages and stable-block snapshot selection.
|
|
126
|
+
|
|
127
|
+
Outbound files are sent only after the active Telegram turn completes, must be staged through the `telegram_attach` tool, and use file-backed multipart blobs so large sends do not require preloading whole files into memory.
|
|
118
128
|
|
|
119
129
|
## Interactive Controls
|
|
120
130
|
|
|
@@ -123,7 +133,7 @@ The bridge exposes Telegram-side session controls in addition to regular chat fo
|
|
|
123
133
|
Current operator controls include:
|
|
124
134
|
|
|
125
135
|
- `/status` for model, usage, cost, and context visibility, queued as a high-priority control item when needed
|
|
126
|
-
- Inline status buttons for model and thinking adjustments, applying idle selections immediately while still respecting busy-run restart rules
|
|
136
|
+
- Inline status buttons for model and thinking adjustments, applying idle selections immediately while still respecting busy-run restart rules; model-menu inputs are cached briefly and stored inline-menu states are pruned by TTL/LRU so old keyboards expire predictably
|
|
127
137
|
- `/model` for interactive model selection, queued as a high-priority control item when needed and supporting in-flight restart of the active Telegram-owned run on a newly selected model
|
|
128
138
|
- `/compact` for Telegram-triggered pi session compaction when the bridge is idle
|
|
129
139
|
- Queue reactions using `π` and `π`, with `π` acting as the canonical queue-removal path because ordinary Telegram DM message deletions are not exposed through the Bot API polling path this bridge uses
|