@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 +1 -0
- package/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/docs/architecture.md +1 -1
- package/index.ts +2 -0
- package/lib/commands.ts +18 -0
- package/lib/routing.ts +4 -0
- package/package.json +1 -1
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.
|
package/docs/architecture.md
CHANGED
|
@@ -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,
|