@llblab/pi-telegram 0.5.0 → 0.5.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/README.md CHANGED
@@ -82,7 +82,7 @@ Use these inside the Telegram DM with your bot:
82
82
  - **`/status`**: View session stats, cost, and use inline buttons to change models.
83
83
  - **`/model`**: Open the interactive model selector.
84
84
  - **`/compact`**: Start session compaction (only works when the session is idle).
85
- - **`/stop`**: Abort the active run.
85
+ - **`/stop`**: Abort the active run and clear all waiting Telegram queue items.
86
86
 
87
87
  Telegram command admission is explicit: `/compact`, `/stop`, `/help`, `/start`, `/status`, and `/model` execute immediately. Synthetic model-switch continuation turns still enter the high-priority control lane so they can resume before normal queued prompts when pi becomes safe to dispatch.
88
88
 
@@ -92,7 +92,7 @@ Admission contract:
92
92
 
93
93
  | Admission | Examples | Queue shape | Dispatch rank |
94
94
  | --------------------- | ---------------------------------------------------- | -------------------------------------------------------------------- | ------------- |
95
- | Immediate execution | `/compact`, `/stop`, `/help`, `/start` | Does not enter the Telegram queue | N/A |
95
+ | Immediate execution | `/compact`, `/stop`, `/help`, `/start` | Does not enter the Telegram queue; `/stop` also clears queued items | N/A |
96
96
  | Control queue | Model-switch continuation turns and future deferred controls | `queueLane: control`; accepts control items and continuation prompts | 0 |
97
97
  | Priority prompt queue | A waiting prompt promoted by `👍` | `kind: prompt`, `queueLane: priority` | 1 |
98
98
  | Default prompt queue | Normal Telegram text/media turns | `kind: prompt`, `queueLane: default` | 2 |
@@ -113,7 +113,7 @@ This prevents queue races around rapid follow-ups, `/compact`, and mixed local p
113
113
 
114
114
  ### Abort Behavior
115
115
 
116
- 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.
116
+ When `/stop` runs from Telegram, it clears pending model-switch state, clears every waiting Telegram queue item, resets aborted-turn history preservation, and then aborts the active Telegram turn when an abort handler exists. This intentionally favors recovery over preservation: priority/default/control queue items are dropped so the next Telegram message can enter a clean queue and dispatch like a fresh TUI prompt after an interrupted run.
117
117
 
118
118
  ## Rendering Model
119
119
 
@@ -162,7 +162,7 @@ Current operator controls include:
162
162
  - Inline status buttons for model and thinking adjustments, applying idle selections immediately while still respecting busy-run restart rules; model-menu inputs are cached briefly and stored inline-menu states are pruned by TTL/LRU so old keyboards expire predictably
163
163
  - `/model` for interactive model selection, executed immediately from Telegram and supporting in-flight restart of the active Telegram-owned run on a newly selected model
164
164
  - `/compact` for Telegram-triggered pi session compaction when the bridge is idle
165
- - `/stop` for aborting the active Telegram-owned run
165
+ - `/stop` for aborting the active Telegram-owned run and clearing waiting Telegram queue items
166
166
  - `/telegram-status` for pi-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
167
167
  - Queue reactions using `👍` and `👎` apply to waiting text, voice, file, image, and media-group turns by matching the turn's source Telegram message ids; `👎` acts as the canonical queue-removal path because ordinary Telegram DM message deletions are not exposed through the Bot API polling path this bridge uses
168
168
 
package/lib/commands.ts CHANGED
@@ -32,7 +32,10 @@ export const TELEGRAM_BOT_COMMANDS: readonly TelegramBotCommandDefinition[] = [
32
32
  },
33
33
  { command: "model", description: "Open the interactive model selector" },
34
34
  { command: "compact", description: "Compact the current pi session" },
35
- { command: "stop", description: "Abort the current pi task" },
35
+ {
36
+ command: "stop",
37
+ description: "Abort the current pi task and clear queued turns",
38
+ },
36
39
  ];
37
40
 
38
41
  export interface TelegramBotCommandRegistrationDeps {
@@ -178,7 +181,7 @@ export interface TelegramCommandActionDeps<TMessage, TContext> {
178
181
  export interface TelegramStopCommandDeps {
179
182
  hasAbortHandler: () => boolean;
180
183
  clearPendingModelSwitch: () => void;
181
- hasQueuedTelegramItems: () => boolean;
184
+ clearQueuedTelegramItems: () => number;
182
185
  setPreserveQueuedTurnsAsHistory: (preserve: boolean) => void;
183
186
  abortCurrentTurn: () => void;
184
187
  updateStatus: () => void;
@@ -408,6 +411,7 @@ export interface TelegramCommandRuntimeDeps<
408
411
  hasAbortHandler: () => boolean;
409
412
  clearPendingModelSwitch: () => void;
410
413
  hasQueuedTelegramItems: () => boolean;
414
+ clearQueuedTelegramItems: (ctx: TContext) => number;
411
415
  setPreserveQueuedTurnsAsHistory: (preserve: boolean) => void;
412
416
  abortCurrentTurn: () => void;
413
417
  isIdle: (ctx: TContext) => boolean;
@@ -439,7 +443,7 @@ export interface TelegramCommandRuntimeDeps<
439
443
  }
440
444
 
441
445
  export const TELEGRAM_HELP_TEXT =
442
- "Send me a message and I will forward it to pi. Commands: /status, /model, /compact, /stop.";
446
+ "Send me a message and I will forward it to pi. Commands: /status, /model, /compact, /stop. /stop aborts the current run and clears queued Telegram turns.";
443
447
 
444
448
  function getTelegramCommandErrorMessage(error: unknown): string {
445
449
  return error instanceof Error ? error.message : String(error);
@@ -482,20 +486,32 @@ export function getTelegramCommandExecutionMode(
482
486
  return action.executionMode;
483
487
  }
484
488
 
489
+ function formatTelegramQueuedTurnCount(count: number): string {
490
+ return count === 1 ? "1 queued turn" : `${count} queued turns`;
491
+ }
492
+
485
493
  export async function handleTelegramStopCommand(
486
494
  deps: TelegramStopCommandDeps,
487
495
  ): Promise<void> {
496
+ deps.clearPendingModelSwitch();
497
+ const clearedCount = deps.clearQueuedTelegramItems();
498
+ deps.setPreserveQueuedTurnsAsHistory(false);
488
499
  if (!deps.hasAbortHandler()) {
489
- await deps.sendTextReply("No active turn.");
500
+ const clearedSuffix =
501
+ clearedCount > 0
502
+ ? ` Cleared ${formatTelegramQueuedTurnCount(clearedCount)}.`
503
+ : "";
504
+ if (clearedCount > 0) deps.updateStatus();
505
+ await deps.sendTextReply(`No active turn.${clearedSuffix}`);
490
506
  return;
491
507
  }
492
- deps.clearPendingModelSwitch();
493
- if (deps.hasQueuedTelegramItems()) {
494
- deps.setPreserveQueuedTurnsAsHistory(true);
495
- }
496
508
  deps.abortCurrentTurn();
497
509
  deps.updateStatus();
498
- await deps.sendTextReply("Aborted current turn.");
510
+ const clearedSuffix =
511
+ clearedCount > 0
512
+ ? ` Cleared ${formatTelegramQueuedTurnCount(clearedCount)}.`
513
+ : "";
514
+ await deps.sendTextReply(`Aborted current turn.${clearedSuffix}`);
499
515
  }
500
516
 
501
517
  export async function handleTelegramCompactCommand(
@@ -656,6 +672,7 @@ export function createTelegramCommandHandlerTargetRuntime<
656
672
  hasAbortHandler: deps.hasAbortHandler,
657
673
  clearPendingModelSwitch: deps.clearPendingModelSwitch,
658
674
  hasQueuedTelegramItems: deps.hasQueuedTelegramItems,
675
+ clearQueuedTelegramItems: deps.clearQueuedTelegramItems,
659
676
  setPreserveQueuedTurnsAsHistory: deps.setPreserveQueuedTurnsAsHistory,
660
677
  abortCurrentTurn: deps.abortCurrentTurn,
661
678
  isIdle: deps.isIdle,
@@ -736,7 +753,8 @@ async function handleTelegramCommandRuntime<
736
753
  await handleTelegramStopCommand({
737
754
  hasAbortHandler: deps.hasAbortHandler,
738
755
  clearPendingModelSwitch: deps.clearPendingModelSwitch,
739
- hasQueuedTelegramItems: deps.hasQueuedTelegramItems,
756
+ clearQueuedTelegramItems: () =>
757
+ deps.clearQueuedTelegramItems(commandCtx),
740
758
  setPreserveQueuedTurnsAsHistory: deps.setPreserveQueuedTurnsAsHistory,
741
759
  abortCurrentTurn: deps.abortCurrentTurn,
742
760
  updateStatus: updateStatusFor(commandCtx),
package/lib/queue.ts CHANGED
@@ -1061,6 +1061,7 @@ export interface TelegramQueueMutationControllerDeps<
1061
1061
  export interface TelegramQueueMutationController<TContext> {
1062
1062
  append: (item: TelegramQueueItem<TContext>, ctx: TContext) => void;
1063
1063
  reorder: (ctx: TContext) => void;
1064
+ clear: (ctx: TContext) => number;
1064
1065
  removeByMessageIds: (messageIds: number[], ctx: TContext) => number;
1065
1066
  clearPriorityByMessageId: (messageId: number, ctx: TContext) => boolean;
1066
1067
  prioritizeByMessageId: (messageId: number, ctx: TContext) => boolean;
@@ -1262,6 +1263,7 @@ export function createTelegramQueueMutationController<TContext>(
1262
1263
  append: (item, ctx) =>
1263
1264
  appendTelegramQueueItemRuntime(item, buildRuntimeDeps(ctx)),
1264
1265
  reorder: (ctx) => reorderTelegramQueueItemsRuntime(buildRuntimeDeps(ctx)),
1266
+ clear: (ctx) => clearTelegramQueueItemsRuntime(buildRuntimeDeps(ctx)),
1265
1267
  removeByMessageIds: (messageIds, ctx) =>
1266
1268
  removeTelegramQueueItemsByMessageIdsRuntime(
1267
1269
  messageIds,
@@ -1291,6 +1293,16 @@ export function reorderTelegramQueueItemsRuntime<TContext>(
1291
1293
  deps.updateStatus(deps.ctx);
1292
1294
  }
1293
1295
 
1296
+ export function clearTelegramQueueItemsRuntime<TContext>(
1297
+ deps: TelegramQueueMutationRuntimeDeps<TContext>,
1298
+ ): number {
1299
+ const removedCount = deps.getQueuedItems().length;
1300
+ if (removedCount === 0) return 0;
1301
+ deps.setQueuedItems([]);
1302
+ deps.updateStatus(deps.ctx);
1303
+ return removedCount;
1304
+ }
1305
+
1294
1306
  export function removeTelegramQueueItemsByMessageIdsRuntime<TContext>(
1295
1307
  messageIds: number[],
1296
1308
  deps: TelegramQueueMutationRuntimeDeps<TContext>,
package/lib/routing.ts CHANGED
@@ -131,6 +131,7 @@ export function createTelegramInboundRouteRuntime<
131
131
  hasAbortHandler: deps.bridgeRuntime.abort.hasHandler,
132
132
  clearPendingModelSwitch: deps.modelSwitchController.clearPendingSwitch,
133
133
  hasQueuedTelegramItems: deps.telegramQueueStore.hasQueuedItems,
134
+ clearQueuedTelegramItems: deps.queueMutationRuntime.clear,
134
135
  setPreserveQueuedTurnsAsHistory:
135
136
  deps.bridgeRuntime.lifecycle.setPreserveQueuedTurnsAsHistory,
136
137
  abortCurrentTurn: deps.bridgeRuntime.abort.abortTurn,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "private": false,
5
5
  "description": "Better Telegram DM bridge extension for pi",
6
6
  "type": "module",