@llblab/pi-telegram 0.10.7 → 0.10.8

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
@@ -57,6 +57,7 @@
57
57
  - The bridge is session-local, paired to one allowed Telegram user, and owns a local queue aligned with π lifecycle hooks
58
58
  - Queue admission is explicit and validated: immediate commands, control lane, priority lane, and default lane must preserve allowed kind/lane pairings
59
59
  - Dispatch is gated by active turns, pending dispatch, unsettled control work, compaction, `ctx.isIdle()`, and π pending messages; dispatched prompts remain queued until `agent_start` consumes them
60
+ - Telegram `/compact` owns a native `typing` keepalive for the compaction window so phone clients show activity between the started/completed notices; stop it on both completion and failure
60
61
  - `/stop`, `/abort`, `/next`, and `/continue` have distinct contracts: reset queue and abort; abort while preserving queue; force next queued turn; enqueue a priority `continue` prompt
61
62
  - `/start`, `/help`, and `/status` open the unified command-help/status-row/control menu; `/model`, `/thinking`, and `/queue` jump to sections directly; visible bot commands are `/start`, `/compact`, `/next`, `/continue`, `/abort`, `/stop`
62
63
  - Command/menu emoji are fixed UI adornments owned by the `commands` map; do not add a persisted emoji toggle or Settings menu until there is a real setting to own
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.8: Compact Typing Timing Hotfix
4
+
5
+ - `[Compaction]` Telegram `/compact` now starts the native `typing` chat-action keepalive after the "Compaction started" notice is sent, then stops it on completion or failure. Impact: operators see the same Telegram activity indicator during context compression that they already see during normal agent/tool work, without showing `typing` before the explicit start confirmation arrives.
6
+
3
7
  ## 0.10.7: Stale Context Hardening Hotfix
4
8
 
5
9
  - `[Session Reloads]` Context-sensitive command, pairing, queue, session-start, and update-dispatch paths now ignore only stale-session/stale-context failures instead of swallowing broad runtime errors. Impact: the bridge survives ctx replacement/fork/reload races while real bugs still surface for diagnostics.
package/README.md CHANGED
@@ -85,7 +85,7 @@ What it feels like:
85
85
  Use these inside the Telegram DM with your bot. The main entrypoint is `/start`: it opens the operator menu and exposes many of the important agent controls that normally live in the CLI, adapted for Telegram.
86
86
 
87
87
  - **`/start`**: Pair the first Telegram user when needed, register bot commands, and open the inline application menu with command help, prompt-template commands, status rows, model controls, thinking controls, settings, and queue controls.
88
- - **`/compact`**: Start session compaction when the session is idle.
88
+ - **`/compact`**: Start session compaction when the session is idle; Telegram shows the native typing indicator while compaction is running.
89
89
  - **`/next`**: Dispatch the next queued turn, aborting π first if needed.
90
90
  - **`/continue`**: Enqueue a priority `continue` prompt.
91
91
  - **`/abort`**: Abort the active run without touching the queue.
@@ -114,7 +114,7 @@ Dispatch is gated by:
114
114
 
115
115
  This prevents queue races around rapid follow-ups, `/compact`, and mixed local plus Telegram activity. Post-agent-end dispatch retries are scheduled through a session-bound deferred dispatcher that activates on session start, cancels timers on session shutdown, and skips callbacks from older generations before they touch `ExtensionContext`. Telegram `/start` and hidden compatibility shortcuts `/status`, `/model`, `/thinking`, `/queue`, and `/settings` execute immediately; the dispatch controller still serializes any deferred control items so a queued control action must settle before the next queued action can dispatch.
116
116
 
117
- `/start` opens the main application menu: visible command help, compact command-only prompt-template rows when π exposes Telegram-compatible prompt-template names, status rows (`Status`, `Usage`, `Cost`, `Context`), and top-level buttons for model, thinking, and queue sections. The `Status` row reports `compacting` while a Telegram `/compact` run is active. The Queue button includes the current queued-item count. Hidden compatibility shortcuts `/help`, `/status`, `/model`, `/thinking`, and `/queue` jump directly to their corresponding menu screens, while `/settings` opens the hidden settings menu for bridge toggles such as proactive push. Settings options open detail submenus; checkbox-like settings use Back plus green/black/yellow On and Off controls instead of mutating directly from the list. Command emoji come from the `commands` domain map so visible command descriptions and matching menu buttons share one fixed adornment source. Prompt-template commands use a fixed `🧩` marker, map π template names to Telegram-safe aliases such as `fix-tests` → `/fix_tests`, stay visible only inside the `/start` menu, and expand before queueing because `ExtensionAPI.sendUserMessage()` intentionally bypasses π prompt-template expansion for extension-originated messages. Every submenu starts with a top Back row so navigation stays anchored near the original user message above the inline keyboard; model-menu pagination controls sit near the top, tapping the pagination indicator opens a compact page picker headed by `<b>Choose a page:</b>`, and tapping a model opens a detail submenu with Back, ☑️ Activate/🟢 Active selection, and yellow/black-marked Scoped/All membership tabs. `menu-model` owns model-menu state, scoped model pages, model detail rendering, scoped-list persistence planning, and model-menu rendering while `model` owns core model identity/switching semantics. `menu-thinking` owns thinking-menu text, reply markup, callback handling, and message rendering. `menu-status` owns status-menu payloads, status callback handling, and status-message rendering. `menu-queue` owns queue-menu UI only: queue items are rendered under a compact `<b>Queue:</b>` heading, top-to-bottom in dispatch order, numbered, and marked with `⚡` for priority prompts or `📎` for prompts with attachments. An empty queue renders bold message text with the bottom-filled `⌛` hourglass plus the top Main menu button, while non-empty queue states keep the running `⏳` hourglass. Selecting an item opens a submenu that displays the queue item number above the full queued prompt text with Back, side-by-side Priority/Normal tabs, and Cancel. If a callback targets an item that has already left the queue, the menu refreshes the list instead of applying a stale mutation.
117
+ `/start` opens the main application menu: visible command help, compact command-only prompt-template rows when π exposes Telegram-compatible prompt-template names, status rows (`Status`, `Usage`, `Cost`, `Context`), and top-level buttons for model, thinking, and queue sections. The `Status` row reports `compacting` while a Telegram `/compact` run is active, and the bridge sends Telegram's native `typing` chat action as a keepalive for the same compaction window. The Queue button includes the current queued-item count. Hidden compatibility shortcuts `/help`, `/status`, `/model`, `/thinking`, and `/queue` jump directly to their corresponding menu screens, while `/settings` opens the hidden settings menu for bridge toggles such as proactive push. Settings options open detail submenus; checkbox-like settings use Back plus green/black/yellow On and Off controls instead of mutating directly from the list. Command emoji come from the `commands` domain map so visible command descriptions and matching menu buttons share one fixed adornment source. Prompt-template commands use a fixed `🧩` marker, map π template names to Telegram-safe aliases such as `fix-tests` → `/fix_tests`, stay visible only inside the `/start` menu, and expand before queueing because `ExtensionAPI.sendUserMessage()` intentionally bypasses π prompt-template expansion for extension-originated messages. Every submenu starts with a top Back row so navigation stays anchored near the original user message above the inline keyboard; model-menu pagination controls sit near the top, tapping the pagination indicator opens a compact page picker headed by `<b>Choose a page:</b>`, and tapping a model opens a detail submenu with Back, ☑️ Activate/🟢 Active selection, and yellow/black-marked Scoped/All membership tabs. `menu-model` owns model-menu state, scoped model pages, model detail rendering, scoped-list persistence planning, and model-menu rendering while `model` owns core model identity/switching semantics. `menu-thinking` owns thinking-menu text, reply markup, callback handling, and message rendering. `menu-status` owns status-menu payloads, status callback handling, and status-message rendering. `menu-queue` owns queue-menu UI only: queue items are rendered under a compact `<b>Queue:</b>` heading, top-to-bottom in dispatch order, numbered, and marked with `⚡` for priority prompts or `📎` for prompts with attachments. An empty queue renders bold message text with the bottom-filled `⌛` hourglass plus the top Main menu button, while non-empty queue states keep the running `⏳` hourglass. Selecting an item opens a submenu that displays the queue item number above the full queued prompt text with Back, side-by-side Priority/Normal tabs, and Cancel. If a callback targets an item that has already left the queue, the menu refreshes the list instead of applying a stale mutation.
118
118
 
119
119
  ### Abort Behavior
120
120
 
package/index.ts CHANGED
@@ -354,6 +354,8 @@ export default function (pi: Pi.ExtensionAPI) {
354
354
  dispatchNextQueuedTelegramTurn,
355
355
  requestDeferredDispatchNextQueuedTelegramTurn:
356
356
  deferredQueueDispatchRuntime.request,
357
+ startTypingLoop: promptDispatchRuntime.startTypingLoop,
358
+ stopTypingLoop: typing.stop,
357
359
  answerCallbackQuery,
358
360
  editInteractiveMessage,
359
361
  sendInteractiveMessage,
package/lib/commands.ts CHANGED
@@ -320,6 +320,8 @@ export interface TelegramCompactCommandDeps extends TelegramRuntimeEventRecorder
320
320
  requestDeferredDispatchNextQueuedTelegramTurn?: (
321
321
  dispatch: () => void,
322
322
  ) => void;
323
+ startTypingLoop?: () => void;
324
+ stopTypingLoop?: () => void;
323
325
  compact: (callbacks: {
324
326
  onComplete: () => void;
325
327
  onError: (error: unknown) => void;
@@ -553,6 +555,8 @@ export interface TelegramCommandRuntimeDeps<
553
555
  requestDeferredDispatchNextQueuedTelegramTurn?: (
554
556
  dispatch: (ctx: TContext) => void,
555
557
  ) => void;
558
+ startTypingLoop?: (ctx: TContext, chatId?: number) => void;
559
+ stopTypingLoop?: () => void;
556
560
  enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
557
561
  compact: (
558
562
  ctx: TContext,
@@ -798,15 +802,20 @@ export async function handleTelegramCompactCommand(
798
802
  }
799
803
  deps.setCompactionInProgress(true);
800
804
  deps.updateStatus();
805
+ let compactionStillInProgress = true;
801
806
  try {
802
807
  deps.compact({
803
808
  onComplete: () => {
809
+ compactionStillInProgress = false;
810
+ deps.stopTypingLoop?.();
804
811
  deps.setCompactionInProgress(false);
805
812
  deps.updateStatus();
806
813
  dispatchNextQueuedTelegramTurnAfterCompact(deps);
807
814
  void deps.sendTextReply("Compaction completed.");
808
815
  },
809
816
  onError: (error) => {
817
+ compactionStillInProgress = false;
818
+ deps.stopTypingLoop?.();
810
819
  deps.setCompactionInProgress(false);
811
820
  deps.updateStatus();
812
821
  dispatchNextQueuedTelegramTurnAfterCompact(deps);
@@ -816,6 +825,8 @@ export async function handleTelegramCompactCommand(
816
825
  },
817
826
  });
818
827
  } catch (error) {
828
+ compactionStillInProgress = false;
829
+ deps.stopTypingLoop?.();
819
830
  deps.setCompactionInProgress(false);
820
831
  deps.updateStatus();
821
832
  deps.recordRuntimeEvent?.("compact", error);
@@ -824,6 +835,7 @@ export async function handleTelegramCompactCommand(
824
835
  return;
825
836
  }
826
837
  await deps.sendTextReply("Compaction started.");
838
+ if (compactionStillInProgress) deps.startTypingLoop?.();
827
839
  }
828
840
 
829
841
  function isTelegramStaleContextError(error: unknown): boolean {
@@ -962,6 +974,8 @@ export function createTelegramCommandHandlerTargetRuntime<
962
974
  setCompactionInProgress: deps.setCompactionInProgress,
963
975
  updateStatus: deps.updateStatus,
964
976
  dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
977
+ startTypingLoop: deps.startTypingLoop,
978
+ stopTypingLoop: deps.stopTypingLoop,
965
979
  enqueueContinueTurn: deps.enqueueContinueTurn,
966
980
  compact: deps.compact,
967
981
  enqueueControlItem: commandTargetRuntime.enqueueControlItem,
@@ -1115,6 +1129,10 @@ async function handleTelegramCommandRuntime<
1115
1129
  )
1116
1130
  : undefined,
1117
1131
  compact: (callbacks) => deps.compact(commandCtx, callbacks),
1132
+ startTypingLoop: deps.startTypingLoop
1133
+ ? () => deps.startTypingLoop?.(commandCtx, nextMessage.chat.id)
1134
+ : undefined,
1135
+ stopTypingLoop: deps.stopTypingLoop,
1118
1136
  sendTextReply: sendReplyFor(nextMessage),
1119
1137
  recordRuntimeEvent: deps.recordRuntimeEvent,
1120
1138
  });
package/lib/routing.ts CHANGED
@@ -82,6 +82,8 @@ export interface TelegramInboundRouteRuntimeDeps<
82
82
  requestDeferredDispatchNextQueuedTelegramTurn?: (
83
83
  dispatch: (ctx: TContext) => void,
84
84
  ) => void;
85
+ startTypingLoop?: (ctx: TContext, chatId?: number) => void;
86
+ stopTypingLoop?: () => void;
85
87
  answerCallbackQuery: (
86
88
  callbackQueryId: string,
87
89
  text?: string,
@@ -344,6 +346,8 @@ export function createTelegramInboundRouteRuntime<
344
346
  dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
345
347
  requestDeferredDispatchNextQueuedTelegramTurn:
346
348
  deps.requestDeferredDispatchNextQueuedTelegramTurn,
349
+ startTypingLoop: deps.startTypingLoop,
350
+ stopTypingLoop: deps.stopTypingLoop,
347
351
  enqueueContinueTurn,
348
352
  compact: deps.compact,
349
353
  allocateItemOrder: deps.bridgeRuntime.queue.allocateItemOrder,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.10.7",
3
+ "version": "0.10.8",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"