@llblab/pi-telegram 0.20.6 → 0.21.1

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 CHANGED
@@ -83,7 +83,7 @@ The core product loop is mobile continuation: start or supervise work in the ter
83
83
  - Instance thread names are provisioner-authored identity metadata, not model-authored output. Fresh Threaded Mode threads select one of five curated compact 4-6 letter Latin names for the assigned slot letter using provisioning timestamp entropy, then create the Telegram thread with that thread name immediately. The title is the thread name itself, not `{slot} {threadName}` or `{slot} — {threadName}`. Do not register or reintroduce an agent-facing thread rename tool; removing the extra rename prompt/tool turn is an intentional simplicity constraint. Thread names must mirror into terminal status, live diagnostics, and `[telegram|thread:name]` through one target-aware current-instance identity resolver. Registered follower/leader metadata takes precedence over a stale shared record for the same target, with the record used only as fallback; do not hide a valid baked/custom thread name behind old slot-prefix validation. A follower that later becomes leader keeps its existing name; leadership changes are transport role changes, not identity resets.
84
84
  - Telegram private-chat Threaded Mode treats `All` as an aggregate/control surface, not a durable `General` thread and not a process launcher. Leader activation creates/reuses a leader thread in the private bot DM (chat = allowedUserId) proactively. If all threads are closed and the owner writes from `All`, a first new unbound thread should be reclaimed by the leader when the leader lacks an active bound thread. Later unknown threads require explicit routing or restore; creating another Pi instance is a manual terminal action followed by `/telegram-connect`.
85
85
  - Unbound thread detection: when a message arrives from the owner in an unknown `message_thread_id` while Threaded Mode is active, the default runtime first reclaims it for the leader if the leader has no active bound thread and routes the prompt locally. Later unknown threads are preserved by default and receive an in-thread reroute chooser that can send the captured prompt to a live thread or restore a stale leader/follower binding through explicit restore actions; destructive cleanup happens only after explicit user confirmation and through `thread-reconciler`. If the operator wants another Pi instance, they start Pi in a terminal and run `/telegram-connect`; Telegram-originated hidden auto-spawn and `/thread`-style process launch flows are intentionally absent. If Threaded Mode is unavailable, the message is processed normally through classic routing.
86
- - Telegram extension work should not hold Pi's core agent lifecycle hostage once Pi has produced the semantic result. For Telegram-originated turns, final Telegram delivery, attachment upload, and transport cleanup are extension-owned side effects; schedule them off the critical `agent_end` path when ordering can still be preserved, record failures as runtime events, and keep dispatch of the next Telegram turn behind the delivery task when needed to avoid interleaving previews/finals.
86
+ - Telegram extension work should not hold Pi's core agent lifecycle hostage once Pi has produced the semantic result. For Telegram-originated turns, final Telegram delivery, attachment upload, and transport cleanup are extension-owned side effects; schedule them off the critical `agent_end` path when ordering can still be preserved, record failures as runtime events, and keep dispatch of the next Telegram turn behind the delivery task when needed to avoid interleaving previews/finals. Public activity handlers run through isolated asynchronous per-handler queues; lifecycle hooks enqueue normalized events and never await consumer work.
87
87
  - Telegram runtime state should be treated as event-driven reconciliation of extension assumptions against observable Telegram signals, not as a full Telegram bot read-model and not as a reason to query Telegram on every action. Keep a local assumption model for bot identity/capabilities, pairing assumptions, thread support, known thread lifecycle, bound instance targets, reservations, and transport health. Invalidate and reconcile the relevant slice on meaningful events: startup/reload, lifecycle service messages, stale-send/API errors, setup/pairing changes, follower register/prune, explicit status/debug requests, and bounded low-frequency leader health ticks. `lib/sync.ts` owns sync slices, observation intake, invalidation triggers, status/debug freshness, and reconciliation scheduling; it must not promise complete bot-state mirroring because Bot API lacks a complete topic listing surface. `lib/thread-reconciler.ts` owns pure Threaded Mode lifecycle planning and should become the only policy authority for destructive thread cleanup decisions. `threads` owns current Telegram UI thread/tab binding primitives and thread-name helpers mapped to Bot API topic transport, `status` renders projections, and `index.ts` only wires ports. `tmp/telegram/logs.jsonl` is a session-local redacted runtime evidence stream for race debugging and resets on extension start / runtime scope changes; it is diagnostics only, not routing/provisioning authority. `state.json` should be an observable/debug snapshot aligned with `/telegram-status` (extension runtime, bot capabilities including `bot.lastSlot`, runtime role, live roster projection, reservations, diagnostics), not routing/provisioning authority. Because every process on one Telegram profile shares this file, only the active transport lock owner may persist it; followers read current state and gain write authority only after promotion. Status-only writes must refresh disk-backed bindings before serialization so a stale loaded snapshot cannot erase newer leader state. Live bus/runtime state is authoritative; file reservations and fresh capability observations may be startup hints/collision guards only; stale snapshots must re-probe before suppressing bus/topic behavior. Do not persist stale/offline/failed thread target history as source of truth.
88
88
  - Thread bindings are bidirectional. From the Pi-instance side, an instance knows and preserves its target, slot, thread name, lifecycle state, and direct-delivery defaults. From the Telegram-client/bot side, the bridge observes thread creation/close/reopen/stale-send signals and reconciles them into instance binding state. Lifecycle transitions that affect operator understanding should be Telegram-visible when a live transport remains: every successful follower registration/re-registration gets a compact connected notice in the assigned thread, while heartbeat pruning stays silent because it is only liveness suspicion. These notices should use the instance thread name when known and fall back to the slot letter only while the thread is still unnamed. New unknown threads (owner writing in `All`) are preserved until the user explicitly chooses a reroute/restore/new-instance action that makes cleanup eligible; any destructive cleanup must go through `thread-reconciler`. Unknown `forum_topic_created` service events are observations, not destructive cleanup proof.
89
89
  - The multi-instance bus uses private bot DMs with Telegram private-chat Threaded Mode enabled for the bot. No supergroup, group, or forum configuration is needed; the thread chat is always the private chat with the paired owner. Threaded capability checks must use bot/private-chat evidence such as `getMe.has_topics_enabled`, incoming `message_thread_id`, and topic operation success/failure; do not use group chat metadata as the control-plane truth for Telegram private-chat Threaded Mode.
@@ -159,9 +159,9 @@ The canonical detailed ownership map lives in [`docs/architecture.md`](./docs/ar
159
159
 
160
160
  - Scheduling and lifecycle: `queue`, `runtime`, `lifecycle`, `locks`
161
161
  - Telegram transport, polling, bus, sync, and inbound flow: `api`, `polling`, `bus`, `bus-api`, `bus-leader`, `bus-follower`, `ownership`, `target`, `sync`, `thread-reconciler`, `threads`, `updates`, `routing`, `media`, `turns`, `inbound`, `config`, `setup`
162
- - Response surfaces: `preview`, `replies`, `rendering`, `keyboard`, `outbound-markup`, `outbound-attachments`, `outbound`, `voice`, `status`
162
+ - Response surfaces: `preview`, `replies`, `rendering`, `keyboard`, `delivery`, `activity`, `outbound-markup`, `outbound-attachments`, `outbound`, `voice`, `status`
163
163
  - Controls and application menu UI: `commands`, `menu`, `menu-model`, `menu-thinking`, `menu-status`, `menu-queue`, `model`, `prompts`
164
- - Extension platform: `sections` owns section registry, token mapping, callback dispatch, context building, and its globalThis bridge; `voice` owns the voice-provider registry and its globalThis bridge
164
+ - Extension platform: `sections` owns section registry, token mapping, callback dispatch, context building, and its globalThis bridge; `delivery` owns target-aware extension views, logical handles, target policy, ordering, lifecycle fencing, and its globalThis runtime membrane; `activity` owns normalized lifecycle registration, activity/source identity, non-blocking dispatch, delivery contexts, and its globalThis runtime membrane; `voice` owns the voice-provider registry and its globalThis bridge
165
165
  - Pi SDK boundary: `pi` owns direct pi imports and bound extension API ports; `bindings` owns pi-facing command/tool/lifecycle registration wiring extracted from the entrypoint
166
166
 
167
167
  ## 6.5 Entrypoint And Import Boundaries
@@ -203,9 +203,11 @@ The canonical detailed ownership map lives in [`docs/architecture.md`](./docs/ar
203
203
  - Inline UI labels and dialogs follow [`docs/ui-style.md`](./docs/ui-style.md): action buttons use emoji plus capitalized action text, state & navigation buttons show state and lead to a submenu, first-level submenus start with `⬆️ Main menu` while deeper submenus start with `⬆️ Back`, boolean toggles use Capitalized horizontal `On`/`Off` with green active `On`, yellow active `Off`, and black inactive dots, tabs use capitalized labels with purple default-state and yellow elevated-state active dots and black inactive dots, vertical option lists mark only the current value green, and confirmation dialogs use a single bold text-only question with emoji on buttons only.
204
204
  - Inbound text/media may be transformed through configured `inboundHandlers` before queueing; legacy `attachmentHandlers` are deprecated compatibility aliases appended after `inboundHandlers`; active-turn outbound files must flow through `telegram_attach`, while explicit local/TUI Telegram sends use `telegram_attach` for files or `telegram_message` for text/buttons
205
205
  - Long Telegram text split recovery belongs to `text-groups`: keep it conservative, short-debounced, same chat/user/message-id contiguous, and gated by near-limit human text so normal rapid follow-ups and slash commands stay separate
206
- - Public API boundaries live in [`docs/public-api.md`](./docs/public-api.md): companion extensions must use public API domain subpaths such as `@llblab/pi-telegram/sections`, `/voice`, `/inbound`, `/outbound`, and `/updates`; `0.12.0` intentionally removes the published `@llblab/pi-telegram/lib/*.ts` compatibility wildcard
207
- - Public handler API matrix: low-level buses use `registerTelegramUpdateHandler(handler)`, `registerTelegramInboundHandler(kind, handler)`, and `registerTelegramOutboundHandler(kind, handler)` without ids; high-level surfaces use stable identity (`registerTelegramSection({ id, ... })`, `registerTelegramVoiceTranscriptionProvider(provider, { id })`, and `registerTelegramVoiceSynthesisProvider(provider, { id })`). Inbound handlers and command-backed outbound handlers use command templates as the standard config contract; built-in outbound buttons use inline keyboards plus callback routing because no polling command execution is needed
206
+ - Public API boundaries live in [`docs/public-api.md`](./docs/public-api.md): companion extensions must use public API domain subpaths such as `@llblab/pi-telegram/sections`, `/delivery`, `/voice`, `/inbound`, `/outbound`, and `/updates`; `0.12.0` intentionally removes the published `@llblab/pi-telegram/lib/*.ts` compatibility wildcard
207
+ - `Companion` names the extension consumer/product relationship, not a core domain responsibility. Source headers, Zones, module names, public API names, and domain docs should use the owned capability (`delivery`, `activity`, `sections`, `voice`, or extension interop); reserve companion terminology for the external extension that composes those APIs.
208
+ - Public handler API matrix: low-level buses use `registerTelegramUpdateHandler(handler)`, `registerTelegramInboundHandler(kind, handler)`, and `registerTelegramOutboundHandler(kind, handler)` without ids; high-level surfaces use stable identity (`registerTelegramSection({ id, ... })`, `registerTelegramVoiceTranscriptionProvider(provider, { id })`, and `registerTelegramVoiceSynthesisProvider(provider, { id })`). Imperative `/delivery` operations resolve the current runtime on every call and use generation-bound logical handles rather than registration ids or captured Pi contexts. Inbound handlers and command-backed outbound handlers use command templates as the standard config contract; built-in outbound buttons use inline keyboards plus callback routing because no polling command execution is needed
208
209
  - Telegram prompt-template commands are discovered from Pi slash commands with `source: "prompt"`; Pi template names are mapped to Bot API-compatible aliases (`fix-tests` → `/fix_tests`), aliases that conflict with built-in bridge commands or hidden shortcuts are not displayed, prompt-template aliases stay out of the Telegram bot command menu, and the bridge expands template files before queueing because extension-originated `sendUserMessage()` bypasses Pi's interactive template expansion
210
+ - Target-aware companion delivery follows [`docs/delivery.md`](./docs/delivery.md): `active-turn`, `instance`, `aggregate`, and explicit target scopes resolve through current profile/role state; followers may target only their assigned thread or aggregate surface, leaders may target current live bindings, logical handles reconcile chunked send/edit/delete, and reload/session replacement invalidates old generations
209
211
  - Unknown callback data not owned by pi-telegram prefixes (`compact:`, `tgbtn:`, `menu:`, `model:`, `thinking:`, `status:`, `queue:`, `section:`, `settings:`) may be forwarded as `[callback] <data>` after built-in handlers decline it; update-handler extensions should follow `docs/callback-namespaces.md` and must not poll the same bot independently
210
212
  - Command templates stay compact and shell-free: no `command` field, no shell execution, inline defaults are allowed as `{name=default}`, `template` may be a string or an ordered composition array, only `args`/`defaults` inherit into leaves, top-level `timeout` wraps composed sequences, stdout pipes to the next step's stdin by default, and multi-step work should use `template: [...]` rather than provider-specific fields; `pipe` is only a legacy local alias
211
213
  - Command-template documentation examples should use portable executable placeholders such as `/path/to/stt` and `/path/to/tts`, not host-local skill paths or machine-specific install locations
package/BACKLOG.md CHANGED
@@ -1,50 +1,6 @@
1
1
  # Project Backlog
2
2
 
3
- _This backlog tracks only open release-relevant work: live promoted-follower verification, evidence-gated Telegram client/runtime follow-ups, and upstream Pi API blockers. Completed validation evidence belongs in `CHANGELOG.md`, not in this queue._
4
-
5
- ## P1 — Guest Media Live Follow-Ups
6
-
7
- Context: 0.20.5 shipped deterministic Guest Mode file/audio delivery coverage. Post-release private-DM smoke confirmed that one local document reaches the remote conversation through `answerGuestQuery`; remaining checks validate Telegram client behavior rather than gate the implemented transport.
8
-
9
- Open work:
10
-
11
- - [x] Confirm one local document in a private Guest Mode DM after extension reload.
12
- - [x] Confirm one synthesized paired-comment voice result in a private Guest Mode DM; Telegram delivered it to the correctly attributed remote peer without visible fallback text.
13
- - [ ] Confirm one local document and one synthesized voice/audio result in group Guest Mode.
14
- - [ ] Record a focused client/API caveat only if live behavior contradicts the one-result and staging contracts.
15
-
16
- Done when: private and group Guest Mode each have direct document and voice/audio delivery evidence, or a confirmed Telegram limitation is documented.
17
-
18
- ## P1 — Compaction Status Ownership And Native Activity
19
-
20
- Context: Pi already renders its own compaction lifecycle, while pi-telegram currently overrides its terminal status row with `compacting` whenever the shared compaction flag is set. This duplicates Pi-owned state and hides the distinction between Telegram-owned activity and unrelated automatic/session compaction. Manual `/compact` already calls the typing-loop port and automatic compaction starts typing only when an active Telegram turn exists, so the reported absence of Telegram `…typing` needs transport-level and live verification rather than an assumed rewrite.
21
-
22
- Planned work:
23
-
24
- - [x] Remove `compacting` as a pi-telegram terminal status label while retaining the internal compaction flag for queue/dispatch safety and explicit diagnostics.
25
- - [x] Track compaction origin for status projection: confirmed Telegram `/compact` and auto-compaction inside a Telegram-owned turn render normal `Active`; local/autonomous/background compaction keeps the stable `connected`, `leader`, or `follower` role.
26
- - [x] Define and verify the native activity matrix: Telegram-owned compaction targets the invoking/active thread plus `All`; non-Telegram compaction uses the connected instance target plus `All` without changing terminal role semantics.
27
- - [x] Trace manual confirmation, `session_before_compact`, `session_compact`, completion, error, timeout, abort, and shutdown ordering to ensure one keyed typing loop remains active for the whole compaction window and always stops.
28
- - [x] Add transport-level regressions that assert actual `sendChatAction(typing)` targets and keepalive lifecycle, not only invocation of a mocked `startTypingLoop` callback.
29
- - [x] Replace status tests that currently require `compacting` with Telegram-owned `Active` and non-Telegram stable-role cases; preserve `/telegram-status` compaction diagnostics where operationally useful.
30
- - [ ] Capture live evidence for manual Telegram compaction, auto-compaction during a Telegram turn, and non-Telegram auto-compaction before finalizing the activity contract.
31
-
32
- Done when: Pi remains the only terminal owner of the `compacting` label, pi-telegram status reflects Telegram ownership rather than generic compaction, and Telegram native `…typing` remains visible and correctly targeted throughout every confirmed compaction class without leaking afterward.
33
-
34
- ## P1 — Leader Endpoint Loss Recovery
35
-
36
- Context: live evidence showed a process retaining a fresh transport lock and active polling while its Threaded Mode Unix socket path was absent. The likely trigger was external removal of the shared Telegram temp directory while the owner process remained alive. The local server keeps listening on the unlinked Unix socket but `start()` treats its in-memory server handle as sufficient, leader health checks only Bot API transport, and a new instance therefore exhausts follower-registration retries with `ENOENT`. This is a real diagnosable recovery gap, but not yet evidence for a broad readiness protocol or automatic takeover; force-acquiring while the old owner may still run `getUpdates` would risk split-brain.
37
-
38
- Planned work:
39
-
40
- - [x] Reproduce deterministically by unlinking only the active Unix leader socket while its process, polling runtime, and in-memory server remain live. Native Windows named pipes have no equivalent filesystem path to unlink, so recovery remains Unix-specific unless separate named-pipe evidence appears.
41
- - [x] Let the owning Threaded Mode runtime detect an externally missing Unix endpoint during its existing health/prune cadence and restart only the local bus server without changing lock ownership, leader epoch, polling, or thread bindings.
42
- - [x] Make initial follower registration report `live owner / unreachable bus endpoint` after bounded retries, with direct operator guidance; do not add automatic or force takeover without separate evidence that the old owner cannot still poll.
43
- - [x] Keep intentional classic ownership unchanged because classic mode does not require a bus endpoint.
44
- - [x] Add focused regressions for Unix endpoint unlink/rebind, bounded follower diagnosis, leader reload overlap, and no duplicate `getUpdates` ownership; add Windows coverage only for behavior the named-pipe transport can reproduce.
45
- - [ ] Capture live recovery evidence without deleting lock/state or creating a replacement Telegram thread.
46
-
47
- Done when: the confirmed endpoint-loss scenario either self-recovers under the existing owner or produces precise safe remediation, while classic mode and single-owner polling remain unchanged.
3
+ _This backlog tracks only open release-relevant work: live runtime verification, evidence-gated Telegram client follow-ups, and upstream Pi API blockers. Completed outcomes and validation evidence belong in `CHANGELOG.md`, not in this queue._
48
4
 
49
5
  ## P1 — Promoted Follower Reload Evidence
50
6
 
@@ -98,6 +54,8 @@ Blocked: upstream Pi core API. Issue: https://github.com/earendil-works/pi/issue
98
54
 
99
55
  Context: Threaded Mode manual followers are separate visible Pi processes. Same-thread `/new` is a different feature: replacing the current Pi session inside the same Telegram thread. Extension-only hacks are rejected because they would desynchronize Pi lifecycle/TUI semantics.
100
56
 
57
+ Current upstream evidence: Pi 0.80.6 safely exposes `ctx.newSession()` to registered extension commands through `ExtensionCommandContext`, including fresh-context rebinding after replacement. Telegram update and callback handlers still receive only `ExtensionContext`, and extension-origin `pi.sendUserMessage()` deliberately disables slash-command handling. The upstream maintainer described an async extension bridge as potentially possible after the current refactor, but no supported API exists yet.
58
+
101
59
  Required upstream shape:
102
60
 
103
61
  - `pi.newSession(...)` or `pi.requestSessionReplacement(...)` callable from trusted extension runtime code.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.1: Runtime And Session Semantics Hotfix
4
+
5
+ - `[Runtime Semantics]` Clarified that Telegram destinations follow running Pi instances and route prompts into each instance's currently active session rather than binding permanently to one session file or session identity. Impact: session replacement and Threaded Mode behavior now match the documented operator mental model without mischaracterizing the bridge as a remote terminal or session browser.
6
+ - `[Session Control]` Documented the exact mobile boundary: Telegram can compact the current session but cannot create, resume, fork, browse, or switch sessions until Pi exposes safe public extension APIs. Impact: operators can distinguish active-session continuation from unavailable session lifecycle/navigation control.
7
+ - `[Context Cost]` Documented that Telegram prompts are normal Pi model turns and inherit the active post-compaction session context just like TUI prompts; pi-telegram does not promise context isolation or cost proportional only to the new mobile message. Impact: token usage expectations no longer conflate transport metadata with model-context economics.
8
+ - `[Prompt Guidance]` Documented the current small transient system note plus on-demand `telegram_help` design and the historical risk from older releases that persisted large guidance suffixes in every user turn. Impact: operators investigating long-lived sessions can separate current behavior from legacy context growth already stored in old session history.
9
+ - `[Diagnostics Identity]` Distinguished Pi session JSONL from shared profile-scoped pi-telegram `logs*.jsonl`, and clarified that `/telegram-connect` never launches hidden Pi processes while explicitly launched long-lived instances remain subject to normal lock ownership. Impact: shared bridge diagnostics can no longer be mistaken for merged Pi session history or hidden process creation.
10
+
11
+ ## 0.21.0: Activity And Delivery Extension Platform
12
+
13
+ - `[Platform Documentation]` Completed 0.21 discoverability across the README, public API inventory/smoke checklist, Activity and Delivery contracts, architecture map, Sections reference, package contents, documented consumer policy examples, and the external demo-project reference. Removed the completed platform epic from `BACKLOG.md` while preserving deferred media/process boundaries and the concrete callback revisit trigger in docs. Impact: extension authors can find and validate the complete supported surface without `/lib` imports, and open-work context now contains only unfinished work.
14
+ - `[Platform Validation]` Added a full-runtime classic-mode regression proving public activity delivery reaches the assigned Telegram surface while `agent_start` remains independent from the consumer's pending Bot API send. Expanded scope coverage to distinguish active-turn, assigned-instance, and aggregate targets and verify autonomous activity can opt into aggregate delivery; existing leader/follower transport, authorization, stale-generation, ordering, chunk reconciliation, and cleanup regressions complete the platform matrix. Impact: the 0.21 extension contracts now carry focused domain, public-boundary, and runtime integration proof.
15
+ - `[Callback Boundary]` Confirmed that the issue #126 consumer shape needs no second managed callback registry: Sections already own interactive Settings namespacing, answers, edits, navigation, and cleanup, while delivered activity rows remain non-interactive and raw update handlers stay the low-level escape hatch. Impact: the platform avoids duplicate callback ownership and records the concrete trigger for revisiting the boundary.
16
+ - `[Activity API]` Added the public `@llblab/pi-telegram/activity` membrane and wired Pi input, agent, assistant text/reasoning, executed-tool, compaction, settlement, and shutdown lifecycle signals into evidence-based activity/source identities. Isolated asynchronous per-handler queues preserve semantic order, coalesce adjacent deltas, fence shutdown generations, report handler failures, and provide fresh target-aware delivery contexts without awaiting consumer work or duplicating bridge previews/finals. Impact: extension consumers can build lifecycle-adjacent Telegram surfaces without private Pi contexts, raw provider messages, or bridge transport ownership.
17
+ - `[Delivery API]` Added the public `@llblab/pi-telegram/delivery` membrane for ownership-gated operational views and chat actions across active-turn, current-instance, aggregate, and explicitly authorized targets. Logical handles reconcile chunked send/edit/delete operations, per-target queues preserve ordering, followers route through the leader transport, and reload/session replacement invalidates old generations without captured Pi contexts. Impact: companion extensions can now deliver lifecycle-adjacent Telegram UI through stable ports instead of importing bridge internals or owning another bot client.
18
+ - `[Public API]` Documented the complete capability inventory, intentional private boundaries, target policy, structured delivery failures, callback boundary decision, and deferred media/process controls; added public-boundary, classic/leader/follower policy, chunk reconciliation, ordering, and lifecycle regressions. Impact: extension authors can distinguish supported platform capabilities from deliberate safety boundaries and the completed 0.21 extension-platform surface.
19
+ - `[Pi Compatibility]` Set the Activity platform floor to Pi/core/AI `0.80.6`, registered the public typed `agent_settled` lifecycle event directly, and aligned Telegram model controls with Pi's `max` thinking level. Impact: activity identity now closes at a supported terminal boundary without unsafe event casts or silently merging unrelated runs on older Pi versions.
20
+ - `[Delivery Lifecycle]` Constructed a fresh Delivery runtime and generation for every session start, shut down displaced bindings, and fenced queued or multi-step in-flight work at every transport boundary. Impact: session replacement cannot revive old handles or let an old operation continue chunks, edits, deletes, or chat actions through a new session.
21
+ - `[Delivery Recovery]` Added recoverable partial handles to failed multi-chunk sends and edit growth, tracking every message id that remains visible after transport failure. Impact: callers can deterministically retry or clean up a partially materialized logical view instead of losing its only valid handle.
22
+ - `[Activity Compaction]` Added explicit abandonment for standalone compaction activities that never receive `session_compact`, triggered by the next run/compaction boundary or the bounded observer timeout, while preserving compaction inside an active agent identity and ignoring late completion. Impact: cancelled or failed compaction cannot leak its activity id or source into an unrelated run.
23
+ - `[Activity Diagnostics]` Reconciled the public contract with implemented diagnostics: handler failures carry redacted identity metadata through the bounded bridge recorder, while 0.21 intentionally exposes no queue phase/length/latency telemetry or drop policy beyond adjacent delta coalescing. Impact: consumers can reason from documented backpressure behavior without relying on diagnostics the runtime does not emit.
24
+ - `[Activity Lifecycle]` Recreated the Activity dispatcher with a fresh generation on every session start while stopping and clearing the retiring generation at shutdown, and bound handler contexts to their exact dispatcher generation and registration. Impact: same-process session replacement resumes normalized lifecycle delivery while blocked handlers from the retiring session fail closed instead of sending stale work through replacement Delivery.
25
+ - `[Guest Identity]` Kept private Guest Mode location identity anchored to the strongest remote-conversation evidence and excluded bot-authored replies from guest resolution, while retaining a non-bot replied peer as a final fallback when chat/caller identity is absent. Impact: replying to a Guest Mode bot response preserves `[telegram|guest:<remote-user>]` alongside `[reply|from:<bot>]` instead of replacing the conversation peer with the bot username.
26
+
3
27
  ## 0.20.6: Guest Attribution And Voice Action Hotfix
4
28
 
5
29
  - `[Voice Action Syntax]` Hidden `telegram_voice` actions now accept the intuitive paired form `<!-- telegram_voice ... -->...<!-- /telegram_voice -->` alongside inline, attribute-text, and single-comment multiline forms. The parser captures a non-empty multiline body and preserves language/rate attributes while leaving surrounding prose visible. Impact: agents can use an explicit closing tag without leaking the intended TTS payload as ordinary text.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  `pi-telegram` turns a private Telegram DM into a mobile operator surface for Pi. It accepts prompts, queues work, streams readable previews, delivers final replies and files, exposes safe controls, and lets companion extensions add Telegram-native capabilities without owning a second bot loop.
8
8
 
9
- It is a **runtime adapter**, not a remote terminal. Start or supervise work in the Pi TUI, then continue from Telegram while away from the keyboard. The bridge preserves Pi session semantics instead of pretending Telegram is a PTY, shell, or process launcher. That boundary is the product: Telegram gets safe runtime handles, not raw terminal power.
9
+ It is a **runtime adapter**, not a remote terminal. Start or supervise work in the Pi TUI, then continue from Telegram while away from the keyboard. Each Telegram destination follows a running Pi instance and sends prompts into that instance's currently active session; it is not permanently bound to one session file or session identity. The bridge preserves Pi session semantics instead of pretending Telegram is a PTY, shell, process launcher, or session browser. That boundary is the product: Telegram gets safe runtime handles, not raw terminal power.
10
10
 
11
11
  This repository is an actively maintained fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
12
12
 
@@ -24,6 +24,8 @@ From git:
24
24
  pi install git:github.com/llblab/pi-telegram
25
25
  ```
26
26
 
27
+ The 0.21 extension platform requires Pi `0.80.6` or newer. Its Activity API uses the public `agent_settled` lifecycle event to keep retries/continuations under one activity identity and release that identity only after the run fully settles.
28
+
27
29
  ## Quick Start
28
30
 
29
31
  ### 1. Create a Telegram bot
@@ -43,7 +45,7 @@ Run this inside Pi:
43
45
 
44
46
  Paste the bot token. If `~/.pi/agent/telegram.json` already contains a saved token, setup offers it as the default. If no saved token exists, setup can prefill from `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, or `TELEGRAM_KEY`. Named profiles are optional; the ordinary `/telegram-setup` and `/telegram-connect` flow keeps using the default profile. Use `/telegram-setup <name>` only when you want an additional bot profile. Cancelling or failing named-profile token validation leaves the currently active profile and polling runtime unchanged; setup reports the profile as saved and connected only after polling startup succeeds.
45
47
 
46
- ### 3. Connect this Pi session
48
+ ### 3. Connect this Pi instance and its active session
47
49
 
48
50
  ```bash
49
51
  /telegram-connect
@@ -75,8 +77,8 @@ The first Telegram user to message the bot becomes the allowed owner. Other user
75
77
 
76
78
  | Lens | What `pi-telegram` owns |
77
79
  | --- | --- |
78
- | Operator companion | A phone-width control surface for a live Pi session |
79
- | Runtime adapter | Telegram turns mapped into Pi lifecycle, queueing, previews, final replies, and artifacts |
80
+ | Operator companion | A phone-width control surface for the active session of a running Pi instance |
81
+ | Runtime adapter | Telegram targets mapped to Pi instances, then into each instance's current session lifecycle, queueing, previews, final replies, and artifacts |
80
82
  | Telegram UI harness | Menus, settings, callbacks, Rich Markdown, drafts, active status, buttons, voice, and files |
81
83
  | Multi-instance organism | One leader plus explicit visible followers routed through Telegram private-chat threads |
82
84
  | Extension platform | Commands, sections, status rows, update handlers, inbound/outbound handlers, and voice providers |
@@ -192,8 +194,8 @@ Classic private DM mode is the base product mode. When Telegram private-chat Thr
192
194
 
193
195
  | Mode | Best for | Runtime shape |
194
196
  | --- | --- | --- |
195
- | Classic DM | One live Pi session controlled from one private bot chat | One polling owner, one queue/runtime surface |
196
- | Threaded Mode | Several visible Pi terminals sharing one bot | One leader owns transport; followers route through named private-chat threads |
197
+ | Classic DM | One running Pi instance and its active session controlled from one private bot chat | One polling owner, one queue/runtime surface |
198
+ | Threaded Mode | Several visible Pi instances sharing one bot | One leader owns transport; each named private-chat thread follows its assigned instance and current session |
197
199
 
198
200
  ## Environment Configuration
199
201
 
@@ -217,12 +219,14 @@ Companion extensions can integrate with Telegram without owning polling or trans
217
219
  - Register Telegram slash commands.
218
220
  - Add menu sections and settings surfaces.
219
221
  - Add compact status rows.
222
+ - Deliver target-aware operational views and chat actions from companion code.
223
+ - Observe normalized assistant, reasoning, tool, compaction, and settlement activity without blocking Pi.
220
224
  - Handle update/callback namespaces.
221
225
  - Provide inbound preprocessing handlers.
222
226
  - Provide outbound voice synthesis.
223
227
  - Use direct delivery helpers for explicit local/TUI sends.
224
228
 
225
- Stable public entrypoints are documented in [Public API](./docs/public-api.md), [Extension Sections](./docs/sections.md), [Inbound Handlers](./docs/inbound.md), [Outbound Handlers](./docs/outbound.md), [Updates](./docs/updates.md), and [Voice Integration](./docs/voice.md).
229
+ Stable public entrypoints are documented in [Public API](./docs/public-api.md), [Telegram Delivery API](./docs/delivery.md), [Telegram Activity API](./docs/activity.md), [Extension Sections](./docs/sections.md), [Inbound Handlers](./docs/inbound.md), [Outbound Handlers](./docs/outbound.md), [Updates](./docs/updates.md), and [Voice Integration](./docs/voice.md).
226
230
 
227
231
  ## Safety Boundaries
228
232
 
@@ -235,12 +239,16 @@ Stable public entrypoints are documented in [Public API](./docs/public-api.md),
235
239
  - Replace Pi session lifecycle without an official Pi API.
236
240
  - Let non-owner Telegram users control the bridge.
237
241
 
238
- Telegram is a companion surface around a live Pi runtime, not a second runtime.
242
+ Telegram is a companion surface around a live Pi runtime, not a second runtime. It can compact the current session, but it cannot create, resume, fork, browse, or switch sessions until Pi exposes safe public extension APIs for those operations.
243
+
244
+ A Telegram prompt is a normal model turn in the active Pi session and therefore inherits that session's active post-compaction context; the bridge does not make token cost proportional only to the new mobile message. Current releases keep per-turn guidance small and transient, with detailed bridge instructions available on demand through `telegram_help` instead of persisted in every user turn. Pi session JSONL contains model history; profile-scoped pi-telegram `logs*.jsonl` contains redacted operational events and is never model context.
239
245
 
240
246
  ## Documentation Map
241
247
 
242
248
  - [Architecture](./docs/architecture.md) — runtime, domains, queue, transport, and Threaded Mode overview.
243
249
  - [Public API](./docs/public-api.md) — package entrypoints and stable companion-extension contracts.
250
+ - [Telegram Delivery API](./docs/delivery.md) — target-aware operational views, logical message handles, and lifecycle-safe transport.
251
+ - [Telegram Activity API](./docs/activity.md) — normalized lifecycle events, source identity, non-blocking delivery contexts, and consumer policy examples.
244
252
  - [Inbound Handlers](./docs/inbound.md) — Telegram-to-Pi preprocessing pipelines.
245
253
  - [Outbound Handlers](./docs/outbound.md) — final text/voice/file transformation and delivery.
246
254
  - [Voice Integration](./docs/voice.md) — STT/TTS provider model and reply policies.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Public Telegram activity API
3
+ * Zones: package boundary, pi agent lifecycle, extension interop
4
+ * Exposes normalized non-blocking activity registration while keeping lifecycle wiring and dispatch internals package-private
5
+ */
6
+
7
+ export {
8
+ registerTelegramActivityHandler,
9
+ type TelegramActivityContext,
10
+ type TelegramActivityEnvelope,
11
+ type TelegramActivityEvent,
12
+ type TelegramActivityHandlerRegistration,
13
+ type TelegramActivitySource,
14
+ type TelegramActivityTarget,
15
+ } from "../lib/activity.ts";
package/api/commands.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram commands API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes the stable Telegram slash-command registration surface while keeping registry internals package-private
5
5
  */
6
6
 
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Public Telegram delivery API
3
+ * Zones: package boundary, telegram delivery, extension interop
4
+ * Exposes target-aware operational view delivery while keeping transport and runtime binding internals package-private
5
+ */
6
+
7
+ export {
8
+ deleteTelegramView,
9
+ editTelegramView,
10
+ sendTelegramChatAction,
11
+ sendTelegramView,
12
+ type SendTelegramViewOptions,
13
+ type TelegramDeliveryChatAction,
14
+ type TelegramDeliveryFailureReason,
15
+ type TelegramDeliveryHandle,
16
+ type TelegramDeliveryParseMode,
17
+ type TelegramDeliveryResult,
18
+ type TelegramDeliveryScope,
19
+ type TelegramDeliveryTarget,
20
+ type TelegramDeliveryView,
21
+ } from "../lib/delivery.ts";
package/api/inbound.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram inbound API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes the stable programmatic inbound handler surface while keeping handler runtime internals package-private
5
5
  */
6
6
 
package/api/keyboard.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram keyboard API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes shared inline-keyboard structural types without runtime exports
5
5
  */
6
6
 
package/api/outbound.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram outbound API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes stable outbound handler and diagnostics surfaces while keeping delivery internals package-private
5
5
  */
6
6
 
package/api/sections.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram sections API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes the stable managed Telegram menu-section surface while keeping registry internals package-private
5
5
  */
6
6
 
package/api/status.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Public Telegram status API
3
- * Zones: package boundary, companion extension interop
4
- * Exposes compact status-menu line registration for companion extensions while keeping status rendering internals package-private
3
+ * Zones: package boundary, extension interop
4
+ * Exposes compact status-menu line registration for extension consumers while keeping status rendering internals package-private
5
5
  */
6
6
 
7
7
  export {
package/api/updates.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram updates API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes the stable raw-update handler surface while keeping update routing internals package-private
5
5
  */
6
6
 
package/api/voice.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Public Telegram voice API
3
- * Zones: package boundary, companion extension interop
3
+ * Zones: package boundary, extension interop
4
4
  * Exposes the stable STT/TTS provider surface and voice policy helpers
5
5
  */
6
6
 
package/docs/README.md CHANGED
@@ -8,6 +8,8 @@ Living index of project documentation in `/docs`.
8
8
 
9
9
  - [architecture.md](./architecture.md) — Overview of the Telegram bridge runtime, optional profile isolation, queueing model, native Rich Markdown delivery, UI/compat rendering, and interactive controls
10
10
  - [public-api.md](./public-api.md) — Stable public API map: package entrypoints, commands, config, assistant markup, extension APIs, smoke examples, and compatibility boundaries
11
+ - [delivery.md](./delivery.md) — Target-aware companion delivery contract for operational views, logical message handles, target scopes, lifecycle fencing, and leader/follower transport
12
+ - [activity.md](./activity.md) — Normalized Pi lifecycle API for extension-owned reasoning, intermediate prose, tool activity, source identity, delivery contexts, and consumer policy examples
11
13
  - [../.agents/skills/telegram-bot/SKILL.md](../.agents/skills/telegram-bot/SKILL.md) — Agent-facing Telegram Bot API lookup skill backed by a local full Bot API reference
12
14
  - [../.agents/skills/domain-dag/SKILL.md](../.agents/skills/domain-dag/SKILL.md) — Project-local Domain DAG architecture skill and validator guidance
13
15
  - [command-templates.md](./command-templates.md) — Portable command-template standard core