@llblab/pi-telegram 0.52.2 → 0.53.0

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.
Files changed (204) hide show
  1. package/AGENTS.md +9 -7
  2. package/BACKLOG.md +1 -18
  3. package/CHANGELOG.md +11 -0
  4. package/README.md +7 -11
  5. package/api/commands.ts +3 -0
  6. package/dist/api/commands.d.ts +1 -1
  7. package/dist/lib/activity-verbosity.d.ts +1 -5
  8. package/dist/lib/activity-verbosity.js +15 -42
  9. package/dist/lib/activity.d.ts +9 -0
  10. package/dist/lib/activity.js +39 -10
  11. package/dist/lib/agent-messages.js +7 -3
  12. package/dist/lib/bindings.d.ts +43 -5
  13. package/dist/lib/bindings.js +163 -20
  14. package/dist/lib/bus-api.d.ts +12 -0
  15. package/dist/lib/bus-api.js +182 -79
  16. package/dist/lib/bus-follower.d.ts +111 -31
  17. package/dist/lib/bus-follower.js +1324 -359
  18. package/dist/lib/bus-leader.d.ts +41 -91
  19. package/dist/lib/bus-leader.js +969 -637
  20. package/dist/lib/bus-transport.js +16 -9
  21. package/dist/lib/bus.d.ts +215 -18
  22. package/dist/lib/bus.js +1048 -327
  23. package/dist/lib/channel-posts.d.ts +5 -0
  24. package/dist/lib/channel-posts.js +434 -261
  25. package/dist/lib/command-templates.d.ts +26 -5
  26. package/dist/lib/command-templates.js +92 -176
  27. package/dist/lib/commands.d.ts +168 -82
  28. package/dist/lib/commands.js +1409 -291
  29. package/dist/lib/config.d.ts +1 -8
  30. package/dist/lib/config.js +34 -76
  31. package/dist/lib/delivery.d.ts +2 -2
  32. package/dist/lib/delivery.js +16 -16
  33. package/dist/lib/extension.js +639 -171
  34. package/dist/lib/generative-app-worker.mjs +19 -7
  35. package/dist/lib/generative-apps.d.ts +0 -5
  36. package/dist/lib/generative-apps.js +61 -49
  37. package/dist/lib/inbound.d.ts +5 -0
  38. package/dist/lib/inbound.js +35 -59
  39. package/dist/lib/journal.d.ts +33 -3
  40. package/dist/lib/journal.js +2063 -782
  41. package/dist/lib/lifecycle.d.ts +2 -6
  42. package/dist/lib/lifecycle.js +75 -58
  43. package/dist/lib/locks.d.ts +29 -3
  44. package/dist/lib/locks.js +289 -108
  45. package/dist/lib/logging.js +50 -14
  46. package/dist/lib/media.d.ts +22 -1
  47. package/dist/lib/media.js +46 -28
  48. package/dist/lib/menu-model.d.ts +19 -5
  49. package/dist/lib/menu-model.js +53 -11
  50. package/dist/lib/menu-queue.d.ts +5 -4
  51. package/dist/lib/menu-queue.js +45 -63
  52. package/dist/lib/menu-settings.d.ts +5 -4
  53. package/dist/lib/menu-settings.js +114 -78
  54. package/dist/lib/menu-status.d.ts +1 -1
  55. package/dist/lib/menu-status.js +8 -25
  56. package/dist/lib/menu-thinking.d.ts +6 -1
  57. package/dist/lib/menu-thinking.js +14 -21
  58. package/dist/lib/menu.d.ts +8 -61
  59. package/dist/lib/menu.js +92 -164
  60. package/dist/lib/model.d.ts +1 -1
  61. package/dist/lib/model.js +2 -16
  62. package/dist/lib/outbound-attachments.d.ts +6 -27
  63. package/dist/lib/outbound-attachments.js +45 -38
  64. package/dist/lib/outbound-buttons.js +22 -16
  65. package/dist/lib/outbound-markup.d.ts +0 -2
  66. package/dist/lib/outbound-markup.js +51 -64
  67. package/dist/lib/outbound-voice.js +6 -3
  68. package/dist/lib/outbound.d.ts +6 -6
  69. package/dist/lib/outbound.js +61 -51
  70. package/dist/lib/ownership.js +6 -2
  71. package/dist/lib/paths.d.ts +5 -0
  72. package/dist/lib/paths.js +37 -21
  73. package/dist/lib/pi.d.ts +2 -0
  74. package/dist/lib/pi.js +8 -1
  75. package/dist/lib/polling.d.ts +21 -37
  76. package/dist/lib/polling.js +59 -50
  77. package/dist/lib/preview.js +39 -19
  78. package/dist/lib/process-identity.d.ts +24 -0
  79. package/dist/lib/process-identity.js +133 -0
  80. package/dist/lib/prompts.js +2 -7
  81. package/dist/lib/queue.d.ts +14 -16
  82. package/dist/lib/queue.js +249 -126
  83. package/dist/lib/recovery.d.ts +2 -83
  84. package/dist/lib/recovery.js +20 -274
  85. package/dist/lib/replies.d.ts +16 -24
  86. package/dist/lib/replies.js +99 -116
  87. package/dist/lib/routing.d.ts +120 -7
  88. package/dist/lib/routing.js +4667 -1152
  89. package/dist/lib/runtime.d.ts +1 -11
  90. package/dist/lib/sections.d.ts +4 -29
  91. package/dist/lib/sections.js +7 -20
  92. package/dist/lib/skills.js +4 -3
  93. package/dist/lib/status.js +86 -57
  94. package/dist/lib/sync.d.ts +1 -3
  95. package/dist/lib/sync.js +67 -43
  96. package/dist/lib/target.d.ts +4 -0
  97. package/dist/lib/target.js +18 -0
  98. package/dist/lib/telegram-api.d.ts +18 -13
  99. package/dist/lib/telegram-api.js +148 -53
  100. package/dist/lib/text-groups.js +4 -39
  101. package/dist/lib/thread-cleanup-manager.d.ts +6 -27
  102. package/dist/lib/thread-cleanup-manager.js +369 -186
  103. package/dist/lib/thread-display.d.ts +1 -2
  104. package/dist/lib/thread-display.js +58 -38
  105. package/dist/lib/thread-naming.d.ts +28 -8
  106. package/dist/lib/thread-naming.js +175 -66
  107. package/dist/lib/thread-reconciler.d.ts +42 -16
  108. package/dist/lib/thread-reconciler.js +90 -50
  109. package/dist/lib/threads.d.ts +82 -20
  110. package/dist/lib/threads.js +2247 -855
  111. package/dist/lib/time-injection.js +1 -2
  112. package/dist/lib/turns.js +1 -19
  113. package/dist/lib/updates.d.ts +169 -80
  114. package/dist/lib/updates.js +2418 -641
  115. package/dist/lib/voice.d.ts +7 -22
  116. package/dist/lib/voice.js +7 -22
  117. package/dist/lib/wire.js +1 -1
  118. package/dist/lib/workspace-admission.d.ts +3 -1
  119. package/dist/lib/workspace-admission.js +425 -211
  120. package/dist/lib/workspace-identity.js +8 -13
  121. package/dist/lib/workspace-retirement.d.ts +1 -1
  122. package/dist/lib/workspace-retirement.js +328 -140
  123. package/dist/lib/workspace-slots.js +8 -4
  124. package/dist/package.json +1 -1
  125. package/docs/architecture.md +31 -29
  126. package/docs/inbound.md +1 -1
  127. package/docs/multi-instance-bus.md +15 -3
  128. package/docs/outbound.md +1 -1
  129. package/docs/public-api.md +29 -1
  130. package/docs/ui-style.md +25 -8
  131. package/lib/activity-verbosity.ts +59 -82
  132. package/lib/activity.ts +146 -102
  133. package/lib/agent-messages.ts +12 -5
  134. package/lib/bindings.ts +389 -82
  135. package/lib/bus-api.ts +263 -102
  136. package/lib/bus-follower.ts +2440 -601
  137. package/lib/bus-leader.ts +2209 -1290
  138. package/lib/bus-transport.ts +46 -12
  139. package/lib/bus.ts +1673 -444
  140. package/lib/channel-posts.ts +848 -327
  141. package/lib/command-templates.ts +152 -265
  142. package/lib/commands.ts +2398 -555
  143. package/lib/config.ts +177 -170
  144. package/lib/delivery.ts +41 -29
  145. package/lib/extension.ts +1206 -433
  146. package/lib/generative-app-worker.mjs +19 -7
  147. package/lib/generative-apps.ts +324 -211
  148. package/lib/inbound.ts +95 -97
  149. package/lib/journal.ts +5311 -1763
  150. package/lib/keyboard.ts +1 -3
  151. package/lib/lifecycle.ts +128 -78
  152. package/lib/locks.ts +677 -203
  153. package/lib/logging.ts +147 -58
  154. package/lib/media.ts +82 -43
  155. package/lib/menu-model.ts +85 -18
  156. package/lib/menu-queue.ts +88 -129
  157. package/lib/menu-settings.ts +260 -155
  158. package/lib/menu-status.ts +20 -60
  159. package/lib/menu-thinking.ts +41 -56
  160. package/lib/menu.ts +112 -285
  161. package/lib/model.ts +8 -22
  162. package/lib/outbound-attachments.ts +156 -125
  163. package/lib/outbound-buttons.ts +76 -49
  164. package/lib/outbound-markup.ts +96 -94
  165. package/lib/outbound-voice.ts +33 -27
  166. package/lib/outbound.ts +125 -125
  167. package/lib/ownership.ts +19 -10
  168. package/lib/paths.ts +148 -49
  169. package/lib/pi.ts +35 -15
  170. package/lib/polling.ts +144 -147
  171. package/lib/preview.ts +93 -40
  172. package/lib/process-identity.ts +164 -0
  173. package/lib/prompts.ts +4 -11
  174. package/lib/queue.ts +511 -300
  175. package/lib/recovery.ts +50 -454
  176. package/lib/rendering.ts +1 -3
  177. package/lib/replies.ts +248 -209
  178. package/lib/routing.ts +8509 -2186
  179. package/lib/runtime.ts +1 -12
  180. package/lib/sections.ts +24 -89
  181. package/lib/setup.ts +1 -3
  182. package/lib/skills.ts +9 -4
  183. package/lib/status.ts +215 -110
  184. package/lib/sync.ts +448 -361
  185. package/lib/target.ts +22 -3
  186. package/lib/telegram-api.ts +302 -127
  187. package/lib/text-groups.ts +5 -39
  188. package/lib/thread-cleanup-manager.ts +833 -331
  189. package/lib/thread-display.ts +213 -97
  190. package/lib/thread-naming.ts +232 -87
  191. package/lib/thread-reconciler.ts +155 -72
  192. package/lib/threads.ts +5318 -1845
  193. package/lib/time-injection.ts +1 -4
  194. package/lib/turns.ts +15 -28
  195. package/lib/updates.ts +5184 -1612
  196. package/lib/voice.ts +7 -35
  197. package/lib/wire.ts +1 -1
  198. package/lib/workspace-admission.ts +996 -416
  199. package/lib/workspace-identity.ts +14 -17
  200. package/lib/workspace-retirement.ts +878 -376
  201. package/lib/workspace-slots.ts +22 -13
  202. package/package.json +1 -1
  203. package/scripts/check-downgrade.mjs +0 -472
  204. package/scripts/measure-workspace.mjs +0 -108
@@ -3,7 +3,7 @@
3
3
  * Zones: telegram ui, queue controls, menu composition
4
4
  * Owns queue-menu rendering, queue item callbacks, and queue-menu runtime adapters while core queue mechanics stay in queue
5
5
  */
6
- // --- Queue Menu ---
6
+ import { createTelegramMenuDelivery, } from "./menu-model.js";
7
7
  const QUEUE_ITEM_PROMPT_HTML_LIMIT = 3600;
8
8
  const QUEUE_ITEM_PROMPT_TRUNCATION_SUFFIX = "\n… [truncated]";
9
9
  const EMPTY_QUEUE_REFRESH_TITLES = [
@@ -87,11 +87,6 @@ function findTelegramQueueItem(items, chatId, replyToMessageId) {
87
87
  return item.chatId === chatId && item.replyToMessageId === replyToMessageId;
88
88
  });
89
89
  }
90
- function findTelegramQueueMenuItem(items, chatId, replyToMessageId) {
91
- return items.find((item) => {
92
- return item.chatId === chatId && item.replyToMessageId === replyToMessageId;
93
- });
94
- }
95
90
  function escapeTelegramQueueMenuHtmlChar(char) {
96
91
  if (char === "&")
97
92
  return "&";
@@ -200,7 +195,7 @@ async function handleTelegramQueueMenuCallback(callbackQueryId, data, replyChatI
200
195
  await refreshStaleTelegramQueueMenuItem(callbackQueryId, replyChatId, replyMessageId, deps);
201
196
  return true;
202
197
  }
203
- await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, "Guest prompt skipped.");
198
+ await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, "Guest prompt skipped");
204
199
  return true;
205
200
  }
206
201
  const pickMatch = data.match(/^queue:pick:(\d+):(\d+)$/);
@@ -238,7 +233,7 @@ async function updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMe
238
233
  await deps.answerCallbackQuery(callbackQueryId, notice);
239
234
  }
240
235
  async function refreshStaleTelegramQueueMenuItem(callbackQueryId, replyChatId, replyMessageId, deps) {
241
- await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, "Item no longer in queue.");
236
+ await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, "Item no longer in queue");
242
237
  }
243
238
  async function handleTelegramGuestQueueMenuPick(callbackQueryId, replyChatId, replyMessageId, queueOrder, deps) {
244
239
  const item = deps.findGuestItem(queueOrder);
@@ -278,7 +273,7 @@ async function updateTelegramQueueMenuPriority(callbackQueryId, replyChatId, rep
278
273
  return refreshStaleTelegramQueueMenuItem(callbackQueryId, replyChatId, replyMessageId, deps);
279
274
  }
280
275
  await deps.updateQueueMessage(replyChatId, replyMessageId, getTelegramQueueMenuItemText(updated), buildTelegramQueueItemSubmenuReplyMarkup(chatId, msgId, updated.isPriority, updated.reactionSuppressionEmoji !== undefined));
281
- await deps.answerCallbackQuery(callbackQueryId, updated.isPriority ? "Prioritized." : "Normal priority.");
276
+ await deps.answerCallbackQuery(callbackQueryId, updated.isPriority ? "Prioritized" : "Normal priority");
282
277
  }
283
278
  async function handleTelegramQueueMenuSkipSet(callbackQueryId, replyChatId, replyMessageId, chatId, msgId, skipped, ctx, deps) {
284
279
  const item = deps.findItem(chatId, msgId);
@@ -298,12 +293,17 @@ export function createTelegramQueueMenuRuntime(deps) {
298
293
  const sendQueueMenuMessage = createQueueMenuSendMessageAdapter(deps.sendInteractiveMessage);
299
294
  const editQueueMenuMessage = createQueueMenuEditMessageAdapter(deps.editInteractiveMessage);
300
295
  return {
301
- openQueueMenu: createOpenQueueMenu({
302
- getQueuedItems: deps.telegramQueueStore.getQueuedItems,
303
- getModelMenuState: deps.getModelMenuState,
304
- storeModelMenuState: deps.storeModelMenuState,
305
- sendInteractiveMessage: sendQueueMenuMessage,
306
- }),
296
+ openQueueMenu: (chatId, replyToMessageId, ctx, threadId, options) => {
297
+ const delivery = createTelegramMenuDelivery({ chatId, threadId }, options, deps);
298
+ return createOpenQueueMenu({
299
+ getQueuedItems: deps.telegramQueueStore.getQueuedItems,
300
+ getModelMenuState: (chat, context) => deps.getModelMenuState(chat, context, threadId),
301
+ storeModelMenuState: delivery.storeModelMenuState,
302
+ sendInteractiveMessage: delivery.assertAuthority
303
+ ? createQueueMenuSendMessageAdapter(delivery.sendInteractiveMessage)
304
+ : sendQueueMenuMessage,
305
+ })(chatId, replyToMessageId, ctx);
306
+ },
307
307
  handleCallbackQuery: createQueueMenuCallbackHandler({
308
308
  telegramQueueStore: deps.telegramQueueStore,
309
309
  queueMutationRuntime: deps.queueMutationRuntime,
@@ -339,7 +339,7 @@ function createQueueMenuCallbackHandler(deps) {
339
339
  if (data === "menu:queue" || data === "status:queue") {
340
340
  const state = deps.getStoredModelMenuState(messageId, chatId);
341
341
  if (!state) {
342
- await deps.answerCallbackQuery(query.id, "Interactive message expired.");
342
+ await deps.answerCallbackQuery(query.id, "Interactive message expired");
343
343
  return true;
344
344
  }
345
345
  const menuItems = toTelegramQueueMenuItems(deps.telegramQueueStore.getQueuedItems());
@@ -357,13 +357,17 @@ function createQueueMenuCallbackHandler(deps) {
357
357
  return toTelegramQueueMenuItems(getQueueSnapshot());
358
358
  };
359
359
  const findItem = (cId, rId) => {
360
- return findTelegramQueueMenuItem(toMenuItems(), cId, rId);
360
+ return findTelegramQueueItem(toMenuItems(), cId, rId);
361
361
  };
362
362
  const findGuestItem = (queueOrder) => {
363
363
  return toMenuItems().find((item) => {
364
364
  return item.isGuest && item.queueOrder === queueOrder;
365
365
  });
366
366
  };
367
+ const flagDeps = {
368
+ getQueueSnapshot,
369
+ queueMutationRuntime: deps.queueMutationRuntime,
370
+ };
367
371
  return handleTelegramQueueMenuCallback(query.id, data, chatId, messageId, ctx, {
368
372
  getQueuedItems: toMenuItems,
369
373
  findItem,
@@ -375,24 +379,17 @@ function createQueueMenuCallbackHandler(deps) {
375
379
  dismissGuestPlaceholder: deps.dismissGuestPlaceholder,
376
380
  });
377
381
  },
378
- togglePriority: (cId, rId) => {
379
- return toggleQueuedTelegramPromptPriority(cId, rId, ctx, {
380
- getQueueSnapshot,
381
- queueMutationRuntime: deps.queueMutationRuntime,
382
- });
383
- },
384
- setPriority: (cId, rId, enabled) => {
385
- return setQueuedTelegramPromptPriority(cId, rId, enabled, ctx, {
386
- getQueueSnapshot,
387
- queueMutationRuntime: deps.queueMutationRuntime,
388
- });
389
- },
390
- setSkipped: (cId, rId, skipped) => {
391
- return setQueuedTelegramPromptSkipped(cId, rId, skipped, ctx, {
392
- getQueueSnapshot,
393
- queueMutationRuntime: deps.queueMutationRuntime,
394
- });
395
- },
382
+ togglePriority: (cId, rId) => applyQueuedTelegramPromptMenuFlags(cId, rId, ctx, flagDeps, (item) => ({
383
+ priority: item.queueLane !== "priority",
384
+ skipped: isQueuedTelegramPromptSkipped(item),
385
+ })),
386
+ setPriority: (cId, rId, enabled) => applyQueuedTelegramPromptMenuFlags(cId, rId, ctx, flagDeps, (item) => ({
387
+ priority: enabled,
388
+ skipped: isQueuedTelegramPromptSkipped(item),
389
+ })),
390
+ setSkipped: (cId, rId, skipped) => applyQueuedTelegramPromptMenuFlags(cId, rId, ctx, flagDeps, (item) => item.kind === "prompt"
391
+ ? { priority: item.queueLane === "priority", skipped }
392
+ : undefined),
396
393
  updateQueueMessage: deps.editInteractiveMessage,
397
394
  answerCallbackQuery: deps.answerCallbackQuery,
398
395
  updateStatus: deps.updateStatus,
@@ -401,9 +398,9 @@ function createQueueMenuCallbackHandler(deps) {
401
398
  }
402
399
  async function skipQueuedTelegramGuestPrompt(queueOrder, ctx, deps) {
403
400
  const item = deps.getQueueSnapshot().find((candidate) => {
404
- return candidate.kind === "prompt" &&
401
+ return (candidate.kind === "prompt" &&
405
402
  candidate.guestQueryId !== undefined &&
406
- candidate.queueOrder === queueOrder;
403
+ candidate.queueOrder === queueOrder);
407
404
  });
408
405
  if (!item || !deps.queueMutationRuntime.removeGuestPromptByQueueOrder) {
409
406
  return false;
@@ -420,49 +417,34 @@ function getQueueMenuReactionDisposition(item, priority, skipped) {
420
417
  if (priority && skipped) {
421
418
  return {
422
419
  kind: "priority-suppressed",
423
- priorityEmoji: item.kind === "prompt" ? item.priorityEmoji ?? "⚡" : "⚡",
424
- suppressionEmoji: item.kind === "prompt"
425
- ? item.reactionSuppressionEmoji ?? "👎"
426
- : "👎",
420
+ priorityEmoji: item.kind === "prompt" ? (item.priorityEmoji ?? "⚡") : "⚡",
421
+ suppressionEmoji: item.kind === "prompt" ? (item.reactionSuppressionEmoji ?? "👎") : "👎",
427
422
  };
428
423
  }
429
424
  if (priority) {
430
425
  return {
431
426
  kind: "priority",
432
- emoji: item.kind === "prompt" ? item.priorityEmoji ?? "⚡" : "⚡",
427
+ emoji: item.kind === "prompt" ? (item.priorityEmoji ?? "⚡") : "⚡",
433
428
  };
434
429
  }
435
430
  if (skipped) {
436
431
  return {
437
432
  kind: "suppressed",
438
- emoji: item.kind === "prompt"
439
- ? item.reactionSuppressionEmoji ?? "👎"
440
- : "👎",
433
+ emoji: item.kind === "prompt" ? (item.reactionSuppressionEmoji ?? "👎") : "👎",
441
434
  };
442
435
  }
443
436
  return { kind: "default" };
444
437
  }
445
- function toggleQueuedTelegramPromptPriority(chatId, replyToMessageId, ctx, deps) {
446
- const item = findTelegramQueueItem(deps.getQueueSnapshot(), chatId, replyToMessageId);
447
- if (!item)
448
- return false;
449
- deps.queueMutationRuntime.applyReactionByMessageId(replyToMessageId, getQueueMenuReactionDisposition(item, item.queueLane !== "priority", item.kind === "prompt" &&
450
- item.reactionSuppressionEmoji !== undefined), ctx);
451
- return true;
452
- }
453
- function setQueuedTelegramPromptPriority(chatId, replyToMessageId, enabled, ctx, deps) {
454
- const item = findTelegramQueueItem(deps.getQueueSnapshot(), chatId, replyToMessageId);
455
- if (!item)
456
- return false;
457
- deps.queueMutationRuntime.applyReactionByMessageId(replyToMessageId, getQueueMenuReactionDisposition(item, enabled, item.kind === "prompt" &&
458
- item.reactionSuppressionEmoji !== undefined), ctx);
459
- return true;
438
+ function isQueuedTelegramPromptSkipped(item) {
439
+ return item.kind === "prompt" && item.reactionSuppressionEmoji !== undefined;
460
440
  }
461
- function setQueuedTelegramPromptSkipped(chatId, replyToMessageId, skipped, ctx, deps) {
441
+ /** Applies a menu Priority/Skip choice to one queued item through the reaction disposition path. */
442
+ function applyQueuedTelegramPromptMenuFlags(chatId, replyToMessageId, ctx, deps, chooseFlags) {
462
443
  const item = findTelegramQueueItem(deps.getQueueSnapshot(), chatId, replyToMessageId);
463
- if (!item || item.kind !== "prompt")
444
+ const flags = item ? chooseFlags(item) : undefined;
445
+ if (!item || !flags)
464
446
  return false;
465
- deps.queueMutationRuntime.applyReactionByMessageId(replyToMessageId, getQueueMenuReactionDisposition(item, item.queueLane === "priority", skipped), ctx);
447
+ deps.queueMutationRuntime.applyReactionByMessageId(replyToMessageId, getQueueMenuReactionDisposition(item, flags.priority, flags.skipped), ctx);
466
448
  return true;
467
449
  }
468
450
  function createQueueMenuSendMessageAdapter(sendInteractiveMessage) {
@@ -3,11 +3,12 @@
3
3
  * Zones: telegram ui, settings controls, menu composition
4
4
  * Owns hidden settings-menu rendering, settings callbacks, and persisted toggle wiring
5
5
  */
6
- import type { TelegramActivityVerbosity, TelegramAssistantRenderingMode, TelegramTimeMode, TelegramThreadDisplayMode } from "./config.ts";
6
+ import type { TelegramActivityVerbosity, TelegramAssistantRenderingMode, TelegramThreadDisplayMode, TelegramTimeMode } from "./config.ts";
7
7
  import type { TelegramInlineKeyboardMarkup } from "./keyboard.ts";
8
- import type { TelegramModelMenuState } from "./menu-model.ts";
8
+ import { type TelegramMenuMessageRuntimeDeps, type TelegramModelMenuState } from "./menu-model.ts";
9
9
  import type { MenuModel } from "./model.ts";
10
10
  import { type TelegramSectionRegistry } from "./sections.ts";
11
+ import type { TelegramApiCallOptions } from "./telegram-api.ts";
11
12
  import type { TelegramVoiceReplyMode } from "./voice.ts";
12
13
  export type TelegramSettingsMenuReplyMarkup = TelegramInlineKeyboardMarkup;
13
14
  export interface TelegramSettingsStateDeps {
@@ -51,7 +52,7 @@ export interface TelegramSettingsMenuCallbackDeps extends TelegramSettingsMutati
51
52
  sectionRegistry?: TelegramSectionRegistry;
52
53
  }
53
54
  export interface TelegramSettingsMenuRuntime<TContext> {
54
- openSettingsMenu: (chatId: number, replyToMessageId: number, ctx: TContext) => Promise<void>;
55
+ openSettingsMenu: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
55
56
  handleCallbackQuery: (query: {
56
57
  id: string;
57
58
  data?: string;
@@ -74,7 +75,7 @@ export interface TelegramSettingsMenuRuntimeDeps<TContext, TModel extends MenuMo
74
75
  getStoredModelMenuState: (messageId: number | undefined, chatId?: number) => TelegramModelMenuState<TModel> | undefined;
75
76
  storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
76
77
  editInteractiveMessage: (chatId: number, messageId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramSettingsMenuReplyMarkup) => Promise<void>;
77
- sendInteractiveMessage: (chatId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramSettingsMenuReplyMarkup) => Promise<number | undefined>;
78
+ sendInteractiveMessage: TelegramMenuMessageRuntimeDeps["sendInteractiveMessage"];
78
79
  answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
79
80
  }
80
81
  export declare function buildTelegramSettingsMenuText(): string;
@@ -3,6 +3,7 @@
3
3
  * Zones: telegram ui, settings controls, menu composition
4
4
  * Owns hidden settings-menu rendering, settings callbacks, and persisted toggle wiring
5
5
  */
6
+ import { createTelegramMenuDelivery, } from "./menu-model.js";
6
7
  import { getTelegramExtensionSettingsRows, } from "./sections.js";
7
8
  const SETTINGS_MENU_TITLE = "<b>⚙️ Settings:</b>";
8
9
  const AUTOMATIC_THREAD_CLEANUP_SETTINGS_TITLE = "<b>🧹 Thread cleanup:</b>";
@@ -52,7 +53,9 @@ export function buildAutomaticThreadCleanupSettingsText(enabled) {
52
53
  ].join("\n");
53
54
  }
54
55
  function buildInactiveThreadReviewText(count) {
55
- return [INACTIVE_THREAD_REVIEW_TITLE, "",
56
+ return [
57
+ INACTIVE_THREAD_REVIEW_TITLE,
58
+ "",
56
59
  `${count} proven inactive tab${count === 1 ? "" : "s"}.`,
57
60
  "No tabs were deleted.",
58
61
  "Deletion requires a separate confirmed Clean action.",
@@ -61,14 +64,26 @@ function buildInactiveThreadReviewText(count) {
61
64
  export function buildInactiveThreadReviewReplyMarkup(operationId, canCleanInactiveThreads = false) {
62
65
  const validOperationId = typeof operationId === "string" &&
63
66
  /^thread-cleanup:[a-f0-9]{32}$/u.test(operationId);
64
- return { inline_keyboard: [
65
- [{ text: "⬆️ Back to Thread cleanup",
66
- callback_data: "settings:open:automatic-thread-cleanup" }],
67
- ...(canCleanInactiveThreads && validOperationId ? [[{
68
- text: "🧹 Clean inactive tabs",
69
- callback_data: `settings:clean:${operationId}`,
70
- }]] : []),
71
- ] };
67
+ return {
68
+ inline_keyboard: [
69
+ [
70
+ {
71
+ text: "⬆️ Back to Thread cleanup",
72
+ callback_data: "settings:open:automatic-thread-cleanup",
73
+ },
74
+ ],
75
+ ...(canCleanInactiveThreads && validOperationId
76
+ ? [
77
+ [
78
+ {
79
+ text: "🧹 Clean inactive tabs",
80
+ callback_data: `settings:clean:${operationId}`,
81
+ },
82
+ ],
83
+ ]
84
+ : []),
85
+ ],
86
+ };
72
87
  }
73
88
  export function buildDraftPreviewsSettingsText(enabled) {
74
89
  return [
@@ -187,7 +202,7 @@ export function buildTelegramSettingsMenuReplyMarkup(draftPreviewsEnabled, assis
187
202
  }
188
203
  async function openTelegramSettingsMenu(deps, sectionRegistry) {
189
204
  const state = await deps.getModelMenuState();
190
- const messageId = await deps.sendSettingsMenu(state, buildTelegramSettingsMenuText(), buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.(), deps.isThreadDisplayCustom?.() ?? false));
205
+ const messageId = await deps.sendSettingsMenu(state, buildTelegramSettingsMenuText(), buildCurrentTelegramSettingsMenuReplyMarkup(deps, sectionRegistry));
191
206
  if (messageId === undefined)
192
207
  return;
193
208
  state.messageId = messageId;
@@ -196,21 +211,30 @@ async function openTelegramSettingsMenu(deps, sectionRegistry) {
196
211
  }
197
212
  function threadDisplayModeLabel(mode) {
198
213
  switch (mode) {
199
- case "letters": return "letters";
200
- case "names": return "names";
201
- case "directory-title": return "directory-title";
202
- case "directory-snake": return "directory-snake";
203
- default: return "letters";
214
+ case "letters":
215
+ return "letters";
216
+ case "names":
217
+ return "names";
218
+ case "directory-title":
219
+ return "directory-title";
220
+ case "directory-snake":
221
+ return "directory-snake";
222
+ default:
223
+ return "letters";
204
224
  }
205
225
  }
206
226
  export function buildThreadDisplaySettingsReplyMarkup(mode, custom = false) {
207
- return { inline_keyboard: [
227
+ return {
228
+ inline_keyboard: [
208
229
  [{ text: "⬆️ Back", callback_data: "settings:list" }],
209
- ...["letters", "names", "directory-title", "directory-snake"].map((value) => [{
230
+ ...["letters", "names", "directory-title", "directory-snake"].map((value) => [
231
+ {
210
232
  text: `${!custom && mode === value ? "🟢 " : ""}${threadDisplayModeLabel(value)}`,
211
233
  callback_data: `settings:set:thread-display:${value}`,
212
- }]),
213
- ] };
234
+ },
235
+ ]),
236
+ ],
237
+ };
214
238
  }
215
239
  export function buildAutomaticThreadCleanupSettingsReplyMarkup(enabled, canReviewInactiveThreads = false) {
216
240
  return {
@@ -226,10 +250,16 @@ export function buildAutomaticThreadCleanupSettingsReplyMarkup(enabled, canRevie
226
250
  callback_data: "settings:set:automatic-thread-cleanup:off",
227
251
  },
228
252
  ],
229
- ...(canReviewInactiveThreads ? [[{
230
- text: "🔎 Review inactive tabs",
231
- callback_data: "settings:review:inactive-threads",
232
- }]] : []),
253
+ ...(canReviewInactiveThreads
254
+ ? [
255
+ [
256
+ {
257
+ text: "🔎 Review inactive tabs",
258
+ callback_data: "settings:review:inactive-threads",
259
+ },
260
+ ],
261
+ ]
262
+ : []),
233
263
  ],
234
264
  };
235
265
  }
@@ -307,8 +337,12 @@ export function buildVoiceReplyModeSettingsReplyMarkup(mode, configured = true)
307
337
  ],
308
338
  };
309
339
  }
340
+ /** Settings markup from the current setting values; rendering only, no state mutation. */
341
+ function buildCurrentTelegramSettingsMenuReplyMarkup(deps, sectionRegistry) {
342
+ return buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.(), deps.isThreadDisplayCustom?.() ?? false);
343
+ }
310
344
  async function updateTelegramSettingsMenuMessage(deps, sectionRegistry) {
311
- await deps.updateSettingsMessage(buildTelegramSettingsMenuText(), buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.(), deps.isThreadDisplayCustom?.() ?? false));
345
+ await deps.updateSettingsMessage(buildTelegramSettingsMenuText(), buildCurrentTelegramSettingsMenuReplyMarkup(deps, sectionRegistry));
312
346
  }
313
347
  async function updateAutomaticThreadCleanupSettingsMessage(deps) {
314
348
  const enabled = deps.isAutomaticThreadCleanupEnabled();
@@ -338,29 +372,32 @@ async function updateVoiceReplyModeSettingsMessage(deps) {
338
372
  export async function handleTelegramSettingsMenuCallbackAction(callbackQueryId, data, deps) {
339
373
  if (!data?.startsWith("settings:"))
340
374
  return false;
341
- if (data === "settings:open:thread-display" || data.startsWith("settings:set:thread-display:")) {
375
+ if (data === "settings:open:thread-display" ||
376
+ data.startsWith("settings:set:thread-display:")) {
342
377
  if (!deps.getThreadDisplayMode?.() || !deps.setThreadDisplayMode) {
343
- await deps.answerCallbackQuery(callbackQueryId, "Thread display requires Threaded Mode and a connected instance.");
378
+ await deps.answerCallbackQuery(callbackQueryId, "Thread display requires Threaded Mode and a connected instance");
344
379
  return true;
345
380
  }
346
381
  if (data.startsWith("settings:set:thread-display:")) {
347
382
  const mode = data.slice("settings:set:thread-display:".length);
348
- if (mode !== "letters" && mode !== "names" &&
349
- mode !== "directory-snake" && mode !== "directory-title") {
350
- await deps.answerCallbackQuery(callbackQueryId, "Unknown Thread display mode.");
383
+ if (mode !== "letters" &&
384
+ mode !== "names" &&
385
+ mode !== "directory-snake" &&
386
+ mode !== "directory-title") {
387
+ await deps.answerCallbackQuery(callbackQueryId, "Unknown Thread display mode");
351
388
  return true;
352
389
  }
353
390
  try {
354
391
  await deps.setThreadDisplayMode(mode);
355
392
  }
356
393
  catch {
357
- await deps.answerCallbackQuery(callbackQueryId, "Thread display was not fully applied. The preference may be saved; retry after checking the leader.");
394
+ await deps.answerCallbackQuery(callbackQueryId, "Thread display was not fully applied. The preference may be saved; retry after checking the leader");
358
395
  return true;
359
396
  }
360
397
  }
361
398
  const mode = deps.getThreadDisplayMode();
362
399
  if (!mode) {
363
- await deps.answerCallbackQuery(callbackQueryId, "Threaded Mode is no longer available.");
400
+ await deps.answerCallbackQuery(callbackQueryId, "Threaded Mode is no longer available");
364
401
  return true;
365
402
  }
366
403
  await deps.updateSettingsMessage(buildThreadDisplaySettingsText(mode, deps.isThreadDisplayCustom?.() ?? false), buildThreadDisplaySettingsReplyMarkup(mode, deps.isThreadDisplayCustom?.() ?? false));
@@ -381,7 +418,7 @@ export async function handleTelegramSettingsMenuCallbackAction(callbackQueryId,
381
418
  data === "settings:set:proactive:on" ||
382
419
  data === "settings:set:proactive:off") {
383
420
  await updateTelegramSettingsMenuMessage(deps, deps.sectionRegistry);
384
- await deps.answerCallbackQuery(callbackQueryId, "Public assistant output is always delivered while Telegram is connected.");
421
+ await deps.answerCallbackQuery(callbackQueryId, "Public assistant output is always delivered while Telegram is connected");
385
422
  return true;
386
423
  }
387
424
  if (data === "settings:open:draft-previews" ||
@@ -472,40 +509,45 @@ export async function handleTelegramSettingsMenuCallbackAction(callbackQueryId,
472
509
  }
473
510
  if (data === "settings:review:inactive-threads") {
474
511
  if (!deps.reviewInactiveThreads) {
475
- await deps.answerCallbackQuery(callbackQueryId, "Inactive tab review is unavailable.");
512
+ await deps.answerCallbackQuery(callbackQueryId, "Inactive tab review is unavailable");
476
513
  return true;
477
514
  }
478
515
  try {
479
516
  const review = await deps.reviewInactiveThreads();
480
517
  if (review.count === 0) {
481
- await deps.answerCallbackQuery(callbackQueryId, "No proven inactive tabs.");
518
+ await deps.answerCallbackQuery(callbackQueryId, "No proven inactive tabs");
482
519
  }
483
520
  else {
484
521
  await deps.updateSettingsMessage(buildInactiveThreadReviewText(review.count), buildInactiveThreadReviewReplyMarkup(review.operationId, !!deps.cleanInactiveThreads));
485
- await deps.answerCallbackQuery(callbackQueryId, "Review prepared. No tabs were deleted.");
522
+ await deps.answerCallbackQuery(callbackQueryId, "Review prepared. No tabs were deleted");
486
523
  }
487
524
  }
488
525
  catch {
489
- await deps.answerCallbackQuery(callbackQueryId, "Could not safely review inactive tabs.");
526
+ await deps.answerCallbackQuery(callbackQueryId, "Could not safely review inactive tabs");
490
527
  }
491
528
  return true;
492
529
  }
493
530
  if (data.startsWith("settings:clean:")) {
494
531
  const operationId = data.slice("settings:clean:".length);
495
- if (!/^thread-cleanup:[a-f0-9]{32}$/u.test(operationId) || !deps.cleanInactiveThreads) {
496
- await deps.answerCallbackQuery(callbackQueryId, "Cleanup confirmation is unavailable or stale.");
532
+ if (!/^thread-cleanup:[a-f0-9]{32}$/u.test(operationId) ||
533
+ !deps.cleanInactiveThreads) {
534
+ await deps.answerCallbackQuery(callbackQueryId, "Cleanup confirmation is unavailable or stale");
497
535
  return true;
498
536
  }
499
537
  try {
500
538
  const result = await deps.cleanInactiveThreads(operationId);
501
- await deps.answerCallbackQuery(callbackQueryId, `Deleted: ${result.deleted}. Outcome unknown: ${result.outcomeUnknown}.` +
502
- (result.blocked ? ` Blocked: ${result.blocked}.` : "") +
503
- (result.recovery === "commit-ready" ? " Recovery: safe local commit pending." :
504
- result.recovery === "deletion-outcome-unknown" ? " Recovery: deletion outcome unknown; no retry." :
505
- result.recovery === "authority-blocked" ? " Recovery: cleanup authority unavailable." : ""));
539
+ await deps.answerCallbackQuery(callbackQueryId, `Deleted: ${result.deleted}. Outcome unknown: ${result.outcomeUnknown}` +
540
+ (result.blocked ? `. Blocked: ${result.blocked}` : "") +
541
+ (result.recovery === "commit-ready"
542
+ ? ". Recovery: safe local commit pending"
543
+ : result.recovery === "deletion-outcome-unknown"
544
+ ? ". Recovery: deletion outcome unknown; no retry"
545
+ : result.recovery === "authority-blocked"
546
+ ? ". Recovery: cleanup authority unavailable"
547
+ : ""));
506
548
  }
507
549
  catch {
508
- await deps.answerCallbackQuery(callbackQueryId, "Cleanup could not be safely completed.");
550
+ await deps.answerCallbackQuery(callbackQueryId, "Cleanup could not be safely completed");
509
551
  }
510
552
  return true;
511
553
  }
@@ -521,36 +563,34 @@ export async function handleTelegramSettingsMenuCallbackAction(callbackQueryId,
521
563
  return true;
522
564
  }
523
565
  export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
566
+ // Read per call so later port replacement on deps stays visible.
567
+ const readSettingsState = () => ({
568
+ areDraftPreviewsEnabled: deps.areDraftPreviewsEnabled,
569
+ getAssistantRenderingMode: deps.getAssistantRenderingMode,
570
+ getActivityVerbosity: deps.getActivityVerbosity,
571
+ getVoiceReplyMode: deps.getVoiceReplyMode,
572
+ isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
573
+ getTimeInjectionMode: deps.getTimeInjectionMode,
574
+ isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
575
+ getThreadDisplayMode: deps.getThreadDisplayMode,
576
+ isThreadDisplayCustom: deps.isThreadDisplayCustom,
577
+ });
524
578
  return {
525
- openSettingsMenu: async (chatId, _replyToMessageId, ctx) => {
579
+ openSettingsMenu: async (chatId, _replyToMessageId, ctx, threadId, options) => {
580
+ const delivery = createTelegramMenuDelivery({ chatId, threadId }, options, deps);
526
581
  await deps.reloadConfig?.();
582
+ delivery.assertCurrent();
527
583
  return openTelegramSettingsMenu({
528
- getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
529
- areDraftPreviewsEnabled: deps.areDraftPreviewsEnabled,
530
- getAssistantRenderingMode: deps.getAssistantRenderingMode,
531
- getActivityVerbosity: deps.getActivityVerbosity,
532
- getVoiceReplyMode: deps.getVoiceReplyMode,
533
- isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
534
- getTimeInjectionMode: deps.getTimeInjectionMode,
535
- isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
536
- getThreadDisplayMode: deps.getThreadDisplayMode,
537
- isThreadDisplayCustom: deps.isThreadDisplayCustom,
538
- sendSettingsMenu: (state, text, replyMarkup) => deps.sendInteractiveMessage(state.chatId, text, "html", replyMarkup),
539
- storeModelMenuState: deps.storeModelMenuState,
584
+ getModelMenuState: () => deps.getModelMenuState(chatId, ctx, threadId),
585
+ ...readSettingsState(),
586
+ sendSettingsMenu: (state, text, replyMarkup) => delivery.sendInteractiveMessage(state.chatId, text, "html", replyMarkup),
587
+ storeModelMenuState: delivery.storeModelMenuState,
540
588
  }, sectionRegistry);
541
589
  },
542
590
  updateSettingsMenuMessage: async (state) => {
543
591
  await deps.reloadConfig?.();
544
592
  return updateTelegramSettingsMenuMessage({
545
- areDraftPreviewsEnabled: deps.areDraftPreviewsEnabled,
546
- getAssistantRenderingMode: deps.getAssistantRenderingMode,
547
- getActivityVerbosity: deps.getActivityVerbosity,
548
- getVoiceReplyMode: deps.getVoiceReplyMode,
549
- isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
550
- getTimeInjectionMode: deps.getTimeInjectionMode,
551
- isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
552
- getThreadDisplayMode: deps.getThreadDisplayMode,
553
- isThreadDisplayCustom: deps.isThreadDisplayCustom,
593
+ ...readSettingsState(),
554
594
  updateSettingsMessage: (text, replyMarkup) => deps.editInteractiveMessage(state.chatId, state.messageId, text, "html", replyMarkup),
555
595
  }, sectionRegistry);
556
596
  },
@@ -563,7 +603,7 @@ export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
563
603
  let state = deps.getStoredModelMenuState(messageId, chatId);
564
604
  if (!state) {
565
605
  if (typeof messageId !== "number" || typeof chatId !== "number") {
566
- await deps.answerCallbackQuery(query.id, "Interactive message expired.");
606
+ await deps.answerCallbackQuery(query.id, "Interactive message expired");
567
607
  return true;
568
608
  }
569
609
  state = await deps.getModelMenuState(chatId, ctx, query.message?.message_thread_id);
@@ -572,15 +612,7 @@ export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
572
612
  deps.storeModelMenuState(state);
573
613
  }
574
614
  return handleTelegramSettingsMenuCallbackAction(query.id, query.data, {
575
- areDraftPreviewsEnabled: deps.areDraftPreviewsEnabled,
576
- getAssistantRenderingMode: deps.getAssistantRenderingMode,
577
- getActivityVerbosity: deps.getActivityVerbosity,
578
- getVoiceReplyMode: deps.getVoiceReplyMode,
579
- isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
580
- getTimeInjectionMode: deps.getTimeInjectionMode,
581
- isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
582
- getThreadDisplayMode: deps.getThreadDisplayMode,
583
- isThreadDisplayCustom: deps.isThreadDisplayCustom,
615
+ ...readSettingsState(),
584
616
  setThreadDisplayMode: deps.setThreadDisplayMode,
585
617
  setDraftPreviewsEnabled: deps.setDraftPreviewsEnabled,
586
618
  setAssistantRenderingMode: deps.setAssistantRenderingMode,
@@ -588,8 +620,12 @@ export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
588
620
  setVoiceReplyMode: deps.setVoiceReplyMode,
589
621
  setTimeInjectionMode: deps.setTimeInjectionMode,
590
622
  setAutomaticThreadCleanupEnabled: deps.setAutomaticThreadCleanupEnabled,
591
- ...(deps.reviewInactiveThreads ? { reviewInactiveThreads: deps.reviewInactiveThreads } : {}),
592
- ...(deps.cleanInactiveThreads ? { cleanInactiveThreads: deps.cleanInactiveThreads } : {}),
623
+ ...(deps.reviewInactiveThreads
624
+ ? { reviewInactiveThreads: deps.reviewInactiveThreads }
625
+ : {}),
626
+ ...(deps.cleanInactiveThreads
627
+ ? { cleanInactiveThreads: deps.cleanInactiveThreads }
628
+ : {}),
593
629
  updateSettingsMessage: (text, replyMarkup) => deps.editInteractiveMessage(state.chatId, state.messageId, text, "html", replyMarkup),
594
630
  answerCallbackQuery: deps.answerCallbackQuery,
595
631
  sectionRegistry,
@@ -3,9 +3,9 @@
3
3
  * Zones: telegram ui, status controls, menu composition
4
4
  * Owns status-menu payloads, status callback handling, and status-menu message rendering
5
5
  */
6
- import { type TelegramSectionRegistry } from "./sections.ts";
7
6
  import { type TelegramMenuMessageRuntimeDeps, type TelegramMenuRenderPayload, type TelegramModelMenuState, type TelegramReplyMarkup } from "./menu-model.ts";
8
7
  import { type MenuModel, type ThinkingLevel } from "./model.ts";
8
+ import { type TelegramSectionRegistry } from "./sections.ts";
9
9
  export interface TelegramStatusMenuCallbackDeps {
10
10
  updateModelMenuMessage: () => Promise<void>;
11
11
  updateThinkingMenuMessage: () => Promise<void>;