@llblab/pi-telegram 0.9.0 β†’ 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.1: Model Detail Hotfix
4
+
5
+ - `[Model Menu]` Detail-mode activation now preserves scoped `thinkingLevel` by resolving the selected scoped entry before falling back to the unscoped model list. Impact: scoped model shortcuts opened through the detail submenu keep their reasoning/thinking level.
6
+ - `[Model Menu]` Activating an already active model from the detail submenu now still runs the refresh path that applies scoped thinking changes while returning to the model list. Impact: tapping Active can still correct the thinking level instead of becoming a no-op.
7
+ - `[Proactive Push]` Removed the unused proactive reply-target store and always sends proactive local-result pushes without `reply_to_message_id`. Impact: the runtime no longer carries dead state for a target-capture behavior that does not exist yet.
8
+ - `[Queue Reactions]` Added `πŸ”₯` as a priority reaction and `πŸ—‘` as a queue-removal reaction. Impact: the intuitive fire/removal gestures now work alongside the existing reaction controls.
9
+ - `[Docs]` Updated the status-bar example to match the compact active/queued display.
10
+ - `[Package]` Bumped package metadata to `0.9.1` and kept the lockfile in sync.
11
+
3
12
  ## 0.9.0: Hidden Settings And Proactive Push
4
13
 
5
14
  - `[Settings Menu]` Added hidden Telegram `/settings` with a proactive push checkbox detail submenu plus `/telegram-settings` in the terminal. Impact: operators can see green/black binary flag state, use green/black/yellow On/Off checkbox controls from Telegram, and toggle the same proactive push flag locally without adding a visible bot-command entry.
package/README.md CHANGED
@@ -110,8 +110,8 @@ Run these inside Ο€, not Telegram:
110
110
 
111
111
  - If you send more Telegram messages while Ο€ is busy, they enter the default prompt queue and are processed in order.
112
112
  - Very long text messages that Telegram appears to split automatically are coalesced through a short conservative debounce and forwarded to Ο€ as one prompt when the first chunk is near Telegram's text limit, currently using a 3600-character threshold. Commands, bot messages, media groups, and normal short follow-ups are not coalesced.
113
- - `πŸ‘`, `⚑️`, `❀️`, and `πŸ•Š` move a waiting prompt into the priority prompt queue, behind control actions but ahead of default prompts. Removing the last priority reaction sends it back to its normal queue position, and adding a priority reaction again gives it a fresh priority position.
114
- - `πŸ‘Ž`, `πŸ‘»`, `πŸ’”`, and `πŸ’©` remove 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 removal reactions.
113
+ - `πŸ‘`, `⚑️`, `❀️`, `πŸ•Š`, and `πŸ”₯` move a waiting prompt into the priority prompt queue, behind control actions but ahead of default prompts. Removing the last priority reaction sends it back to its normal queue position, and adding a priority reaction again gives it a fresh priority position.
114
+ - `πŸ‘Ž`, `πŸ‘»`, `πŸ’”`, `πŸ’©`, and `πŸ—‘` remove 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 removal reactions.
115
115
  - Reactions apply to any waiting Telegram turn, including text, voice, files, images, and media groups. For media groups, a reaction on any message in the group applies to the whole queued turn.
116
116
  - 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.
117
117
  - Telegram replies to earlier text or caption messages are forwarded as `[reply]` context for normal prompts, while slash commands still parse from the new message text only.
@@ -237,7 +237,7 @@ Rich previews are sent through editable messages because Telegram drafts are tex
237
237
  The Ο€ status bar shows the current bridge state plus queued Telegram turns as compact previews. Busy labels distinguish states such as `active`, `dispatching`, `queued`, `tool running`, `model`, and `compacting`. Telegram prompt guidance asks agents to keep tables, dense list items, and compact text blocks within about 37 visible cells when possible so mobile replies stay readable.
238
238
 
239
239
  ```text
240
- telegram queued +3: [⚑ write a shell script…, summarize this image…, πŸ“Ž 2 attachments]
240
+ telegram active +3
241
241
  ```
242
242
 
243
243
  ## Notes
@@ -23,24 +23,24 @@ Naming rule: because the repository already scopes this codebase to Telegram, ex
23
23
 
24
24
  Current runtime areas use these ownership boundaries:
25
25
 
26
- | Domain | Owns |
27
- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
- | `index.ts` | Single composition root for live Ο€/Telegram ports, session state, API-bound transport adapters, and status updates |
29
- | `api` | Bot API transport shapes/helpers, retries, file download, temp-dir lifecycle, inbound limits, chat actions, lazy bot-token clients, runtime error recording |
30
- | `config` / `setup` | Persisted bot/session pairing state, authorization, first-user pairing, token prompting, env fallback, validation, config persistence |
31
- | `locks` / `polling` | Singleton `locks.json` ownership, takeover/restart semantics, long-poll controller state, update offset persistence, poll-loop runtime wiring |
32
- | `updates` / `routing` | Update classification/execution planning, paired authorization, reactions, edits, callbacks, and inbound route composition |
33
- | `media` / `text-groups` / `turns` / `inbound-handlers` | Text/media extraction, media-group debounce, long-text split coalescing, inbound downloads, inbound text/media handler execution, turn building/editing, image reads, legacy `attachmentHandlers` compatibility |
34
- | `queue` | Queue item contracts, lane admission/order, stores, mutations, dispatch readiness/runtime, prompt/control enqueueing, session and agent/tool lifecycle sequencing |
35
- | `runtime` | Session-local coordination primitives: counters, lifecycle flags, setup guard, abort handler, typing-loop timers, prompt-dispatch flags, agent-end reset binding |
26
+ | Domain | Owns |
27
+ | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | `index.ts` | Single composition root for live Ο€/Telegram ports, session state, API-bound transport adapters, and status updates |
29
+ | `api` | Bot API transport shapes/helpers, retries, file download, temp-dir lifecycle, inbound limits, chat actions, lazy bot-token clients, runtime error recording |
30
+ | `config` / `setup` | Persisted bot/session pairing state, authorization, first-user pairing, token prompting, env fallback, validation, config persistence |
31
+ | `locks` / `polling` | Singleton `locks.json` ownership, takeover/restart semantics, long-poll controller state, update offset persistence, poll-loop runtime wiring |
32
+ | `updates` / `routing` | Update classification/execution planning, paired authorization, reactions, edits, callbacks, and inbound route composition |
33
+ | `media` / `text-groups` / `turns` / `inbound-handlers` | Text/media extraction, media-group debounce, long-text split coalescing, inbound downloads, inbound text/media handler execution, turn building/editing, image reads, legacy `attachmentHandlers` compatibility |
34
+ | `queue` | Queue item contracts, lane admission/order, stores, mutations, dispatch readiness/runtime, prompt/control enqueueing, session and agent/tool lifecycle sequencing |
35
+ | `runtime` | Session-local coordination primitives: counters, lifecycle flags, setup guard, abort handler, typing-loop timers, prompt-dispatch flags, agent-end reset binding |
36
36
  | `model` / `menu-model` / `menu-thinking` / `menu-status` / `menu` / `menu-queue` / `menu-settings` / `commands` | Model identity/thinking levels, scoped model resolution, in-flight switching, model-menu UI, thinking-menu UI, status-menu UI, inline application callback composition, queue-menu UI, hidden settings UI, slash commands, bot command registration |
37
- | `keyboard` | Shared Telegram inline-keyboard reply-markup structure; feature domains own callback semantics and button construction |
38
- | `preview` / `replies` / `rendering` | Preview lifecycle/transports, final reply delivery and reply parameters, Telegram HTML Markdown rendering, chunking, stable-preview snapshots |
39
- | `outbound-handlers` | Outbound text transformation, assistant-authored outbound comments, generated reply artifacts, inline-keyboard callbacks, and post-`agent_end` outbound action delivery |
40
- | `outbound-attachments` | `telegram_attach` registration, outbound attachment queueing, stat/limit checks, photo/document delivery classification |
41
- | `status` | Status-bar/status-message rendering, queue-lane status views, redacted runtime event ring, grouped Ο€ diagnostics |
42
- | `lifecycle` / `prompts` / `prompt-templates` / `pi` | Ο€ hook registration, Telegram-specific before-agent prompt injection, Ο€ prompt-template discovery/expansion, centralized direct pi SDK imports and context adapters |
43
- | `command-templates` | Portable shell-free command-template standard helpers, composition expansion, placeholder substitution, and executable resolution |
37
+ | `keyboard` | Shared Telegram inline-keyboard reply-markup structure; feature domains own callback semantics and button construction |
38
+ | `preview` / `replies` / `rendering` | Preview lifecycle/transports, final reply delivery and reply parameters, Telegram HTML Markdown rendering, chunking, stable-preview snapshots |
39
+ | `outbound-handlers` | Outbound text transformation, assistant-authored outbound comments, generated reply artifacts, inline-keyboard callbacks, and post-`agent_end` outbound action delivery |
40
+ | `outbound-attachments` | `telegram_attach` registration, outbound attachment queueing, stat/limit checks, photo/document delivery classification |
41
+ | `status` | Status-bar/status-message rendering, queue-lane status views, redacted runtime event ring, grouped Ο€ diagnostics |
42
+ | `lifecycle` / `prompts` / `prompt-templates` / `pi` | Ο€ hook registration, Telegram-specific before-agent prompt injection, Ο€ prompt-template discovery/expansion, centralized direct pi SDK imports and context adapters |
43
+ | `command-templates` | Portable shell-free command-template standard helpers, composition expansion, placeholder substitution, and executable resolution |
44
44
 
45
45
  Boundary invariants:
46
46
 
@@ -95,13 +95,13 @@ Queued items now use two explicit dimensions:
95
95
 
96
96
  Admission contract:
97
97
 
98
- | Admission | Examples | Queue shape | Dispatch rank |
99
- | --------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------- | ------------- |
100
- | Immediate execution | `/compact`, `/queue`, `/stop`, `/help`, `/start` | Does not enter the Telegram queue; `/help` opens the same menu as `/start`; `/stop` also clears queued items | N/A |
101
- | Queued prompt command | `/continue`, `/template_name args` | `/continue` enqueues a Telegram-owned `continue` prompt; prompt-template commands expand the matching Ο€ template before entering the normal prompt queue | priority for `/continue`, otherwise default |
102
- | Control queue | Model-switch continuation turns and future deferred controls | `queueLane: control`; accepts control items and continuation prompts | 0 |
103
- | Priority prompt queue | A waiting prompt promoted by `πŸ‘`, `⚑️`, `❀️`, or `πŸ•Š` | `kind: prompt`, `queueLane: priority` | 1 |
104
- | Default prompt queue | Normal Telegram text/media turns | `kind: prompt`, `queueLane: default` | 2 |
98
+ | Admission | Examples | Queue shape | Dispatch rank |
99
+ | --------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
100
+ | Immediate execution | `/compact`, `/queue`, `/stop`, `/help`, `/start` | Does not enter the Telegram queue; `/help` opens the same menu as `/start`; `/stop` also clears queued items | N/A |
101
+ | Queued prompt command | `/continue`, `/template_name args` | `/continue` enqueues a Telegram-owned `continue` prompt; prompt-template commands expand the matching Ο€ template before entering the normal prompt queue | priority for `/continue`, otherwise default |
102
+ | Control queue | Model-switch continuation turns and future deferred controls | `queueLane: control`; accepts control items and continuation prompts | 0 |
103
+ | Priority prompt queue | A waiting prompt promoted by `πŸ‘`, `⚑️`, `❀️`, `πŸ•Š`, or `πŸ”₯` | `kind: prompt`, `queueLane: priority` | 1 |
104
+ | Default prompt queue | Normal Telegram text/media turns | `kind: prompt`, `queueLane: default` | 2 |
105
105
 
106
106
  The command action itself carries its execution mode, and the queue domain exposes lane contracts for admission mode, dispatch rank, and allowed item kinds. Queue append and planning paths validate lane admission so a malformed control/default or other invalid lane pairing fails predictably instead of silently changing priority. This lets synthetic control actions and Telegram prompts share one stable ordering model while still rendering distinctly in status output. In the Ο€ status bar, busy labels distinguish `active`, `dispatching`, `queued`, `tool running`, `model`, and `compacting`; priority prompts and priority control items are marked with `⚑`. If a queue mutation removes the last waiting item while Telegram-owned work still has running tools, the status remains yellow `active` instead of degrading to green `connected`.
107
107
 
@@ -181,7 +181,7 @@ Current operator controls include:
181
181
  - `/stop` for aborting the active Telegram-owned run and clearing waiting Telegram queue items
182
182
  - `/telegram-status` for Ο€-side diagnostics as grouped line-by-line sections separated by blank lines: connection, polling, execution, queue, and the recent redacted runtime/API event ring. These sections include polling state, last update id, active turn source ids, pending dispatch, compaction state, active tool count, pending model-switch state, total queue depth, and queue-lane counts. The event ring records transport/API, polling/update, prompt-dispatch, control-action, typing, compaction, setup, session-lifecycle, and attachment queue/delivery failures; benign unchanged edit responses and unsupported empty draft-clear attempts are filtered out so expected preview transport noise does not obscure real failures
183
183
  - `/telegram-settings` for Ο€-side bridge settings; it currently exposes proactive push as a local toggle backed by the same `telegram.json` flag as the hidden Telegram `/settings` menu
184
- - Queue reactions apply to waiting text, voice, file, image, and media-group turns by matching the turn's source Telegram message ids: `πŸ‘`, `⚑️`, `❀️`, and `πŸ•Š` promote waiting prompts, while `πŸ‘Ž`, `πŸ‘»`, `πŸ’”`, and `πŸ’©` remove waiting turns because ordinary Telegram DM message deletions are not exposed through the Bot API polling path this bridge uses
184
+ - Queue reactions apply to waiting text, voice, file, image, and media-group turns by matching the turn's source Telegram message ids: `πŸ‘`, `⚑️`, `❀️`, `πŸ•Š`, and `πŸ”₯` promote waiting prompts, while `πŸ‘Ž`, `πŸ‘»`, `πŸ’”`, `πŸ’©`, and `πŸ—‘` remove waiting turns because ordinary Telegram DM message deletions are not exposed through the Bot API polling path this bridge uses
185
185
 
186
186
  ## In-Flight Model Switching
187
187
 
package/index.ts CHANGED
@@ -53,8 +53,6 @@ export default function (pi: Pi.ExtensionAPI) {
53
53
  Config.createTelegramProactivePushChecker(configStore);
54
54
  const setProactivePushEnabled =
55
55
  Config.createTelegramProactivePushSetter(configStore);
56
- const proactivePromptTargetStore =
57
- Config.createTelegramProactivePromptTargetStore();
58
56
  const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>();
59
57
  const lockOwnershipGuard =
60
58
  Locks.createTelegramLockOwnershipGuard(lockRuntime);
@@ -496,7 +494,6 @@ export default function (pi: Pi.ExtensionAPI) {
496
494
  sendOutboundReplyArtifacts: outboundReplyArtifactSender,
497
495
  isCurrentOwner: lockOwnershipGuard.ownsContext,
498
496
  getDefaultChatId: proactivePushChatIdGetter,
499
- consumeProactiveReplyToMessageId: proactivePromptTargetStore.consumeForChat,
500
497
  isProactivePushEnabled,
501
498
  recordRuntimeEvent,
502
499
  getActiveToolExecutions: lifecycle.getActiveToolExecutions,
package/lib/config.ts CHANGED
@@ -148,31 +148,6 @@ export function createTelegramProactivePushChatIdGetter(deps: {
148
148
  return () => deps.getActiveTurnChatId() ?? deps.getAllowedUserId();
149
149
  }
150
150
 
151
- export interface TelegramProactivePromptTarget {
152
- chatId: number;
153
- messageId: number;
154
- }
155
-
156
- export interface TelegramProactivePromptTargetStore {
157
- set: (target: TelegramProactivePromptTarget) => void;
158
- consumeForChat: (chatId: number) => number | undefined;
159
- }
160
-
161
- export function createTelegramProactivePromptTargetStore(): TelegramProactivePromptTargetStore {
162
- let target: TelegramProactivePromptTarget | undefined;
163
- return {
164
- set: (nextTarget) => {
165
- target = nextTarget;
166
- },
167
- consumeForChat: (chatId) => {
168
- if (!target || target.chatId !== chatId) return undefined;
169
- const messageId = target.messageId;
170
- target = undefined;
171
- return messageId;
172
- },
173
- };
174
- }
175
-
176
151
  export type TelegramAuthorizationState =
177
152
  | { kind: "pair"; userId: number }
178
153
  | { kind: "allow" }
package/lib/menu-model.ts CHANGED
@@ -620,6 +620,19 @@ export function applyTelegramModelDetailSelection(
620
620
  export function getTelegramSelectedDetailModel<
621
621
  TModel extends MenuModel = MenuModel,
622
622
  >(state: TelegramModelMenuState<TModel>): TelegramMenuSelectionResult<TModel> {
623
+ const indexedSelection = getTelegramModelSelection(
624
+ state,
625
+ state.selectedModelIndex?.toString(),
626
+ );
627
+ if (indexedSelection.kind === "selected") {
628
+ if (!state.selectedModelKey) return indexedSelection;
629
+ const indexedKey = getCanonicalModelId(
630
+ indexedSelection.selection.model,
631
+ ).toLowerCase();
632
+ if (indexedKey === state.selectedModelKey.toLowerCase()) {
633
+ return indexedSelection;
634
+ }
635
+ }
623
636
  if (state.selectedModelKey) {
624
637
  const lowerKey = state.selectedModelKey.toLowerCase();
625
638
  const selection = state.allModels.find(
@@ -627,7 +640,7 @@ export function getTelegramSelectedDetailModel<
627
640
  );
628
641
  if (selection) return { kind: "selected", selection };
629
642
  }
630
- return getTelegramModelSelection(state, state.selectedModelIndex?.toString());
643
+ return indexedSelection;
631
644
  }
632
645
 
633
646
  export function isTelegramModelScoped(
@@ -817,7 +830,6 @@ export function buildTelegramModelCallbackPlan<
817
830
  if (modelsMatch(selection.model, params.activeModel)) {
818
831
  if (action.action === "pick-selected") {
819
832
  focusTelegramModelListPage(params.state, selection.model);
820
- return { kind: "update-menu", text: `Model: ${selection.model.id}` };
821
833
  }
822
834
  return {
823
835
  kind: "refresh-status",
package/lib/queue.ts CHANGED
@@ -794,7 +794,6 @@ export interface TelegramAgentEndRuntimeDeps<
794
794
  options?: { replyToPrompt?: boolean },
795
795
  ) => Promise<void>;
796
796
  getDefaultChatId?: () => number | undefined;
797
- consumeProactiveReplyToMessageId?: (chatId: number) => number | undefined;
798
797
  isProactivePushEnabled?: () => boolean;
799
798
  recordRuntimeEvent?: (
800
799
  category: string,
@@ -839,7 +838,6 @@ export interface TelegramAgentEndHookRuntimeDeps<
839
838
  >["planOutboundReply"];
840
839
  sendOutboundReplyArtifacts?: TelegramAgentEndRuntimeDeps<TTurn>["sendOutboundReplyArtifacts"];
841
840
  getDefaultChatId?: TelegramAgentEndRuntimeDeps<TTurn>["getDefaultChatId"];
842
- consumeProactiveReplyToMessageId?: TelegramAgentEndRuntimeDeps<TTurn>["consumeProactiveReplyToMessageId"];
843
841
  isProactivePushEnabled?: TelegramAgentEndRuntimeDeps<TTurn>["isProactivePushEnabled"];
844
842
  recordRuntimeEvent?: TelegramAgentEndRuntimeDeps<TTurn>["recordRuntimeEvent"];
845
843
  }
@@ -957,7 +955,6 @@ export function createTelegramAgentEndHook<
957
955
  planOutboundReply: deps.planOutboundReply,
958
956
  sendOutboundReplyArtifacts: deps.sendOutboundReplyArtifacts,
959
957
  getDefaultChatId: deps.getDefaultChatId,
960
- consumeProactiveReplyToMessageId: deps.consumeProactiveReplyToMessageId,
961
958
  isProactivePushEnabled: deps.isProactivePushEnabled,
962
959
  recordRuntimeEvent: deps.recordRuntimeEvent,
963
960
  });
@@ -998,14 +995,8 @@ export async function handleTelegramAgentEndRuntime<
998
995
  ) {
999
996
  const defaultChatId = deps.getDefaultChatId?.();
1000
997
  if (defaultChatId !== undefined) {
1001
- const replyToMessageId =
1002
- deps.consumeProactiveReplyToMessageId?.(defaultChatId);
1003
998
  try {
1004
- await deps.sendMarkdownReply(
1005
- defaultChatId,
1006
- replyToMessageId,
1007
- finalText,
1008
- );
999
+ await deps.sendMarkdownReply(defaultChatId, undefined, finalText);
1009
1000
  } catch (error) {
1010
1001
  deps.recordRuntimeEvent?.("proactive-push", error, {
1011
1002
  chatId: defaultChatId,
package/lib/updates.ts CHANGED
@@ -31,12 +31,14 @@ export const TELEGRAM_PRIORITY_REACTIONS = [
31
31
  { id: 11, name: "lightning", emoji: "⚑" },
32
32
  { id: 12, name: "heart", emoji: "❀" },
33
33
  { id: 13, name: "dove", emoji: "πŸ•Š" },
34
+ { id: 14, name: "fire", emoji: "πŸ”₯" },
34
35
  ] as const;
35
36
  export const TELEGRAM_REMOVAL_REACTIONS = [
36
37
  { id: 20, name: "dislike", emoji: "πŸ‘Ž" },
37
38
  { id: 21, name: "ghost", emoji: "πŸ‘»" },
38
39
  { id: 22, name: "broken-heart", emoji: "πŸ’”" },
39
40
  { id: 23, name: "poop", emoji: "πŸ’©" },
41
+ { id: 24, name: "wastebasket", emoji: "πŸ—‘" },
40
42
  ] as const;
41
43
  export const TELEGRAM_PRIORITY_REACTION_EMOJIS =
42
44
  TELEGRAM_PRIORITY_REACTIONS.map((reaction) => reaction.emoji);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "private": false,
5
5
  "description": "Better Telegram DM bridge extension for Ο€",
6
6
  "type": "module",