@llblab/pi-telegram 0.9.9 → 0.10.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.
@@ -4,6 +4,10 @@
4
4
  * Owns hidden settings-menu rendering, settings callbacks, and persisted toggle wiring
5
5
  */
6
6
 
7
+ import {
8
+ getTelegramExtensionSettingsRows,
9
+ type TelegramSectionRegistry,
10
+ } from "./extension-sections.ts";
7
11
  import type { TelegramInlineKeyboardMarkup } from "./keyboard.ts";
8
12
  import type { TelegramModelMenuState } from "./menu-model.ts";
9
13
  import type { MenuModel } from "./model.ts";
@@ -39,6 +43,7 @@ export interface TelegramSettingsMenuCallbackDeps extends TelegramSettingsMutati
39
43
  callbackQueryId: string,
40
44
  text?: string,
41
45
  ) => Promise<void>;
46
+ sectionRegistry?: TelegramSectionRegistry;
42
47
  }
43
48
 
44
49
  export interface TelegramSettingsMenuRuntime<TContext> {
@@ -116,28 +121,41 @@ export function buildProactivePushSettingsText(): string {
116
121
 
117
122
  export function buildTelegramSettingsMenuReplyMarkup(
118
123
  proactivePushEnabled: boolean,
124
+ sectionRegistry?: TelegramSectionRegistry,
119
125
  ): TelegramSettingsMenuReplyMarkup {
120
- return {
121
- inline_keyboard: [
122
- [{ text: "⬆️ Main menu", callback_data: "menu:back" }],
123
- [
124
- {
125
- text: `${proactivePushEnabled ? "🟢" : "⚫️"} Proactive push`,
126
- callback_data: "settings:open:proactive",
127
- },
128
- ],
129
- ],
130
- };
126
+ const rows: Array<Array<{ text: string; callback_data: string }>> = [
127
+ [{ text: "⬆️ Main menu", callback_data: "menu:back" }],
128
+ ];
129
+ // Extension settings rows before built-in controls
130
+ if (sectionRegistry) {
131
+ const settingsRows = getTelegramExtensionSettingsRows(sectionRegistry);
132
+ for (const row of settingsRows) {
133
+ rows.push([{ text: row.label, callback_data: row.callback_data }]);
134
+ }
135
+ }
136
+ rows.push([
137
+ {
138
+ text: `${proactivePushEnabled ? "🟢" : "⚫️"} Proactive push`,
139
+ callback_data: "settings:open:proactive",
140
+ },
141
+ ]);
142
+ return { inline_keyboard: rows };
131
143
  }
132
144
 
133
145
  export async function openTelegramSettingsMenu<
134
146
  TModel extends MenuModel = MenuModel,
135
- >(deps: TelegramSettingsMenuOpenDeps<TModel>): Promise<void> {
147
+ >(
148
+ deps: TelegramSettingsMenuOpenDeps<TModel>,
149
+ sectionRegistry?: TelegramSectionRegistry,
150
+ ): Promise<void> {
136
151
  const state = await deps.getModelMenuState();
137
152
  const messageId = await deps.sendSettingsMenu(
138
153
  state,
139
154
  buildTelegramSettingsMenuText(),
140
- buildTelegramSettingsMenuReplyMarkup(deps.isProactivePushEnabled()),
155
+ buildTelegramSettingsMenuReplyMarkup(
156
+ deps.isProactivePushEnabled(),
157
+ sectionRegistry,
158
+ ),
141
159
  );
142
160
  if (messageId === undefined) return;
143
161
  state.messageId = messageId;
@@ -167,10 +185,14 @@ export function buildProactivePushSettingsReplyMarkup(
167
185
 
168
186
  export async function updateTelegramSettingsMenuMessage(
169
187
  deps: TelegramSettingsMenuMessageUpdateDeps,
188
+ sectionRegistry?: TelegramSectionRegistry,
170
189
  ): Promise<void> {
171
190
  await deps.updateSettingsMessage(
172
191
  buildTelegramSettingsMenuText(),
173
- buildTelegramSettingsMenuReplyMarkup(deps.isProactivePushEnabled()),
192
+ buildTelegramSettingsMenuReplyMarkup(
193
+ deps.isProactivePushEnabled(),
194
+ sectionRegistry,
195
+ ),
174
196
  );
175
197
  }
176
198
 
@@ -190,7 +212,7 @@ export async function handleTelegramSettingsMenuCallbackAction(
190
212
  ): Promise<boolean> {
191
213
  if (!data?.startsWith("settings:")) return false;
192
214
  if (data === "settings:list") {
193
- await updateTelegramSettingsMenuMessage(deps);
215
+ await updateTelegramSettingsMenuMessage(deps, deps.sectionRegistry);
194
216
  await deps.answerCallbackQuery(callbackQueryId);
195
217
  return true;
196
218
  }
@@ -221,28 +243,40 @@ export function createTelegramSettingsMenuRuntime<
221
243
  TModel extends MenuModel = MenuModel,
222
244
  >(
223
245
  deps: TelegramSettingsMenuRuntimeDeps<TContext, TModel>,
246
+ sectionRegistry?: TelegramSectionRegistry,
224
247
  ): TelegramSettingsMenuRuntime<TContext> {
225
248
  return {
226
249
  openSettingsMenu: (chatId, _replyToMessageId, ctx) =>
227
- openTelegramSettingsMenu({
228
- getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
229
- isProactivePushEnabled: deps.isProactivePushEnabled,
230
- sendSettingsMenu: (state, text, replyMarkup) =>
231
- deps.sendInteractiveMessage(state.chatId, text, "html", replyMarkup),
232
- storeModelMenuState: deps.storeModelMenuState,
233
- }),
250
+ openTelegramSettingsMenu(
251
+ {
252
+ getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
253
+ isProactivePushEnabled: deps.isProactivePushEnabled,
254
+ sendSettingsMenu: (state, text, replyMarkup) =>
255
+ deps.sendInteractiveMessage(
256
+ state.chatId,
257
+ text,
258
+ "html",
259
+ replyMarkup,
260
+ ),
261
+ storeModelMenuState: deps.storeModelMenuState,
262
+ },
263
+ sectionRegistry,
264
+ ),
234
265
  updateSettingsMenuMessage: (state) =>
235
- updateTelegramSettingsMenuMessage({
236
- isProactivePushEnabled: deps.isProactivePushEnabled,
237
- updateSettingsMessage: (text, replyMarkup) =>
238
- deps.editInteractiveMessage(
239
- state.chatId,
240
- state.messageId,
241
- text,
242
- "html",
243
- replyMarkup,
244
- ),
245
- }),
266
+ updateTelegramSettingsMenuMessage(
267
+ {
268
+ isProactivePushEnabled: deps.isProactivePushEnabled,
269
+ updateSettingsMessage: (text, replyMarkup) =>
270
+ deps.editInteractiveMessage(
271
+ state.chatId,
272
+ state.messageId,
273
+ text,
274
+ "html",
275
+ replyMarkup,
276
+ ),
277
+ },
278
+ sectionRegistry,
279
+ ),
246
280
  handleCallbackQuery: async (query) => {
247
281
  if (!query.data?.startsWith("settings:")) return false;
248
282
  const state = deps.getStoredModelMenuState(query.message?.message_id);
@@ -265,6 +299,7 @@ export function createTelegramSettingsMenuRuntime<
265
299
  replyMarkup,
266
300
  ),
267
301
  answerCallbackQuery: deps.answerCallbackQuery,
302
+ sectionRegistry,
268
303
  });
269
304
  },
270
305
  };
@@ -5,6 +5,10 @@
5
5
  */
6
6
 
7
7
  import { formatTelegramCommandEmojiPrefix } from "./commands.ts";
8
+ import {
9
+ getTelegramSectionMainMenuRows,
10
+ type TelegramSectionRegistry,
11
+ } from "./extension-sections.ts";
8
12
  import {
9
13
  formatStatusButtonLabel,
10
14
  type TelegramMenuMessageRuntimeDeps,
@@ -143,6 +147,7 @@ export function buildStatusReplyMarkup(
143
147
  activeModel: MenuModel | undefined,
144
148
  currentThinkingLevel: ThinkingLevel,
145
149
  queueItemCount = 0,
150
+ sectionRegistry?: TelegramSectionRegistry,
146
151
  ): TelegramReplyMarkup {
147
152
  const rows: Array<Array<{ text: string; callback_data: string }>> = [];
148
153
  rows.push([
@@ -171,6 +176,13 @@ export function buildStatusReplyMarkup(
171
176
  callback_data: "menu:queue",
172
177
  },
173
178
  ]);
179
+ // Extension section rows before Settings
180
+ if (sectionRegistry) {
181
+ const sectionRows = getTelegramSectionMainMenuRows(sectionRegistry);
182
+ for (const row of sectionRows) {
183
+ rows.push([{ text: row.text, callback_data: row.callback_data }]);
184
+ }
185
+ }
174
186
  rows.push([
175
187
  {
176
188
  text: "⚙️ Settings",
@@ -185,6 +197,7 @@ export function buildTelegramStatusMenuRenderPayload(
185
197
  activeModel: MenuModel | undefined,
186
198
  currentThinkingLevel: ThinkingLevel,
187
199
  queueItemCount = 0,
200
+ sectionRegistry?: TelegramSectionRegistry,
188
201
  ): TelegramMenuRenderPayload {
189
202
  return {
190
203
  nextMode: "status",
@@ -194,6 +207,7 @@ export function buildTelegramStatusMenuRenderPayload(
194
207
  activeModel,
195
208
  currentThinkingLevel,
196
209
  queueItemCount,
210
+ sectionRegistry,
197
211
  ),
198
212
  };
199
213
  }
@@ -205,6 +219,7 @@ export async function updateTelegramStatusMessage(
205
219
  currentThinkingLevel: ThinkingLevel,
206
220
  deps: TelegramMenuMessageRuntimeDeps,
207
221
  queueItemCount = 0,
222
+ sectionRegistry?: TelegramSectionRegistry,
208
223
  ): Promise<void> {
209
224
  await editTelegramMenuMessage(
210
225
  state,
@@ -213,6 +228,7 @@ export async function updateTelegramStatusMessage(
213
228
  activeModel,
214
229
  currentThinkingLevel,
215
230
  queueItemCount,
231
+ sectionRegistry,
216
232
  ),
217
233
  deps,
218
234
  );
@@ -225,6 +241,7 @@ export function sendTelegramStatusMessage(
225
241
  currentThinkingLevel: ThinkingLevel,
226
242
  deps: TelegramMenuMessageRuntimeDeps,
227
243
  queueItemCount = 0,
244
+ sectionRegistry?: TelegramSectionRegistry,
228
245
  ): Promise<number | undefined> {
229
246
  return sendTelegramMenuMessage(
230
247
  state,
@@ -233,6 +250,7 @@ export function sendTelegramStatusMessage(
233
250
  activeModel,
234
251
  currentThinkingLevel,
235
252
  queueItemCount,
253
+ sectionRegistry,
236
254
  ),
237
255
  deps,
238
256
  );
package/lib/menu.ts CHANGED
@@ -4,6 +4,13 @@
4
4
  * Owns app-menu/status state, inline UI text, and callback composition while model/thinking/queue menu details live in dedicated domains
5
5
  */
6
6
 
7
+ import {
8
+ handleTelegramSectionCallback,
9
+ handleTelegramSectionOpen,
10
+ handleTelegramSectionSettingsOpen,
11
+ parseTelegramSectionCallback,
12
+ type TelegramSectionRegistry,
13
+ } from "./extension-sections.ts";
7
14
  import {
8
15
  createTelegramModelMenuStateBuilder,
9
16
  handleTelegramModelMenuCallbackAction,
@@ -14,6 +21,7 @@ import {
14
21
  type TelegramModelMenuState,
15
22
  type TelegramModelMenuStateBuilderContext,
16
23
  type TelegramModelMenuStateBuilderDeps,
24
+ type TelegramReplyMarkup,
17
25
  } from "./menu-model.ts";
18
26
  import {
19
27
  handleTelegramStatusMenuCallbackAction,
@@ -39,9 +47,9 @@ export {
39
47
  buildModelPageMenuReplyMarkup,
40
48
  buildTelegramModelCallbackPlan,
41
49
  buildTelegramModelMenuRenderPayload,
42
- buildTelegramModelPageMenuRenderPayload,
43
50
  buildTelegramModelMenuState,
44
51
  buildTelegramModelMenuStateRuntime,
52
+ buildTelegramModelPageMenuRenderPayload,
45
53
  createTelegramModelMenuRuntime,
46
54
  createTelegramModelMenuStateBuilder,
47
55
  formatScopedModelButtonText,
@@ -215,6 +223,21 @@ export interface TelegramMenuCallbackRuntimeDeps<
215
223
  selection: ScopedTelegramModel<TModel>,
216
224
  ctx: TContext,
217
225
  ) => Promise<boolean> | boolean;
226
+ sectionRegistry?: TelegramSectionRegistry;
227
+ editInteractiveMessage?: (
228
+ chatId: number,
229
+ messageId: number,
230
+ text: string,
231
+ mode: "html" | "plain",
232
+ replyMarkup: TelegramReplyMarkup,
233
+ ) => Promise<void>;
234
+ sendInteractiveMessage?: (
235
+ chatId: number,
236
+ text: string,
237
+ mode: "html" | "plain",
238
+ replyMarkup: TelegramReplyMarkup,
239
+ ) => Promise<number | undefined>;
240
+ enqueueSectionPrompt?: (prompt: string, ctx: TContext) => Promise<void>;
218
241
  }
219
242
 
220
243
  export interface TelegramMenuActionRuntimeDeps<
@@ -237,6 +260,7 @@ export interface TelegramMenuActionRuntimeDeps<
237
260
  replyToMessageId: number,
238
261
  text: string,
239
262
  ) => Promise<unknown>;
263
+ sectionRegistry?: TelegramSectionRegistry;
240
264
  }
241
265
 
242
266
  export interface TelegramMenuActionRuntime<
@@ -432,6 +456,21 @@ export interface TelegramMenuCallbackRuntimeAdapterDeps<
432
456
  selection: ScopedTelegramModel<TModel>,
433
457
  ctx: TContext,
434
458
  ) => Promise<boolean> | boolean;
459
+ sectionRegistry?: TelegramSectionRegistry;
460
+ editInteractiveMessage?: (
461
+ chatId: number,
462
+ messageId: number,
463
+ text: string,
464
+ mode: "html" | "plain",
465
+ replyMarkup: TelegramReplyMarkup,
466
+ ) => Promise<void>;
467
+ sendInteractiveMessage?: (
468
+ chatId: number,
469
+ text: string,
470
+ mode: "html" | "plain",
471
+ replyMarkup: TelegramReplyMarkup,
472
+ ) => Promise<number | undefined>;
473
+ enqueueSectionPrompt?: (prompt: string, ctx: TContext) => Promise<void>;
435
474
  }
436
475
 
437
476
  export function createTelegramMenuCallbackHandler<
@@ -471,6 +510,10 @@ export function createTelegramMenuCallbackHandlerForContext<
471
510
  setCurrentModel: deps.setCurrentModel,
472
511
  stagePendingModelSwitch: deps.stagePendingModelSwitch,
473
512
  restartInterruptedTelegramTurn: deps.restartInterruptedTelegramTurn,
513
+ sectionRegistry: deps.sectionRegistry,
514
+ editInteractiveMessage: deps.editInteractiveMessage,
515
+ sendInteractiveMessage: deps.sendInteractiveMessage,
516
+ enqueueSectionPrompt: deps.enqueueSectionPrompt,
474
517
  });
475
518
  }
476
519
 
@@ -493,6 +536,89 @@ export async function handleTelegramMenuCallbackRuntime<
493
536
  await deps.answerCallbackQuery(query.id);
494
537
  return;
495
538
  }
539
+ // Section callbacks: dispatch before built-in menu handling
540
+ if (deps.sectionRegistry && query.data?.startsWith("section:")) {
541
+ const parsed = parseTelegramSectionCallback(query.data);
542
+ if (parsed) {
543
+ const chatId = (query as { message?: { chat?: { id?: number } } }).message
544
+ ?.chat?.id;
545
+ const messageId = (query as { message?: { message_id?: number } }).message
546
+ ?.message_id;
547
+ if (typeof chatId === "number" && typeof messageId === "number") {
548
+ const { token, action, payload } = parsed;
549
+ if (action === "open") {
550
+ const state = deps.getStoredModelMenuState(messageId);
551
+ if (!state) {
552
+ await deps.answerCallbackQuery(
553
+ query.id,
554
+ "Interactive message expired.",
555
+ );
556
+ return;
557
+ }
558
+ const handled = await handleTelegramSectionOpen(
559
+ deps.sectionRegistry,
560
+ token,
561
+ chatId,
562
+ messageId,
563
+ query.id,
564
+ {
565
+ answerCallbackQuery: deps.answerCallbackQuery,
566
+ editInteractiveMessage:
567
+ deps.editInteractiveMessage ?? (async () => {}),
568
+ sendInteractiveMessage:
569
+ deps.sendInteractiveMessage ?? (async () => undefined),
570
+ enqueuePrompt: deps.enqueueSectionPrompt
571
+ ? (prompt: string) => deps.enqueueSectionPrompt!(prompt, ctx)
572
+ : async () => {},
573
+ },
574
+ );
575
+ if (handled) return;
576
+ } else if (action === "settings") {
577
+ if (typeof chatId === "number" && typeof messageId === "number") {
578
+ const handled = await handleTelegramSectionSettingsOpen(
579
+ deps.sectionRegistry,
580
+ token,
581
+ chatId,
582
+ messageId,
583
+ query.id,
584
+ {
585
+ answerCallbackQuery: deps.answerCallbackQuery,
586
+ editInteractiveMessage:
587
+ deps.editInteractiveMessage ?? (async () => {}),
588
+ sendInteractiveMessage:
589
+ deps.sendInteractiveMessage ?? (async () => undefined),
590
+ enqueuePrompt: deps.enqueueSectionPrompt
591
+ ? (prompt: string) => deps.enqueueSectionPrompt!(prompt, ctx)
592
+ : async () => {},
593
+ },
594
+ );
595
+ if (handled) return;
596
+ }
597
+ } else {
598
+ const handled = await handleTelegramSectionCallback(
599
+ deps.sectionRegistry,
600
+ token,
601
+ action,
602
+ payload,
603
+ chatId,
604
+ messageId,
605
+ query.id,
606
+ {
607
+ answerCallbackQuery: deps.answerCallbackQuery,
608
+ editInteractiveMessage:
609
+ deps.editInteractiveMessage ?? (async () => {}),
610
+ sendInteractiveMessage:
611
+ deps.sendInteractiveMessage ?? (async () => undefined),
612
+ enqueuePrompt: deps.enqueueSectionPrompt
613
+ ? (prompt: string) => deps.enqueueSectionPrompt!(prompt, ctx)
614
+ : async () => {},
615
+ },
616
+ );
617
+ if (handled) return;
618
+ }
619
+ }
620
+ }
621
+ }
496
622
  await handleStoredTelegramMenuCallback(query, {
497
623
  getStoredModelMenuState: deps.getStoredModelMenuState,
498
624
  handleStatusAction: async (state) =>
@@ -601,6 +727,7 @@ export function createTelegramMenuActionRuntimeWithStateBuilder<
601
727
  sendTextReply: deps.sendTextReply,
602
728
  editInteractiveMessage: deps.editInteractiveMessage,
603
729
  sendInteractiveMessage: deps.sendInteractiveMessage,
730
+ sectionRegistry: deps.sectionRegistry,
604
731
  });
605
732
  }
606
733
 
@@ -628,6 +755,7 @@ export function createTelegramMenuActionRuntime<
628
755
  deps.getThinkingLevel(),
629
756
  deps,
630
757
  deps.getQueueItemCount?.() ?? 0,
758
+ deps.sectionRegistry,
631
759
  ),
632
760
  sendStatusMessage: (chatId, replyToMessageId, ctx) =>
633
761
  openTelegramStatusMenu({
@@ -658,6 +786,7 @@ export function createTelegramMenuActionRuntime<
658
786
  thinkingLevel,
659
787
  deps,
660
788
  queueItemCount,
789
+ deps.sectionRegistry,
661
790
  ),
662
791
  storeModelMenuState: deps.storeModelMenuState,
663
792
  }),
package/lib/replies.ts CHANGED
@@ -430,8 +430,7 @@ export function dedupSendMarkdownReply<TReplyMarkup = unknown>(
430
430
  /**
431
431
  * Guest reply sender: renders Markdown → HTML, sends via answerGuestQuery.
432
432
  * Keeps guest rendering inside the replies domain so the orchestration layer
433
- * (index.ts) does not import from rendering.ts directly.
434
- */
433
+ * (index.ts) does not import from rendering.ts directly. */
435
434
  export function createGuestMarkdownReplySender(deps: {
436
435
  renderTelegramMessage: (
437
436
  text: string,
package/lib/routing.ts CHANGED
@@ -4,21 +4,22 @@
4
4
  * Wires authorized updates into menus, commands, media grouping, and prompt queueing
5
5
  */
6
6
 
7
- import * as OutboundHandlers from "./outbound-handlers.ts";
8
- import * as Commands from "./commands.ts";
9
7
  import { readFile } from "node:fs/promises";
8
+ import * as Commands from "./commands.ts";
10
9
  import type { TelegramConfigStore } from "./config.ts";
10
+ import type { TelegramSectionRegistry } from "./extension-sections.ts";
11
11
  import type { TelegramInboundHandlerRuntime } from "./inbound-handlers.ts";
12
12
  import * as Media from "./media.ts";
13
13
  import * as Menu from "./menu.ts";
14
14
  import * as Model from "./model.ts";
15
- import * as Queue from "./queue.ts";
15
+ import * as OutboundHandlers from "./outbound-handlers.ts";
16
16
  import * as PromptTemplates from "./prompt-templates.ts";
17
+ import * as Queue from "./queue.ts";
17
18
  import type { TelegramBridgeRuntime } from "./runtime.ts";
18
19
  import * as TextGroups from "./text-groups.ts";
19
20
  import * as Turns from "./turns.ts";
20
- import * as Updates from "./updates.ts";
21
21
  import type { TelegramUser } from "./updates.ts";
22
+ import * as Updates from "./updates.ts";
22
23
 
23
24
  export type TelegramRoutedMessage = Updates.TelegramUpdateMessage &
24
25
  Media.TelegramMediaMessage &
@@ -82,6 +83,19 @@ export interface TelegramInboundRouteRuntimeDeps<
82
83
  callbackQueryId: string,
83
84
  text?: string,
84
85
  ) => Promise<void>;
86
+ editInteractiveMessage?: (
87
+ chatId: number,
88
+ messageId: number,
89
+ text: string,
90
+ mode: "html" | "plain",
91
+ replyMarkup: Menu.TelegramReplyMarkup,
92
+ ) => Promise<void>;
93
+ sendInteractiveMessage?: (
94
+ chatId: number,
95
+ text: string,
96
+ mode: "html" | "plain",
97
+ replyMarkup: Menu.TelegramReplyMarkup,
98
+ ) => Promise<number | undefined>;
85
99
  answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
86
100
  sendTextReply: (
87
101
  chatId: number,
@@ -112,12 +126,14 @@ export interface TelegramInboundRouteRuntimeDeps<
112
126
  error: unknown,
113
127
  details?: Record<string, unknown>,
114
128
  ) => void;
129
+ sectionRegistry?: TelegramSectionRegistry;
115
130
  }
116
131
 
117
132
  const TELEGRAM_OWNED_CALLBACK_PREFIXES = [
118
133
  "menu:",
119
134
  "model:",
120
135
  "queue:",
136
+ "section:",
121
137
  "settings:",
122
138
  "status:",
123
139
  "tgbtn:",
@@ -174,6 +190,35 @@ export function createTelegramInboundRouteRuntime<
174
190
  stagePendingModelSwitch: deps.modelSwitchController.stagePendingSwitch,
175
191
  restartInterruptedTelegramTurn:
176
192
  deps.modelSwitchController.restartInterruptedTurn,
193
+ sectionRegistry: deps.sectionRegistry,
194
+ editInteractiveMessage: deps.editInteractiveMessage,
195
+ sendInteractiveMessage: deps.sendInteractiveMessage,
196
+ enqueueSectionPrompt: async (prompt: string, ctx: TContext) => {
197
+ const chatId = deps.configStore.getAllowedUserId();
198
+ if (typeof chatId !== "number") return;
199
+ const order = deps.bridgeRuntime.queue.allocateItemOrder();
200
+ const turn: Queue.PendingTelegramTurn = {
201
+ kind: "prompt",
202
+ chatId,
203
+ replyToMessageId: 0,
204
+ sourceMessageIds: [],
205
+ queueOrder: order,
206
+ queueLane: "default",
207
+ laneOrder: order,
208
+ queuedAttachments: [],
209
+ content: [
210
+ {
211
+ type: "text",
212
+ text: `[telegram] ${prompt}`,
213
+ },
214
+ ],
215
+ historyText: Turns.truncateTelegramQueueSummary(prompt),
216
+ statusSummary: Turns.truncateTelegramQueueSummary(prompt),
217
+ };
218
+ deps.queueMutationRuntime.append(turn, ctx);
219
+ deps.updateStatus(ctx);
220
+ deps.dispatchNextQueuedTelegramTurn(ctx);
221
+ },
177
222
  });
178
223
  const callbackHandler = async (
179
224
  query: TCallbackQuery,
@@ -376,9 +421,7 @@ export function createTelegramInboundRouteRuntime<
376
421
  // Build telegram prefix with guest context
377
422
  const fromRaw = gm.from as Record<string, unknown> | undefined;
378
423
  const fromName =
379
- (fromRaw?.username as string) ||
380
- (fromRaw?.first_name as string) ||
381
- "";
424
+ (fromRaw?.username as string) || (fromRaw?.first_name as string) || "";
382
425
  const chatRaw = gm.chat as Record<string, unknown>;
383
426
  const chatTitle = chatRaw?.title as string | undefined;
384
427
  const chatType = chatRaw?.type as string;
@@ -390,20 +433,24 @@ export function createTelegramInboundRouteRuntime<
390
433
  const telegramPrefix = `[${prefixParts.join("|")}]`;
391
434
  // Extract reply context
392
435
  const replyMsg = gm.reply_to_message as Record<string, unknown> | undefined;
393
- const replyText =
394
- replyMsg
395
- ? ((replyMsg.text as string) || (replyMsg.caption as string) || "").trim()
396
- : "";
397
- const replyFrom =
398
- replyMsg
399
- ? (replyMsg.from as Record<string, unknown> | undefined)?.username as string | undefined
400
- : undefined;
436
+ const replyText = replyMsg
437
+ ? ((replyMsg.text as string) || (replyMsg.caption as string) || "").trim()
438
+ : "";
439
+ const replyFrom = replyMsg
440
+ ? ((replyMsg.from as Record<string, unknown> | undefined)?.username as
441
+ | string
442
+ | undefined)
443
+ : undefined;
401
444
  // Download files, run inbound handlers
402
445
  const guestMsg = guestMessage as unknown as Media.TelegramMediaMessage;
403
446
  const files = await Media.downloadTelegramMessageFiles([guestMsg], {
404
447
  downloadFile: deps.downloadFile,
405
448
  });
406
- const processed = await deps.inboundHandlerRuntime.process(files, text, ctx);
449
+ const processed = await deps.inboundHandlerRuntime.process(
450
+ files,
451
+ text,
452
+ ctx,
453
+ );
407
454
  let rawText = processed.rawText || text;
408
455
  // Append reply context after handler processing
409
456
  if (replyText) {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.9.9",
3
+ "version": "0.10.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "description": "Telegram Runtime Adapter for π",
8
+ "description": "Telegram runtime adapter for π",
9
9
  "type": "module",
10
10
  "keywords": [
11
11
  "pi-package",