@llblab/pi-kit 0.29.2 → 0.30.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.
Files changed (208) hide show
  1. package/BACKLOG.md +2 -1
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +2 -2
  4. package/node_modules/@llblab/pi-telegram/AGENTS.md +9 -7
  5. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -18
  6. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +15 -0
  7. package/node_modules/@llblab/pi-telegram/README.md +7 -11
  8. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  9. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  10. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  11. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  12. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  13. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  14. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  15. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  16. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +163 -20
  17. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  18. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +182 -79
  19. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  20. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  21. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  22. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  23. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  24. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  25. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  26. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  28. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  29. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  30. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  31. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  32. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  33. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  34. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  35. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  36. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +639 -171
  37. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  38. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  39. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  40. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  42. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +33 -3
  43. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2063 -782
  44. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  45. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  46. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +29 -3
  47. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +289 -108
  48. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  49. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  50. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  51. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  52. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  53. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  54. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  55. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  56. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  57. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  58. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  59. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  60. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  61. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  62. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  63. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  65. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  66. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  67. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  68. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  69. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  70. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  71. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  72. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +65 -51
  73. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  74. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  75. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  76. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  77. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +21 -37
  79. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +59 -50
  80. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  81. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  82. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  83. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  84. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +14 -16
  85. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +252 -127
  86. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  87. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  88. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  89. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  90. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +120 -7
  91. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4667 -1152
  92. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  93. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  94. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  95. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  96. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  97. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +1 -3
  98. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +67 -43
  99. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  100. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  101. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -13
  102. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +148 -53
  103. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  104. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  105. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  106. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  107. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  108. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  109. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  110. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  111. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  112. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +82 -20
  113. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2247 -855
  114. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  115. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  116. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +169 -80
  117. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2418 -641
  118. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  119. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  120. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  121. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  122. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  123. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  124. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  125. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  126. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  127. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  128. package/node_modules/@llblab/pi-telegram/docs/architecture.md +31 -29
  129. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  130. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  131. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  132. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  133. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  134. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  135. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  136. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  137. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +389 -82
  138. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +263 -102
  139. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  140. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  141. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  142. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  143. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  144. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  145. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  146. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  147. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  148. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1206 -433
  149. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  150. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  151. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  152. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5311 -1763
  153. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  154. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  155. package/node_modules/@llblab/pi-telegram/lib/locks.ts +677 -203
  156. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  157. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  158. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  159. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  160. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  161. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  162. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  163. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  164. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  165. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  166. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  167. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  168. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  169. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +128 -125
  170. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  171. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  172. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  173. package/node_modules/@llblab/pi-telegram/lib/polling.ts +144 -147
  174. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  175. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  176. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  177. package/node_modules/@llblab/pi-telegram/lib/queue.ts +516 -301
  178. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  179. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  180. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  181. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8509 -2186
  182. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  183. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  184. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  185. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  186. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  187. package/node_modules/@llblab/pi-telegram/lib/sync.ts +448 -361
  188. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  189. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +302 -127
  190. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  191. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  192. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  193. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  194. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  195. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5318 -1845
  196. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  197. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  198. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5184 -1612
  199. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  200. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  201. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  202. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  203. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  204. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  205. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  206. package/package.json +2 -2
  207. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  208. package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
@@ -4,26 +4,13 @@
4
4
  * Owns status-menu payloads, status callback handling, and status-menu message rendering
5
5
  */
6
6
  import { formatTelegramCommandEmojiPrefix } from "./commands.js";
7
- import { getTelegramSectionMainMenuRows, } from "./sections.js";
8
- import { formatStatusButtonLabel, } from "./menu-model.js";
7
+ import { refuseUnavailableTelegramThinkingControls } from "./menu-thinking.js";
8
+ import { editTelegramMenuMessage, formatStatusButtonLabel, sendTelegramMenuMessage, } from "./menu-model.js";
9
9
  import { getCanonicalModelId, } from "./model.js";
10
+ import { getTelegramSectionMainMenuRows, } from "./sections.js";
10
11
  function isTelegramStatusMenuCallbackAction(data, action) {
11
12
  return data === `menu:${action}` || data === `status:${action}`;
12
13
  }
13
- function applyTelegramMenuRenderPayload(state, payload) {
14
- state.mode = payload.nextMode;
15
- return payload;
16
- }
17
- async function editTelegramMenuMessage(state, payload, deps) {
18
- const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
19
- await deps.editInteractiveMessage(state.chatId, state.messageId, appliedPayload.text, appliedPayload.mode, appliedPayload.replyMarkup);
20
- }
21
- function sendTelegramMenuMessage(state, payload, deps) {
22
- const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
23
- return deps.sendInteractiveMessage(state.chatId, appliedPayload.text, appliedPayload.mode, appliedPayload.replyMarkup, state.threadId !== undefined
24
- ? { target: { chatId: state.chatId, threadId: state.threadId } }
25
- : undefined);
26
- }
27
14
  export async function openTelegramStatusMenu(deps) {
28
15
  const state = await deps.getModelMenuState();
29
16
  const messageId = await deps.sendStatusMenu(state, deps.buildStatusHtml(), deps.getActiveModel(), deps.getThinkingLevel(), deps.getQueueItemCount?.() ?? 0);
@@ -48,14 +35,8 @@ export async function handleTelegramStatusMenuCallbackAction(callbackQueryId, da
48
35
  }
49
36
  if (!isTelegramStatusMenuCallbackAction(data, "thinking"))
50
37
  return false;
51
- if (deps.isVoiceReplyActive?.()) {
52
- await deps.answerCallbackQuery(callbackQueryId, "Thinking controls are disabled during voice replies.");
53
- return true;
54
- }
55
- if (!activeModel?.reasoning) {
56
- await deps.answerCallbackQuery(callbackQueryId, "This model has no reasoning controls.");
38
+ if (await refuseUnavailableTelegramThinkingControls(callbackQueryId, activeModel, deps))
57
39
  return true;
58
- }
59
40
  await deps.updateThinkingMenuMessage();
60
41
  await deps.answerCallbackQuery(callbackQueryId);
61
42
  return true;
@@ -83,10 +64,12 @@ export function buildStatusReplyMarkup(activeModel, currentThinkingLevel, queueI
83
64
  },
84
65
  ]);
85
66
  if (pendingCancellationCount > 0)
86
- rows.push([{
67
+ rows.push([
68
+ {
87
69
  text: `❌ Pending cancellations: ${pendingCancellationCount}`,
88
70
  callback_data: "reroutecancel:review:open",
89
- }]);
71
+ },
72
+ ]);
90
73
  if (sectionRegistry) {
91
74
  const sectionRows = getTelegramSectionMainMenuRows(sectionRegistry);
92
75
  for (const row of sectionRows) {
@@ -3,7 +3,7 @@
3
3
  * Zones: telegram ui, thinking controls, menu composition
4
4
  * Owns thinking-menu text, reply markup, callback handling, and thinking-menu message rendering
5
5
  */
6
- import type { TelegramMenuMessageRuntimeDeps, TelegramMenuRenderPayload, TelegramModelMenuState, TelegramReplyMarkup } from "./menu-model.ts";
6
+ import { type TelegramMenuMessageRuntimeDeps, type TelegramMenuRenderPayload, type TelegramModelMenuState, type TelegramReplyMarkup } from "./menu-model.ts";
7
7
  import { type MenuModel, type ThinkingLevel } from "./model.ts";
8
8
  export interface TelegramThinkingMenuCallbackDeps {
9
9
  setThinkingLevel: (level: ThinkingLevel) => void;
@@ -19,6 +19,11 @@ export interface TelegramThinkingMenuOpenDeps<TModel extends MenuModel = MenuMod
19
19
  storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
20
20
  isVoiceReplyActive?: () => boolean;
21
21
  }
22
+ /** Answers and returns true when voice replies or a non-reasoning model disable thinking controls. */
23
+ export declare function refuseUnavailableTelegramThinkingControls(callbackQueryId: string, activeModel: MenuModel | undefined, deps: {
24
+ answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
25
+ isVoiceReplyActive?: () => boolean;
26
+ }): Promise<boolean>;
22
27
  export declare function handleTelegramThinkingMenuCallbackAction(callbackQueryId: string, data: string | undefined, activeModel: MenuModel | undefined, deps: TelegramThinkingMenuCallbackDeps): Promise<boolean>;
23
28
  export declare function buildThinkingMenuText(): string;
24
29
  export declare function buildThinkingMenuReplyMarkup(currentThinkingLevel: ThinkingLevel): TelegramReplyMarkup;
@@ -3,42 +3,35 @@
3
3
  * Zones: telegram ui, thinking controls, menu composition
4
4
  * Owns thinking-menu text, reply markup, callback handling, and thinking-menu message rendering
5
5
  */
6
+ import { editTelegramMenuMessage, sendTelegramMenuMessage, } from "./menu-model.js";
6
7
  import { isThinkingLevel, THINKING_LEVELS, } from "./model.js";
7
8
  function parseTelegramThinkingMenuCallbackAction(data) {
8
9
  if (!data?.startsWith("thinking:set:"))
9
10
  return undefined;
10
11
  return { kind: "thinking:set", level: data.slice("thinking:set:".length) };
11
12
  }
12
- function applyTelegramMenuRenderPayload(state, payload) {
13
- state.mode = payload.nextMode;
14
- return payload;
15
- }
16
- async function editTelegramMenuMessage(state, payload, deps) {
17
- const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
18
- await deps.editInteractiveMessage(state.chatId, state.messageId, appliedPayload.text, appliedPayload.mode, appliedPayload.replyMarkup);
19
- }
20
- function sendTelegramMenuMessage(state, payload, deps) {
21
- const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
22
- return deps.sendInteractiveMessage(state.chatId, appliedPayload.text, appliedPayload.mode, appliedPayload.replyMarkup, state.threadId !== undefined
23
- ? { target: { chatId: state.chatId, threadId: state.threadId } }
24
- : undefined);
13
+ /** Answers and returns true when voice replies or a non-reasoning model disable thinking controls. */
14
+ export async function refuseUnavailableTelegramThinkingControls(callbackQueryId, activeModel, deps) {
15
+ if (deps.isVoiceReplyActive?.()) {
16
+ await deps.answerCallbackQuery(callbackQueryId, "Thinking controls are disabled during voice replies");
17
+ return true;
18
+ }
19
+ if (!activeModel?.reasoning) {
20
+ await deps.answerCallbackQuery(callbackQueryId, "This model has no reasoning controls");
21
+ return true;
22
+ }
23
+ return false;
25
24
  }
26
25
  export async function handleTelegramThinkingMenuCallbackAction(callbackQueryId, data, activeModel, deps) {
27
26
  const action = parseTelegramThinkingMenuCallbackAction(data);
28
27
  if (!action)
29
28
  return false;
30
29
  if (!isThinkingLevel(action.level)) {
31
- await deps.answerCallbackQuery(callbackQueryId, "Invalid thinking level.");
32
- return true;
33
- }
34
- if (deps.isVoiceReplyActive?.()) {
35
- await deps.answerCallbackQuery(callbackQueryId, "Thinking controls are disabled during voice replies.");
30
+ await deps.answerCallbackQuery(callbackQueryId, "Invalid thinking level");
36
31
  return true;
37
32
  }
38
- if (!activeModel?.reasoning) {
39
- await deps.answerCallbackQuery(callbackQueryId, "This model has no reasoning controls.");
33
+ if (await refuseUnavailableTelegramThinkingControls(callbackQueryId, activeModel, deps))
40
34
  return true;
41
- }
42
35
  deps.setThinkingLevel(action.level);
43
36
  await deps.updateThinkingMenuMessage();
44
37
  await deps.answerCallbackQuery(callbackQueryId, `Thinking: ${deps.getCurrentThinkingLevel()}`);
@@ -3,10 +3,10 @@
3
3
  * Zones: telegram ui, controls, status menu
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
- import { type TelegramMenuMessageRuntimeDeps, type TelegramModelMenuState, type TelegramModelMenuStateBuilderContext, type TelegramModelMenuStateBuilderDeps, type TelegramReplyMarkup } from "./menu-model.ts";
6
+ import { type TelegramMenuMessageRuntimeDeps, type TelegramModelMenuState, type TelegramReplyMarkup } from "./menu-model.ts";
7
7
  import { type MenuModel, type ScopedTelegramModel, type TelegramModelSwitchContinuationSource, type ThinkingLevel } from "./model.ts";
8
- import type { TelegramInputRichMessage } from "./telegram-api.ts";
9
8
  import { type TelegramSectionRegistry } from "./sections.ts";
9
+ import type { TelegramApiCallOptions, TelegramInputRichMessage } from "./telegram-api.ts";
10
10
  export { applyTelegramModelPageSelection, applyTelegramModelScopeSelection, buildModelMenuReplyMarkup, buildModelPageMenuReplyMarkup, buildTelegramModelCallbackPlan, buildTelegramModelMenuRenderPayload, buildTelegramModelMenuState, buildTelegramModelMenuStateRuntime, buildTelegramModelPageMenuRenderPayload, createTelegramModelMenuRuntime, createTelegramModelMenuStateBuilder, formatScopedModelButtonText, getModelMenuItems, getStoredTelegramModelMenuState, getTelegramModelMenuPage, getTelegramModelSelection, handleTelegramModelMenuCallbackAction, MODEL_MENU_TITLE, MODEL_PAGE_MENU_TITLE, openTelegramModelMenu, pruneStoredTelegramModelMenus, resolveCachedTelegramModelMenuInputs, sendTelegramModelMenuMessage, storeTelegramModelMenuState, TELEGRAM_MODEL_PAGE_SIZE, updateTelegramModelMenuMessage, } from "./menu-model.ts";
11
11
  export type { BuildTelegramModelCallbackPlanParams, BuildTelegramModelMenuStateParams, CachedTelegramModelMenuInputs, MenuSettingsManager, StoredTelegramModelMenuState, TelegramMenuMessageRuntimeDeps, TelegramMenuMutationResult, TelegramMenuRenderPayload, TelegramMenuSelectionResult, TelegramModelCallbackPlan, TelegramModelMenuCallbackDeps, TelegramModelMenuInputCacheDeps, TelegramModelMenuOpenDeps, TelegramModelMenuPage, TelegramModelMenuRuntime, TelegramModelMenuRuntimeContext, TelegramModelMenuRuntimeOptions, TelegramModelMenuState, TelegramModelMenuStateBuilderContext, TelegramModelMenuStateBuilderDeps, TelegramModelMenuStoreOptions, TelegramModelScope, TelegramReplyMarkup, } from "./menu-model.ts";
12
12
  export { buildStatusReplyMarkup, buildTelegramStatusMenuRenderPayload, handleTelegramStatusMenuCallbackAction, openTelegramStatusMenu, sendTelegramStatusMessage, updateTelegramStatusMessage, } from "./menu-status.ts";
@@ -93,6 +93,7 @@ export interface TelegramMenuActionRuntimeDeps<TContext, TModel extends MenuMode
93
93
  threadId?: number;
94
94
  };
95
95
  parseMode?: "HTML";
96
+ assertAuthority?: TelegramApiCallOptions["assertAuthority"];
96
97
  }) => Promise<unknown>;
97
98
  sectionRegistry?: TelegramSectionRegistry;
98
99
  isVoiceReplyActive?: () => boolean;
@@ -101,70 +102,16 @@ export interface TelegramMenuActionRuntime<TContext, TModel extends MenuModel =
101
102
  updateModelMenuMessage: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
102
103
  updateThinkingMenuMessage: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
103
104
  updateStatusMessage: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
104
- sendStatusMessage: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number) => Promise<void>;
105
- openModelMenu: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number) => Promise<void>;
106
- openThinkingMenu: (chatId: number, replyToMessageId: number, ctx: TContext) => Promise<void>;
105
+ sendStatusMessage: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
106
+ openModelMenu: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
107
+ openThinkingMenu: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
107
108
  }
108
- export type TelegramMenuCallbackAction = {
109
- kind: "ignore";
110
- } | {
111
- kind: "status";
112
- action: "model" | "thinking" | "queue" | "settings";
113
- } | {
114
- kind: "thinking:set";
115
- level: string;
116
- } | {
117
- kind: "model";
118
- action: "noop" | "scope" | "page" | "pages" | "open" | "pick" | "pick-selected" | "scope-enable" | "scope-disable" | "scope-toggle";
119
- value?: string;
120
- };
121
- export declare function parseTelegramMenuCallbackAction(data: string | undefined): TelegramMenuCallbackAction;
122
109
  export declare function handleTelegramMenuCallbackEntry(callbackQueryId: string, data: string | undefined, state: TelegramModelMenuState | undefined, deps: TelegramMenuCallbackEntryDeps): Promise<void>;
123
110
  export declare function handleStoredTelegramMenuCallback<TModel extends MenuModel = MenuModel>(query: MenuCallbackQuery, deps: StoredTelegramMenuCallbackDeps<TModel>): Promise<void>;
124
- export interface TelegramMenuCallbackRuntimeAdapterDeps<TContext, TModel extends MenuModel = MenuModel> {
125
- getStoredModelMenuState: (messageId: number | undefined, chatId?: number) => TelegramModelMenuState<TModel> | undefined;
126
- getActiveModel: (ctx: TContext) => TModel | undefined;
127
- getThinkingLevel: () => ThinkingLevel;
128
- setThinkingLevel: (level: ThinkingLevel) => void;
129
- updateStatus: (ctx: TContext, error?: string) => void;
130
- updateModelMenuMessage: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
131
- updateThinkingMenuMessage: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
132
- updateStatusMessage: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
133
- updateSettingsMenuMessage?: (state: TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
134
- answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
135
- isIdle: (ctx: TContext) => boolean;
136
- hasAbortHandler: () => boolean;
111
+ export type TelegramMenuCallbackRuntimeAdapterDeps<TContext, TModel extends MenuModel = MenuModel> = Omit<TelegramMenuCallbackRuntimeDeps<TContext, TModel>, "hasActiveToolExecutions"> & {
137
112
  getActiveToolExecutions: () => number;
138
- persistScopedModelPatterns?: (patterns: string[], ctx: TContext) => Promise<void>;
139
- setModel: (model: TModel) => Promise<boolean>;
140
- setCurrentModel: (model: TModel, ctx: TContext) => void;
141
- stagePendingModelSwitch: (selection: ScopedTelegramModel<TModel>, ctx: TContext, continuationTurn: TelegramModelSwitchContinuationSource) => void;
142
- restartInterruptedTelegramTurn: (selection: ScopedTelegramModel<TModel>, ctx: TContext, continuationTurn: TelegramModelSwitchContinuationSource) => Promise<boolean> | boolean;
143
- sectionRegistry?: TelegramSectionRegistry;
144
- editInteractiveMessage?: (chatId: number, messageId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramReplyMarkup) => Promise<void>;
145
- sendInteractiveMessage?: (chatId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramReplyMarkup, options?: {
146
- target?: {
147
- chatId: number;
148
- threadId?: number;
149
- };
150
- }) => Promise<number | undefined>;
151
- sendSectionRichMessage?: (chatId: number, message: TelegramInputRichMessage, options?: {
152
- target?: {
153
- chatId: number;
154
- threadId?: number;
155
- };
156
- }) => Promise<number | undefined>;
157
- enqueueSectionPrompt?: (prompt: string, ctx: TContext, target?: {
158
- chatId: number;
159
- threadId?: number;
160
- }, source?: unknown) => Promise<void>;
161
- deleteMessage?: (chatId: number, messageId: number) => Promise<void>;
162
- }
113
+ };
163
114
  export declare function createTelegramMenuCallbackHandler<TQuery extends MenuCallbackQuery, TContext, TModel extends MenuModel = MenuModel>(deps: TelegramMenuCallbackRuntimeDeps<TContext, TModel>): (query: TQuery, ctx: TContext) => Promise<void>;
164
115
  export declare function createTelegramMenuCallbackHandlerForContext<TQuery extends MenuCallbackQuery, TContext, TModel extends MenuModel = MenuModel>(deps: TelegramMenuCallbackRuntimeAdapterDeps<TContext, TModel>): (query: TQuery, ctx: TContext) => Promise<void>;
165
116
  export declare function handleTelegramMenuCallbackRuntime<TQuery extends MenuCallbackQuery, TContext, TModel extends MenuModel = MenuModel>(query: TQuery, ctx: TContext, deps: TelegramMenuCallbackRuntimeDeps<TContext, TModel>): Promise<void>;
166
- export interface TelegramMenuActionRuntimeWithStateBuilderDeps<TModel extends MenuModel = MenuModel, TContext extends TelegramModelMenuStateBuilderContext<TModel> = TelegramModelMenuStateBuilderContext<TModel>> extends Omit<TelegramMenuActionRuntimeDeps<TContext, TModel>, "getModelMenuState">, TelegramModelMenuStateBuilderDeps<TModel, TContext> {
167
- isVoiceReplyActive?: () => boolean;
168
- }
169
- export declare function createTelegramMenuActionRuntimeWithStateBuilder<TModel extends MenuModel = MenuModel, TContext extends TelegramModelMenuStateBuilderContext<TModel> = TelegramModelMenuStateBuilderContext<TModel>>(deps: TelegramMenuActionRuntimeWithStateBuilderDeps<TModel, TContext>): TelegramMenuActionRuntime<TContext, TModel>;
170
117
  export declare function createTelegramMenuActionRuntime<TContext, TModel extends MenuModel = MenuModel>(deps: TelegramMenuActionRuntimeDeps<TContext, TModel>): TelegramMenuActionRuntime<TContext, TModel>;
@@ -3,56 +3,20 @@
3
3
  * Zones: telegram ui, controls, status menu
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
- import { createTelegramModelMenuStateBuilder, handleTelegramModelMenuCallbackAction, openTelegramModelMenu, sendTelegramModelMenuMessage, updateTelegramModelMenuMessage, } from "./menu-model.js";
6
+ import { createTelegramMenuDelivery, handleTelegramModelMenuCallbackAction, openTelegramModelMenu, sendTelegramModelMenuMessage, updateTelegramModelMenuMessage, } from "./menu-model.js";
7
7
  import { handleTelegramStatusMenuCallbackAction, openTelegramStatusMenu, sendTelegramStatusMessage, updateTelegramStatusMessage, } from "./menu-status.js";
8
8
  import { handleTelegramThinkingMenuCallbackAction, openTelegramThinkingMenu, updateTelegramThinkingMenuMessage, } from "./menu-thinking.js";
9
9
  import { handleTelegramSectionCallback, handleTelegramSectionOpen, handleTelegramSectionSettingsOpen, parseTelegramSectionCallback, } from "./sections.js";
10
10
  export { applyTelegramModelPageSelection, applyTelegramModelScopeSelection, buildModelMenuReplyMarkup, buildModelPageMenuReplyMarkup, buildTelegramModelCallbackPlan, buildTelegramModelMenuRenderPayload, buildTelegramModelMenuState, buildTelegramModelMenuStateRuntime, buildTelegramModelPageMenuRenderPayload, createTelegramModelMenuRuntime, createTelegramModelMenuStateBuilder, formatScopedModelButtonText, getModelMenuItems, getStoredTelegramModelMenuState, getTelegramModelMenuPage, getTelegramModelSelection, handleTelegramModelMenuCallbackAction, MODEL_MENU_TITLE, MODEL_PAGE_MENU_TITLE, openTelegramModelMenu, pruneStoredTelegramModelMenus, resolveCachedTelegramModelMenuInputs, sendTelegramModelMenuMessage, storeTelegramModelMenuState, TELEGRAM_MODEL_PAGE_SIZE, updateTelegramModelMenuMessage, } from "./menu-model.js";
11
11
  export { buildStatusReplyMarkup, buildTelegramStatusMenuRenderPayload, handleTelegramStatusMenuCallbackAction, openTelegramStatusMenu, sendTelegramStatusMessage, updateTelegramStatusMessage, } from "./menu-status.js";
12
12
  export { buildTelegramThinkingMenuRenderPayload, buildThinkingMenuReplyMarkup, buildThinkingMenuText, handleTelegramThinkingMenuCallbackAction, openTelegramThinkingMenu, updateTelegramThinkingMenuMessage, } from "./menu-thinking.js";
13
- export function parseTelegramMenuCallbackAction(data) {
14
- if (data === "menu:model" || data === "status:model") {
15
- return { kind: "status", action: "model" };
16
- }
17
- if (data === "menu:thinking" || data === "status:thinking") {
18
- return { kind: "status", action: "thinking" };
19
- }
20
- if (data === "menu:queue" || data === "status:queue") {
21
- return { kind: "status", action: "queue" };
22
- }
23
- if (data === "menu:settings" || data === "status:settings") {
24
- return { kind: "status", action: "settings" };
25
- }
26
- if (data?.startsWith("thinking:set:")) {
27
- return {
28
- kind: "thinking:set",
29
- level: data.slice("thinking:set:".length),
30
- };
31
- }
32
- if (data?.startsWith("model:")) {
33
- const [, action, value] = data.split(":");
34
- if (action === "noop" ||
35
- action === "scope" ||
36
- action === "page" ||
37
- action === "pages" ||
38
- action === "open" ||
39
- action === "pick" ||
40
- action === "pick-selected" ||
41
- action === "scope-enable" ||
42
- action === "scope-disable" ||
43
- action === "scope-toggle") {
44
- return { kind: "model", action, value };
45
- }
46
- }
47
- return { kind: "ignore" };
48
- }
49
13
  export async function handleTelegramMenuCallbackEntry(callbackQueryId, data, state, deps) {
50
14
  if (!data) {
51
15
  await deps.answerCallbackQuery(callbackQueryId);
52
16
  return;
53
17
  }
54
18
  if (!state) {
55
- await deps.answerCallbackQuery(callbackQueryId, "Interactive message expired.");
19
+ await deps.answerCallbackQuery(callbackQueryId, "Interactive message expired");
56
20
  return;
57
21
  }
58
22
  const handled = (await deps.handleStatusAction()) ||
@@ -87,38 +51,17 @@ export function createTelegramMenuCallbackHandler(deps) {
87
51
  return (query, ctx) => handleTelegramMenuCallbackRuntime(query, ctx, deps);
88
52
  }
89
53
  export function createTelegramMenuCallbackHandlerForContext(deps) {
54
+ const { getActiveToolExecutions, ...ports } = deps;
90
55
  return createTelegramMenuCallbackHandler({
91
- getStoredModelMenuState: deps.getStoredModelMenuState,
92
- getActiveModel: deps.getActiveModel,
93
- getThinkingLevel: deps.getThinkingLevel,
94
- setThinkingLevel: deps.setThinkingLevel,
95
- updateStatus: deps.updateStatus,
96
- updateModelMenuMessage: deps.updateModelMenuMessage,
97
- updateThinkingMenuMessage: deps.updateThinkingMenuMessage,
98
- updateStatusMessage: deps.updateStatusMessage,
99
- updateSettingsMenuMessage: deps.updateSettingsMenuMessage,
100
- answerCallbackQuery: deps.answerCallbackQuery,
101
- isIdle: deps.isIdle,
102
- hasAbortHandler: deps.hasAbortHandler,
103
- hasActiveToolExecutions: () => deps.getActiveToolExecutions() > 0,
104
- persistScopedModelPatterns: deps.persistScopedModelPatterns,
105
- setModel: deps.setModel,
106
- setCurrentModel: deps.setCurrentModel,
107
- stagePendingModelSwitch: deps.stagePendingModelSwitch,
108
- restartInterruptedTelegramTurn: deps.restartInterruptedTelegramTurn,
109
- sectionRegistry: deps.sectionRegistry,
110
- editInteractiveMessage: deps.editInteractiveMessage,
111
- sendInteractiveMessage: deps.sendInteractiveMessage,
112
- sendSectionRichMessage: deps.sendSectionRichMessage,
113
- enqueueSectionPrompt: deps.enqueueSectionPrompt,
114
- deleteMessage: deps.deleteMessage,
56
+ ...ports,
57
+ hasActiveToolExecutions: () => getActiveToolExecutions() > 0,
115
58
  });
116
59
  }
117
60
  export async function handleTelegramMenuCallbackRuntime(query, ctx, deps) {
118
61
  if (query.data === "menu:back") {
119
62
  const state = deps.getStoredModelMenuState(query.message?.message_id, query.message?.chat?.id);
120
63
  if (!state) {
121
- await deps.answerCallbackQuery(query.id, "Interactive message expired.");
64
+ await deps.answerCallbackQuery(query.id, "Interactive message expired");
122
65
  return;
123
66
  }
124
67
  await deps.updateStatusMessage(state, ctx);
@@ -139,61 +82,37 @@ export async function handleTelegramMenuCallbackRuntime(query, ctx, deps) {
139
82
  : undefined;
140
83
  if (typeof chatId === "number" && typeof messageId === "number") {
141
84
  const { token, action, payload } = parsed;
85
+ const sectionDeps = {
86
+ answerCallbackQuery: deps.answerCallbackQuery,
87
+ target,
88
+ editInteractiveMessage: deps.editInteractiveMessage ?? (async () => { }),
89
+ sendInteractiveMessage: deps.sendInteractiveMessage ?? (async () => undefined),
90
+ sendRichMessage: deps.sendSectionRichMessage ??
91
+ (async () => {
92
+ throw new Error("Rich Message delivery is unavailable");
93
+ }),
94
+ enqueuePrompt: deps.enqueueSectionPrompt
95
+ ? (prompt) => deps.enqueueSectionPrompt(prompt, ctx, target, query)
96
+ : async () => { },
97
+ deleteMessage: deps.deleteMessage ?? (async () => { }),
98
+ };
142
99
  if (action === "open") {
143
100
  const state = deps.getStoredModelMenuState(messageId, chatId);
144
101
  if (!state) {
145
- await deps.answerCallbackQuery(query.id, "Interactive message expired.");
102
+ await deps.answerCallbackQuery(query.id, "Interactive message expired");
146
103
  return;
147
104
  }
148
- const handled = await handleTelegramSectionOpen(deps.sectionRegistry, token, chatId, messageId, query.id, {
149
- answerCallbackQuery: deps.answerCallbackQuery,
150
- target,
151
- editInteractiveMessage: deps.editInteractiveMessage ?? (async () => { }),
152
- sendInteractiveMessage: deps.sendInteractiveMessage ?? (async () => undefined),
153
- sendRichMessage: deps.sendSectionRichMessage ?? (async () => {
154
- throw new Error("Rich Message delivery is unavailable");
155
- }),
156
- enqueuePrompt: deps.enqueueSectionPrompt
157
- ? (prompt) => deps.enqueueSectionPrompt(prompt, ctx, target, query)
158
- : async () => { },
159
- deleteMessage: deps.deleteMessage ?? (async () => { }),
160
- });
105
+ const handled = await handleTelegramSectionOpen(deps.sectionRegistry, token, chatId, messageId, query.id, sectionDeps);
161
106
  if (handled)
162
107
  return;
163
108
  }
164
109
  else if (action === "settings") {
165
- if (typeof chatId === "number" && typeof messageId === "number") {
166
- const handled = await handleTelegramSectionSettingsOpen(deps.sectionRegistry, token, chatId, messageId, query.id, {
167
- answerCallbackQuery: deps.answerCallbackQuery,
168
- target,
169
- editInteractiveMessage: deps.editInteractiveMessage ?? (async () => { }),
170
- sendInteractiveMessage: deps.sendInteractiveMessage ?? (async () => undefined),
171
- sendRichMessage: deps.sendSectionRichMessage ?? (async () => {
172
- throw new Error("Rich Message delivery is unavailable");
173
- }),
174
- enqueuePrompt: deps.enqueueSectionPrompt
175
- ? (prompt) => deps.enqueueSectionPrompt(prompt, ctx, target, query)
176
- : async () => { },
177
- deleteMessage: deps.deleteMessage ?? (async () => { }),
178
- });
179
- if (handled)
180
- return;
181
- }
110
+ const handled = await handleTelegramSectionSettingsOpen(deps.sectionRegistry, token, chatId, messageId, query.id, sectionDeps);
111
+ if (handled)
112
+ return;
182
113
  }
183
114
  else {
184
- const handled = await handleTelegramSectionCallback(deps.sectionRegistry, token, action, payload, chatId, messageId, query.id, {
185
- answerCallbackQuery: deps.answerCallbackQuery,
186
- target,
187
- editInteractiveMessage: deps.editInteractiveMessage ?? (async () => { }),
188
- sendInteractiveMessage: deps.sendInteractiveMessage ?? (async () => undefined),
189
- sendRichMessage: deps.sendSectionRichMessage ?? (async () => {
190
- throw new Error("Rich Message delivery is unavailable");
191
- }),
192
- enqueuePrompt: deps.enqueueSectionPrompt
193
- ? (prompt) => deps.enqueueSectionPrompt(prompt, ctx, target, query)
194
- : async () => { },
195
- deleteMessage: deps.deleteMessage ?? (async () => { }),
196
- });
115
+ const handled = await handleTelegramSectionCallback(deps.sectionRegistry, token, action, payload, chatId, messageId, query.id, sectionDeps);
197
116
  if (handled)
198
117
  return;
199
118
  }
@@ -257,67 +176,76 @@ export async function handleTelegramMenuCallbackRuntime(query, ctx, deps) {
257
176
  answerCallbackQuery: deps.answerCallbackQuery,
258
177
  });
259
178
  }
260
- export function createTelegramMenuActionRuntimeWithStateBuilder(deps) {
261
- return createTelegramMenuActionRuntime({
262
- getModelMenuState: createTelegramModelMenuStateBuilder({
263
- runtime: deps.runtime,
264
- createSettingsManager: deps.createSettingsManager,
265
- getActiveModel: deps.getActiveModel,
266
- }),
267
- getActiveModel: deps.getActiveModel,
268
- getThinkingLevel: deps.getThinkingLevel,
269
- getQueueItemCount: deps.getQueueItemCount,
270
- getPendingCancellationCount: deps.getPendingCancellationCount,
271
- buildStatusHtml: deps.buildStatusHtml,
272
- storeModelMenuState: deps.storeModelMenuState,
273
- isIdle: deps.isIdle,
274
- canOfferInFlightModelSwitch: deps.canOfferInFlightModelSwitch,
275
- sendTextReply: deps.sendTextReply,
276
- editInteractiveMessage: deps.editInteractiveMessage,
277
- sendInteractiveMessage: deps.sendInteractiveMessage,
278
- sectionRegistry: deps.sectionRegistry,
279
- });
280
- }
281
179
  export function createTelegramMenuActionRuntime(deps) {
282
180
  return {
283
181
  updateModelMenuMessage: (state, ctx) => updateTelegramModelMenuMessage(state, deps.getActiveModel(ctx), deps),
284
182
  updateThinkingMenuMessage: (state, ctx) => updateTelegramThinkingMenuMessage(state, deps.getActiveModel(ctx), deps.getThinkingLevel(), deps),
285
183
  updateStatusMessage: (state, ctx) => updateTelegramStatusMessage(state, deps.buildStatusHtml(ctx), deps.getActiveModel(ctx), deps.getThinkingLevel(), deps, deps.getQueueItemCount?.() ?? 0, deps.sectionRegistry, deps.isVoiceReplyActive?.(), deps.getPendingCancellationCount?.() ?? 0),
286
- sendStatusMessage: (chatId, replyToMessageId, ctx, threadId) => openTelegramStatusMenu({
287
- isIdle: () => deps.isIdle(ctx),
288
- sendBusyMessage: async () => {
289
- await deps.sendTextReply(chatId, replyToMessageId, "<b>⏳ Cannot open status while Pi is busy. Send /abort, /next, or /stop.</b>", { target: { chatId, threadId }, parseMode: "HTML" });
290
- },
291
- getModelMenuState: () => deps.getModelMenuState(chatId, ctx, threadId),
292
- buildStatusHtml: () => deps.buildStatusHtml(ctx),
293
- getActiveModel: () => deps.getActiveModel(ctx),
294
- getThinkingLevel: deps.getThinkingLevel,
295
- getQueueItemCount: deps.getQueueItemCount,
296
- sendStatusMenu: (state, statusHtml, activeModel, thinkingLevel, queueItemCount) => sendTelegramStatusMessage(state, statusHtml, activeModel, thinkingLevel, deps, queueItemCount, deps.sectionRegistry, deps.isVoiceReplyActive?.(), deps.getPendingCancellationCount?.() ?? 0),
297
- storeModelMenuState: deps.storeModelMenuState,
298
- }),
299
- openModelMenu: (chatId, replyToMessageId, ctx, threadId) => openTelegramModelMenu({
300
- isIdle: () => deps.isIdle(ctx),
301
- canOfferInFlightModelSwitch: () => deps.canOfferInFlightModelSwitch(ctx),
302
- sendBusyMessage: async () => {
303
- await deps.sendTextReply(chatId, replyToMessageId, "<b>⏳ Cannot switch model while Pi is busy. Send /abort, /next, or /stop.</b>", { target: { chatId, threadId }, parseMode: "HTML" });
304
- },
305
- sendNoModelsMessage: async () => {
306
- await deps.sendTextReply(chatId, replyToMessageId, "<b>🚫 No available models with configured auth.</b>", { target: { chatId, threadId }, parseMode: "HTML" });
307
- },
308
- getModelMenuState: () => deps.getModelMenuState(chatId, ctx, threadId),
309
- getActiveModel: () => deps.getActiveModel(ctx),
310
- sendModelMenu: (state, activeModel) => sendTelegramModelMenuMessage(state, activeModel, deps),
311
- storeModelMenuState: deps.storeModelMenuState,
312
- }),
313
- openThinkingMenu: (chatId, _replyToMessageId, ctx) => openTelegramThinkingMenu({
314
- getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
315
- getActiveModel: () => deps.getActiveModel(ctx),
316
- getThinkingLevel: deps.getThinkingLevel,
317
- storeModelMenuState: deps.storeModelMenuState,
318
- editInteractiveMessage: deps.editInteractiveMessage,
319
- sendInteractiveMessage: deps.sendInteractiveMessage,
320
- isVoiceReplyActive: deps.isVoiceReplyActive,
321
- }),
184
+ sendStatusMessage: (chatId, replyToMessageId, ctx, threadId, options) => {
185
+ const delivery = createTelegramMenuDelivery({ chatId, threadId }, options, deps);
186
+ return openTelegramStatusMenu({
187
+ isIdle: () => deps.isIdle(ctx),
188
+ sendBusyMessage: async () => {
189
+ await deps.sendTextReply(chatId, replyToMessageId, "<b>⏳ Cannot open status while Pi is busy. Send /abort, /next, or /stop.</b>", {
190
+ target: { chatId, threadId },
191
+ parseMode: "HTML",
192
+ ...(delivery.assertAuthority
193
+ ? { assertAuthority: delivery.assertAuthority }
194
+ : {}),
195
+ });
196
+ },
197
+ getModelMenuState: () => deps.getModelMenuState(chatId, ctx, threadId),
198
+ buildStatusHtml: () => deps.buildStatusHtml(ctx),
199
+ getActiveModel: () => deps.getActiveModel(ctx),
200
+ getThinkingLevel: deps.getThinkingLevel,
201
+ getQueueItemCount: deps.getQueueItemCount,
202
+ sendStatusMenu: (state, statusHtml, activeModel, thinkingLevel, queueItemCount) => sendTelegramStatusMessage(state, statusHtml, activeModel, thinkingLevel, {
203
+ ...deps,
204
+ sendInteractiveMessage: delivery.sendInteractiveMessage,
205
+ }, queueItemCount, deps.sectionRegistry, deps.isVoiceReplyActive?.(), deps.getPendingCancellationCount?.() ?? 0),
206
+ storeModelMenuState: delivery.storeModelMenuState,
207
+ });
208
+ },
209
+ openModelMenu: (chatId, replyToMessageId, ctx, threadId, options) => {
210
+ const delivery = createTelegramMenuDelivery({ chatId, threadId }, options, deps);
211
+ const assertAuthority = delivery.assertAuthority;
212
+ return openTelegramModelMenu({
213
+ isIdle: () => deps.isIdle(ctx),
214
+ canOfferInFlightModelSwitch: () => deps.canOfferInFlightModelSwitch(ctx),
215
+ sendBusyMessage: async () => {
216
+ await deps.sendTextReply(chatId, replyToMessageId, "<b>⏳ Cannot switch model while Pi is busy. Send /abort, /next, or /stop.</b>", {
217
+ target: { chatId, threadId },
218
+ parseMode: "HTML",
219
+ ...(assertAuthority ? { assertAuthority } : {}),
220
+ });
221
+ },
222
+ sendNoModelsMessage: async () => {
223
+ await deps.sendTextReply(chatId, replyToMessageId, "<b>🚫 No available models with configured auth.</b>", {
224
+ target: { chatId, threadId },
225
+ parseMode: "HTML",
226
+ ...(assertAuthority ? { assertAuthority } : {}),
227
+ });
228
+ },
229
+ getModelMenuState: () => deps.getModelMenuState(chatId, ctx, threadId),
230
+ getActiveModel: () => deps.getActiveModel(ctx),
231
+ sendModelMenu: (state, activeModel) => sendTelegramModelMenuMessage(state, activeModel, {
232
+ ...deps,
233
+ sendInteractiveMessage: delivery.sendInteractiveMessage,
234
+ }),
235
+ storeModelMenuState: delivery.storeModelMenuState,
236
+ });
237
+ },
238
+ openThinkingMenu: (chatId, _replyToMessageId, ctx, threadId, options) => {
239
+ const delivery = createTelegramMenuDelivery({ chatId, threadId }, options, deps);
240
+ return openTelegramThinkingMenu({
241
+ getModelMenuState: () => deps.getModelMenuState(chatId, ctx, threadId),
242
+ getActiveModel: () => deps.getActiveModel(ctx),
243
+ getThinkingLevel: deps.getThinkingLevel,
244
+ storeModelMenuState: delivery.storeModelMenuState,
245
+ editInteractiveMessage: deps.editInteractiveMessage,
246
+ sendInteractiveMessage: delivery.sendInteractiveMessage,
247
+ isVoiceReplyActive: deps.isVoiceReplyActive,
248
+ });
249
+ },
322
250
  };
323
251
  }
@@ -3,7 +3,7 @@
3
3
  * Zones: pi agent model control, telegram controls, queue continuation
4
4
  * Owns model identity, thinking levels, scoped resolution, current-model state, and in-flight model switching
5
5
  */
6
- import type { PendingTelegramTurn } from "./queue.ts";
6
+ import { type PendingTelegramTurn } from "./queue.ts";
7
7
  export interface MenuModel {
8
8
  provider: string;
9
9
  id: string;
@@ -3,6 +3,7 @@
3
3
  * Zones: pi agent model control, telegram controls, queue continuation
4
4
  * Owns model identity, thinking levels, scoped resolution, current-model state, and in-flight model switching
5
5
  */
6
+ import { truncateTelegramQueueSummary, } from "./queue.js";
6
7
  import { TELEGRAM_PREFIX } from "./turns.js";
7
8
  export const THINKING_LEVELS = [
8
9
  "off",
@@ -277,21 +278,6 @@ export function restartTelegramModelSwitchContinuation(state) {
277
278
  state.abort();
278
279
  return true;
279
280
  }
280
- function truncateTelegramModelSwitchStatusSummary(text, maxWords = 4, maxLength = 32) {
281
- const normalized = text.replace(/\s+/g, " ").trim();
282
- if (!normalized)
283
- return "";
284
- const words = normalized.split(" ");
285
- let summary = words.slice(0, maxWords).join(" ");
286
- if (summary.length === 0)
287
- summary = normalized;
288
- if (summary.length > maxLength) {
289
- summary = summary.slice(0, maxLength).trimEnd();
290
- }
291
- return summary.length < normalized.length || words.length > maxWords
292
- ? `${summary}…`
293
- : summary;
294
- }
295
281
  export function buildTelegramModelSwitchContinuationText(telegramPrefix, model, thinkingLevel) {
296
282
  const modelLabel = `${model.provider}/${model.id}`;
297
283
  const thinkingSuffix = thinkingLevel ? `; thinking: ${thinkingLevel}` : "";
@@ -299,7 +285,7 @@ export function buildTelegramModelSwitchContinuationText(telegramPrefix, model,
299
285
  }
300
286
  export function buildTelegramModelSwitchContinuationTurn(options) {
301
287
  const modelLabel = `${options.selection.model.provider}/${options.selection.model.id}`;
302
- const statusLabel = truncateTelegramModelSwitchStatusSummary(`continue on ${options.selection.model.id}`);
288
+ const statusLabel = truncateTelegramQueueSummary(`continue on ${options.selection.model.id}`, 4, 32);
303
289
  return {
304
290
  kind: "prompt",
305
291
  chatId: options.turn.chatId,