@llblab/pi-telegram 0.2.0

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 ADDED
@@ -0,0 +1,90 @@
1
+ # Project Context
2
+
3
+ ## 0. Meta-Protocol Principles
4
+
5
+ - `Constraint-Driven Evolution`: Add structure when the bridge gains real operator or runtime constraints
6
+ - `Single Source of Truth`: Keep durable rules in `AGENTS.md`, open work in `BACKLOG.md`, completed delivery in `CHANGELOG.md`, and deeper technical detail in `/docs`
7
+ - `Boundary Clarity`: Separate Telegram transport concerns, pi integration concerns, rendering behavior, and release/documentation state
8
+ - `Progressive Enhancement + Graceful Degradation`: Prefer behavior that upgrades automatically when richer runtime context exists, but always preserves a useful fallback path when it does not
9
+ - `Runtime Safety`: Prefer queue and rendering behavior that fails predictably over clever behavior that can desynchronize the Telegram bridge from pi session state
10
+
11
+ ## 1. Concept
12
+
13
+ `pi-telegram` is a pi extension that turns a Telegram DM into a session-local frontend for pi, including text/file forwarding, streaming previews, queued follow-ups, model controls, and outbound attachment delivery.
14
+
15
+ ## 2. Identity & Naming Contract
16
+
17
+ - `Telegram turn`: One unit of Telegram input processed by pi; this may represent one message or a coalesced media group
18
+ - `Queued Telegram turn`: A Telegram turn accepted by the bridge but not yet active in pi
19
+ - `Active Telegram turn`: The Telegram turn currently bound to the running pi agent loop
20
+ - `Preview`: The transient streamed response shown through Telegram drafts or editable messages before the final reply lands
21
+ - `Scoped models`: The subset of models exposed to Telegram model selection when pi settings or CLI flags limit the available list
22
+
23
+ ## 3. Project Topology
24
+
25
+ - `/index.ts`: Main extension entrypoint and runtime composition layer for the bridge
26
+ - `/lib/*.ts`: Flat domain modules for reusable runtime logic. Favor domain files such as queueing/runtime, replies, polling, updates, attachments, registration/hooks, Telegram API/config, turns, media, setup, rendering, menu/status/model-resolution support, and other cohesive bridge subsystems; use `shared` only when a type or constant truly spans multiple domains
27
+ - `/tests/*.test.ts`: Domain-mirrored regression suites that follow the same flat naming as `/lib`
28
+ - `/docs/README.md`: Documentation index for technical project docs
29
+ - `/docs/architecture.md`: Runtime and subsystem overview for the bridge
30
+ - `/README.md`: User-facing project entry point, install guide, and fork summary
31
+ - `/AGENTS.md`: Durable engineering and runtime conventions
32
+ - `/BACKLOG.md`: Canonical open work
33
+ - `/CHANGELOG.md`: Completed delivery history
34
+
35
+ ## 4. Core Entities
36
+
37
+ - `TelegramConfig`: Persisted bot/session pairing state
38
+ - `PendingTelegramTurn` / `ActiveTelegramTurn`: Queue and active-turn state for Telegram-originated work
39
+ - `TelegramPreviewState`: Streaming preview state for drafts or editable Telegram messages
40
+ - `TelegramModelMenuState`: Inline menu state for status/model/thinking controls
41
+ - `QueuedAttachment`: Outbound files staged for delivery through `telegram_attach`
42
+
43
+ ## 5. Architectural Decisions
44
+
45
+ - `index.ts` stays the single extension entrypoint, while reusable runtime logic should be split into flat domain files under `/lib`; prefer domain-oriented grouping over atomizing every helper into its own file, and use `shared` sparingly for genuinely cross-domain types or constants
46
+ - The bridge is session-local and intentionally pairs with a single allowed Telegram user per config
47
+ - Telegram queue state is tracked locally and must stay aligned with pi agent lifecycle hooks; queued items now have explicit kinds and lanes so prompt turns and synthetic control actions can share one ordering model, while dispatch still respects active turns, pending dispatch, compaction, and pi pending-message state
48
+ - Prompt items should remain in the queue until `agent_start` consumes the dispatched turn; removing them earlier breaks active-turn binding, preview delivery, and end-of-turn follow-up behavior
49
+ - In-flight `/model` switching is supported only for Telegram-owned active turns and is implemented as set-model plus synthetic continuation turn plus abort; if a tool call is active, the abort is delayed until that tool finishes instead of interrupting the tool mid-flight
50
+ - Telegram replies render through Telegram HTML, not raw Markdown; real code blocks must stay literal and escaped
51
+ - `telegram_attach` is the canonical outbound file-delivery path for Telegram-originated requests
52
+
53
+ ## 6. Engineering Conventions
54
+
55
+ - Treat queue handling, compaction interaction, and lifecycle-hook state transitions as regression-prone areas; validate them after changing dispatch logic
56
+ - Treat Markdown rendering as Telegram-specific output work, not generic Markdown rendering; preserve literal code content, avoid HTML chunk splits that break tags, prefer width-efficient monospace table and list formatting for narrow clients, and flatten nested Markdown quotes into indented single-blockquote output because Telegram does not render nested blockquotes reliably
57
+ - Keep comments and user-facing docs in English unless the surrounding file already follows another convention
58
+ - Each project `.ts` file should start with a short multi-line responsibility header comment that explains the file boundary to future maintainers
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
61
+
62
+ ## 7. Operational Conventions
63
+
64
+ - When Telegram-visible behavior changes, sync `README.md` and the relevant `/docs` entry in the same pass
65
+ - When durable runtime constraints or repeat bug patterns emerge, record them here instead of burying them in changelog prose
66
+ - When fork identity changes, keep `README.md`, package metadata, and docs aligned so the published package does not point back at stale upstream coordinates
67
+ - Work only inside this repository during development tasks; updating the installed Pi extension checkout is a separate manual operator step, not part of normal in-repo implementation work
68
+
69
+ ## 8. Integration Protocols
70
+
71
+ - Telegram API methods currently used include polling, message editing, draft streaming, callback queries, reactions, file download, and media upload endpoints
72
+ - pi integration depends on lifecycle hooks such as `before_agent_start`, `agent_start`, `message_start`, `message_update`, and `agent_end`
73
+ - `ctx.ui.input()` provides placeholder text rather than an editable prefilled value; when a real default must appear already filled in, prefer `ctx.ui.editor()`
74
+ - For `/telegram-setup`, prefer the locally saved bot token over environment variables on repeat setup runs; env vars are the bootstrap path when no local token exists
75
+ - Status/model/thinking controls are driven through Telegram inline keyboards and callback queries
76
+ - Inbound files may become pi image inputs; outbound files must flow through `telegram_attach`
77
+
78
+ ## 9. Pre-Task Preparation Protocol
79
+
80
+ - Read `README.md` for current user-facing behavior and fork positioning
81
+ - Read `BACKLOG.md` before changing runtime behavior or documentation so open work stays truthful
82
+ - Read `/docs/architecture.md` before restructuring queue, preview, rendering, or command-handling logic
83
+ - Inspect the relevant `index.ts` section before editing because most bridge behavior is stateful and cross-linked
84
+
85
+ ## 10. Task Completion Protocol
86
+
87
+ - Run the smallest meaningful validation for the touched area; `npm test` is the default regression suite once rendering or queue logic changes
88
+ - For rendering changes, ensure regressions still cover nested lists, code blocks, underscore-heavy text, and long-message chunking
89
+ - For queue/dispatch changes, validate abort, compaction, pending-dispatch, and pi pending-message guard behavior
90
+ - Sync `README.md`, `CHANGELOG.md`, `BACKLOG.md`, and `/docs` whenever user-visible behavior or real open-work state changes
package/BACKLOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Project Backlog
2
+
3
+ ## Open Backlog
4
+
5
+ - No open backlog items right now
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## Current
4
+
5
+ - `[Docs]` Added short responsibility header comments to every project `.ts` file. Impact: file boundaries are easier to understand while navigating the growing `/lib` split.
6
+ - `[Naming]` Renamed extracted domain modules and mirrored regression suites to use repo-scoped bare domain filenames such as `api.ts`, `queue.ts`, and `queue.test.ts` instead of repeating `telegram-*` in every path. Impact: the internal topology is easier to scan and stays aligned with the repository-level Telegram scope.
7
+ - `[Controls]` Expanded Telegram session controls with a richer `/status` view, inline model selection, and thinking-level controls. Impact: more bridge configuration can be managed directly from Telegram.
8
+ - `[Queue]` Upgraded Telegram turn queueing with previews, reaction-driven prioritization/removal, media-group handling, aborted-turn history preservation, and safer dispatch gating. Impact: follow-up handling is more transparent and less prone to lifecycle races.
9
+ - `[Rendering]` Added Telegram-oriented Markdown rendering and hardened reply streaming/chunking behavior, including narrower monospace Markdown table output without outer side borders, monospace list markers for unordered and ordered lists, and flattened nested quote indentation inside a single Telegram blockquote. Impact: formatted replies render more reliably while preserving literal code blocks and using width more efficiently on narrow Telegram clients.
10
+ - `[Runtime]` Hardened attachment delivery, polling/runtime behavior, Telegram session integration, preview-finalization and reply-transport routing into the replies domain, lazy Telegram API client routing into the Telegram API domain, turn-building extraction into its own domain, menu/model-resolution plus menu-state, 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, and interface-polished callback effect ports into the menu domain, direct execute-from-update routing into the updates domain, model-switch restart glue extraction into the model-switch domain, and tool/command/lifecycle-hook registration extraction into a dedicated registration domain. Impact: the bridge is more robust as a daily Telegram frontend for pi.
11
+ - `[Metadata]` Updated package repository metadata to point at the `llblab/pi-telegram` fork and renamed the npm package to `@llblab/pi-telegram` with public scoped publish settings. Impact: published package links no longer send users to stale upstream coordinates and the package can be published under the fork-owned npm scope.
12
+ - `[Validation]` Added lightweight regression tests for Telegram Markdown rendering, queue/runtime/agent-loop/session/control/dispatch, replies, polling, updates, attachments, registration, turns, menu, and Telegram API/media/config helpers, including quote/list, table, link/code, mixed-link/code chunking, mixed-block chunk transitions, long multi-block, long-quote, long inline-formatting chunk boundaries, list-code-quote-prose chunk transitions, narrower monospace table rendering without outer side borders, monospace unordered and ordered list markers, flattened nested quote indentation inside one Telegram blockquote, inbound poll/pair/dispatch runtime cases, preview finalization, aborted-turn history carry-over, queued-status/model-after-agent-end sequencing, compaction gating, media-group debounce dispatch, direct menu callback planning and execution, pure menu-page derivation, pure menu render-payload builders, reaction-driven reprioritization/removal, immediate in-flight model-switch continuation, delayed abort-after-tool-completion, lazy Telegram API client routing, turn-building, and scoped-model resolution. Impact: key renderer and queue invariants now have repeatable automated coverage across the known high-risk bridge paths.
13
+ - `[Model Switching]` Enabled `/model` during an active Telegram-owned run by applying the new model and continuing on the new model automatically, delaying the abort until the current tool finishes when needed. Impact: Telegram can now approximate pi's manual stop-switch-continue workflow with fewer mid-tool aborts.
14
+ - `[Queue Core]` Introduced queued item kinds and explicit queue-lane ordering semantics so prompt turns and synthetic control actions share one ordering model, then regrouped the extracted helpers into flatter domain-oriented `/lib` modules such as queue, replies, polling, updates, attachments, turns, menu, Telegram API, and registration while keeping `index.ts` as the entrypoint. Prompt items now stay queued until `agent_start` consumes the dispatched turn, which restores correct active-turn binding for previews and final delivery. Impact: the bridge now has a clearer foundation for scheduling async extension operations alongside Telegram prompts without losing a single obvious runtime entry file.
15
+ - `[Registration]` Moved extension tool, command, and lifecycle-hook binding into the registration domain and added registration-focused regression coverage. Impact: extension wiring is easier to reason about and test without dragging full runtime state into every registration change.
16
+ - `[Control Queue]` Moved `/status` and `/model` command handling onto high-priority control queue items. Impact: control actions can wait safely behind the current run while still jumping ahead of normal queued prompts.
17
+ - `[Setup]` `/telegram-setup` now shows the stored bot token first, otherwise prefills from common Telegram bot environment variables before falling back to the placeholder, using an actual prefilled editor when a real default exists. Impact: repeat setup respects local saved state while first-run and secret-managed setup stay fast.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # pi-telegram
2
+
3
+ ![pi-telegram screenshot](screenshot.png)
4
+
5
+ Telegram DM bridge for pi.
6
+
7
+ This repository is a fork of the original [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram).
8
+ It started from upstream commit [`cb34008460b6c1ca036d92322f69d87f626be0fc`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
9
+
10
+ ## Start Here
11
+
12
+ - [Project Context](./AGENTS.md)
13
+ - [Open Backlog](./BACKLOG.md)
14
+ - [Changelog](./CHANGELOG.md)
15
+ - [Documentation](./docs/README.md)
16
+
17
+ ## What Changed In This Fork
18
+
19
+ Compared to upstream commit `cb34008`, this fork significantly extends and hardens the extension.
20
+
21
+ - Better Telegram control UI, including an improved `/status` view with inline buttons for model and thinking selection
22
+ - Interactive model selection improvements, including scoped model lists, thinking-level control for reasoning-capable models, and in-flight restart on a newly selected model for active Telegram-owned runs
23
+ - Queueing and interaction upgrades, including queue previews, reaction-based prioritization/removal, media-group handling, high-priority control actions, and safer dispatch behavior
24
+ - Markdown and reply rendering improvements, with richer formatting support, narrow-client-friendly table/list rendering, quote compatibility fixes, and multiple fixes for incorrect Telegram rendering and chunking edge cases
25
+ - Streaming, attachment, and delivery workflow hardening, including more robust preview updates and file handling
26
+ - General runtime polish, bug fixes, and refactors across pairing, command handling, and Telegram session behavior
27
+ - Cleaner internal domain layout, with flat `/lib/*.ts` modules and mirrored `/tests/*.test.ts` suites that use repo-scoped domain names instead of redundant `telegram-*` filename prefixes
28
+
29
+ In short: this fork is no longer just a repackaged copy of upstream; it is a feature-expanded and bug-fixed Telegram frontend for pi.
30
+
31
+ ## Install
32
+
33
+ From npm once published:
34
+
35
+ ```bash
36
+ pi install @llblab/pi-telegram
37
+ ```
38
+
39
+ From git:
40
+
41
+ ```bash
42
+ pi install git:github.com/llblab/pi-telegram
43
+ ```
44
+
45
+ Or for a single run:
46
+
47
+ ```bash
48
+ pi -e @llblab/pi-telegram
49
+ ```
50
+
51
+ ## Configure
52
+
53
+ ### Telegram
54
+
55
+ 1. Open [@BotFather](https://t.me/BotFather)
56
+ 2. Run `/newbot`
57
+ 3. Pick a name and username
58
+ 4. Copy the bot token
59
+
60
+ ### pi
61
+
62
+ Start pi, then run:
63
+
64
+ ```bash
65
+ /telegram-setup
66
+ ```
67
+
68
+ Paste the bot token when prompted.
69
+ If a bot token is already saved in `~/.pi/agent/telegram.json`, `/telegram-setup` shows that stored value by default. Otherwise it pre-fills from the first configured environment variable in `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, or `TELEGRAM_KEY`.
70
+
71
+ The extension stores config in:
72
+
73
+ ```text
74
+ ~/.pi/agent/telegram.json
75
+ ```
76
+
77
+ ## Connect a pi session
78
+
79
+ The Telegram bridge is session-local. Connect it only in the pi session that should own the bot:
80
+
81
+ ```bash
82
+ /telegram-connect
83
+ ```
84
+
85
+ To stop polling in the current session:
86
+
87
+ ```bash
88
+ /telegram-disconnect
89
+ ```
90
+
91
+ Check status:
92
+
93
+ ```bash
94
+ /telegram-status
95
+ ```
96
+
97
+ ## Pair your Telegram account
98
+
99
+ After token setup and `/telegram-connect`:
100
+
101
+ 1. Open the DM with your bot in Telegram
102
+ 2. Send `/start`
103
+
104
+ The first DM user becomes the allowed Telegram user for the bridge. The extension only accepts messages from that user.
105
+
106
+ ## Usage
107
+
108
+ Chat with your bot in Telegram DMs.
109
+
110
+ Additional fork-specific controls:
111
+
112
+ - `/status` now has a richer view with inline buttons for model and thinking controls, and joins the high-priority control queue when pi is busy
113
+ - `/model` opens the interactive model selector, joins the high-priority control queue when pi is busy, and can restart the active Telegram-owned run on the newly selected model, waiting for the current tool call to finish when needed
114
+ - `/compact` starts session compaction when pi and the Telegram queue are idle
115
+ - Queue reactions: `👍` prioritizes a waiting turn, `👎` removes it
116
+
117
+ ### Send text
118
+
119
+ Send any message in the bot DM. It is forwarded into pi with a `[telegram]` prefix.
120
+
121
+ ### Send images and files
122
+
123
+ Send images, albums, or files in the DM.
124
+
125
+ The extension:
126
+
127
+ - downloads them to `~/.pi/agent/tmp/telegram`
128
+ - includes local file paths in the prompt
129
+ - forwards inbound images as image inputs to pi
130
+
131
+ ### Ask for files back
132
+
133
+ If you ask pi for a file or generated artifact, pi should call the `telegram_attach` tool. The extension then sends those files with the next Telegram reply.
134
+
135
+ Examples:
136
+
137
+ - `summarize this image`
138
+ - `read this README and summarize it`
139
+ - `write me a markdown file with the plan and send it back`
140
+ - `generate a shell script and attach it`
141
+
142
+ ### Stop a run
143
+
144
+ In Telegram, send:
145
+
146
+ ```text
147
+ stop
148
+ ```
149
+
150
+ or:
151
+
152
+ ```text
153
+ /stop
154
+ ```
155
+
156
+ That aborts the active pi turn.
157
+
158
+ ### Queue follow-ups
159
+
160
+ If you send more Telegram messages while pi is busy, they are queued and processed in order.
161
+
162
+ The pi status bar shows queued Telegram turns as compact previews, for example:
163
+
164
+ ```text
165
+ +3: [summarize this image…, write a shell script…, 📎 2 attachments]
166
+ ```
167
+
168
+ Each preview is limited to at most 5 words or 40 characters.
169
+
170
+ ### Reprioritize or discard queued messages
171
+
172
+ While a message is still waiting in the queue:
173
+
174
+ - React with 👍 to move it into the priority block
175
+ - React with 👎 to remove it from the queue
176
+
177
+ Priority is stable:
178
+
179
+ - The first liked queued message stays ahead of later liked messages
180
+ - Removing 👍 sends the message back to its normal queue position
181
+ - Adding 👍 again gives it a fresh priority position
182
+
183
+ For media groups, a reaction on any message in the group applies to the whole queued turn.
184
+
185
+ Message reactions depend on Telegram delivering `message_reaction` updates for your bot and chat type.
186
+
187
+ ## Streaming
188
+
189
+ The extension streams assistant text previews back to Telegram while pi is generating.
190
+
191
+ It tries Telegram draft streaming first with `sendMessageDraft`. If that is not supported for your bot, it falls back to `sendMessage` plus `editMessageText`.
192
+
193
+ ## Notes
194
+
195
+ - Only one pi session should be connected to the bot at a time
196
+ - Replies are sent as normal Telegram messages, not quote-replies
197
+ - Long replies are split below Telegram's 4096 character limit
198
+ - Outbound files are sent via `telegram_attach`
199
+
200
+ ## License
201
+
202
+ MIT
package/docs/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Documentation Index
2
+
3
+ Living index of project documentation in `/docs`.
4
+
5
+ ## Documents
6
+
7
+ | Document | Description |
8
+ | --- | --- |
9
+ | [architecture.md](./architecture.md) | Overview of the Telegram bridge runtime, queueing model, rendering pipeline, and interactive controls |
@@ -0,0 +1,148 @@
1
+ # Telegram Bridge Architecture
2
+
3
+ ## Overview
4
+
5
+ `pi-telegram` is a session-local pi extension that binds one Telegram DM to one running pi session. The bridge owns four main responsibilities:
6
+
7
+ - Poll Telegram updates and enforce single-user pairing
8
+ - Translate Telegram messages and media into pi inputs
9
+ - Stream and deliver pi responses back to Telegram
10
+ - Manage Telegram-specific controls such as queue reactions, `/status`, `/model`, and `/compact`
11
+
12
+ ## Runtime Structure
13
+
14
+ `index.ts` remains the extension entrypoint and composition layer. Reusable runtime logic is split into flat domain files under `/lib` rather than into a deep local module tree.
15
+
16
+ Domain grouping rule: prefer cohesive domain files over atomizing every helper into its own file. A `shared` domain is allowed only for types or constants that genuinely span multiple bridge domains.
17
+
18
+ Naming rule: because the repository already scopes this codebase to Telegram, extracted module and test filenames use bare domain names such as `api.ts`, `queue.ts`, `updates.ts`, and `queue.test.ts` rather than repeating `telegram-*` in every filename.
19
+
20
+ Current runtime areas include:
21
+
22
+ - Telegram API types and local bridge state in `index.ts`
23
+ - Queueing and queue-runtime helpers in `/lib/queue.ts`
24
+ - Reply, preview, preview-finalization, reply-transport, and rendered-message delivery helpers in `/lib/replies.ts`
25
+ - Polling request, stop-condition, and long-poll loop helpers in `/lib/polling.ts`
26
+ - Telegram API/config helpers and lazy bot-token client wrappers in `/lib/api.ts`
27
+ - Telegram turn-building helpers in `/lib/turns.ts`
28
+ - Telegram media/text extraction helpers in `/lib/media.ts`
29
+ - Telegram updates extraction, authorization, flow, execution-planning, direct execute-from-update routing, and runtime helpers in `/lib/updates.ts`
30
+ - Telegram attachment queueing and delivery helpers in `/lib/attachments.ts`
31
+ - Telegram tool, command, and lifecycle-hook registration helpers in `/lib/registration.ts`
32
+ - Setup/token prompt helpers in `/lib/setup.ts`
33
+ - Markdown and Telegram message rendering helpers in `/lib/rendering.ts`
34
+ - Status rendering helpers in `/lib/status.ts`
35
+ - 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
+ - Model-switch guard, continuation, and restart helpers in `/lib/model-switch.ts`
37
+ - Telegram API-bound reply transport wiring and broader event-side orchestration in `index.ts`
38
+ - Additional domains can be extracted into `/lib/*.ts` as the bridge grows, while keeping `index.ts` as the single entrypoint
39
+ - Mirrored domain regression coverage lives in `/tests/*.test.ts` using the same bare domain naming scheme
40
+
41
+ ## Configuration UX
42
+
43
+ `/telegram-setup` uses a progressive-enhancement flow for the bot token prompt:
44
+
45
+ 1. Show the locally saved token from `~/.pi/agent/telegram.json` when one already exists
46
+ 2. Otherwise use the first configured environment variable from the supported Telegram token list
47
+ 3. Fall back to the example placeholder when no real value exists
48
+
49
+ Because `ctx.ui.input()` only exposes placeholder text, the bridge uses `ctx.ui.editor()` whenever a real default value must appear already filled in.
50
+
51
+ ## Message And Queue Flow
52
+
53
+ ### Inbound Path
54
+
55
+ 1. Telegram updates are polled through `getUpdates`
56
+ 2. The bridge filters to the paired private user
57
+ 3. Media groups are coalesced into a single Telegram turn when needed
58
+ 4. Files are downloaded into `~/.pi/agent/tmp/telegram`
59
+ 5. A `PendingTelegramTurn` is created and queued locally
60
+ 6. The queue dispatcher sends the turn into pi only when dispatch is safe
61
+
62
+ ### Queue Safety Model
63
+
64
+ The bridge keeps its own Telegram queue and does not rely only on pi's internal pending-message state.
65
+
66
+ Queued items now use two explicit dimensions:
67
+
68
+ - `kind`: prompt vs control
69
+ - `queueLane`: control vs priority vs default
70
+
71
+ This lets synthetic control actions and Telegram prompts share one stable ordering model while still rendering distinctly in status output.
72
+
73
+ A dispatched prompt remains in the queue until `agent_start` consumes it. That keeps the active Telegram turn bound correctly for previews, attachments, abort handling, and final reply delivery.
74
+
75
+ Dispatch is gated by:
76
+
77
+ - No active Telegram turn
78
+ - No pending Telegram dispatch already sent to pi
79
+ - No compaction in progress
80
+ - `ctx.isIdle()` being true
81
+ - `ctx.hasPendingMessages()` being false
82
+
83
+ This prevents queue races around rapid follow-ups, `/compact`, and mixed local plus Telegram activity.
84
+
85
+ ### Abort Behavior
86
+
87
+ When `/stop` aborts an active Telegram turn, queued follow-up Telegram messages can be preserved as prior-user history for the next turn. This keeps later Telegram input from being silently dropped after an interrupted run.
88
+
89
+ ## Rendering Model
90
+
91
+ Telegram replies are rendered as Telegram HTML rather than raw Markdown.
92
+
93
+ Key rules:
94
+
95
+ - Rich text should render cleanly in Telegram chats
96
+ - Real code blocks must remain literal and escaped
97
+ - 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
+ - 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
+ - Nested Markdown quotes should flatten into one Telegram blockquote with added non-breaking-space indentation because Telegram does not render nested blockquotes reliably
100
+ - Long replies must be split below Telegram's 4096-character limit
101
+ - Chunking should avoid breaking HTML structure where possible
102
+ - Preview rendering is intentionally simpler than final rich rendering
103
+
104
+ The renderer is a Telegram-specific formatter, not a general Markdown engine, so rendering changes should be treated as regression-prone.
105
+
106
+ ## Streaming And Delivery
107
+
108
+ During generation, the bridge streams previews back to Telegram.
109
+
110
+ Preferred order:
111
+
112
+ 1. Try `sendMessageDraft`
113
+ 2. Fall back to `sendMessage` plus `editMessageText`
114
+ 3. Replace the preview with the final rendered reply when generation ends
115
+
116
+ Outbound files are sent only after the active Telegram turn completes and must be staged through the `telegram_attach` tool.
117
+
118
+ ## Interactive Controls
119
+
120
+ The bridge exposes Telegram-side session controls in addition to regular chat forwarding.
121
+
122
+ Current operator controls include:
123
+
124
+ - `/status` for model, usage, cost, and context visibility, queued as a high-priority control item when needed
125
+ - Inline status buttons for model and thinking adjustments
126
+ - `/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
127
+ - `/compact` for Telegram-triggered pi session compaction when the bridge is idle
128
+ - Queue reactions using `👍` and `👎`
129
+
130
+ ## In-Flight Model Switching
131
+
132
+ When `/model` is used during an active Telegram-owned run, the bridge can emulate the interactive pi workflow of stopping, switching model, and continuing.
133
+
134
+ The current implementation does this by:
135
+
136
+ 1. Applying the newly selected model immediately
137
+ 2. Queuing or staging a synthetic Telegram continuation turn
138
+ 3. Aborting the active Telegram turn immediately, or delaying the abort until the current tool finishes when a tool call is in flight
139
+ 4. Dispatching the continuation turn after the abort completes
140
+
141
+ This behavior is intentionally limited to runs currently owned by the Telegram bridge. If pi is busy with non-Telegram work, the bridge still refuses the switch instead of hijacking unrelated session activity.
142
+
143
+ ## Related
144
+
145
+ - [README.md](../README.md)
146
+ - [Project Context](../AGENTS.md)
147
+ - [Project Backlog](../BACKLOG.md)
148
+ - [Changelog](../CHANGELOG.md)