@llblab/pi-kit 0.29.1 → 0.30.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 (222) hide show
  1. package/BACKLOG.md +3 -1
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +4 -4
  4. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +12 -0
  5. package/node_modules/@llblab/pi-state-flow/README.md +2 -2
  6. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +2 -2
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +17 -8
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +7 -3
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +2 -1
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +3 -2
  11. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  12. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -3
  13. package/node_modules/@llblab/pi-state-flow/docs/usage.md +3 -2
  14. package/node_modules/@llblab/pi-state-flow/lib/context.ts +17 -8
  15. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +6 -3
  16. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +4 -2
  17. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  18. package/node_modules/@llblab/pi-telegram/AGENTS.md +13 -10
  19. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -1
  20. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +23 -0
  21. package/node_modules/@llblab/pi-telegram/README.md +10 -11
  22. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  23. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  24. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  25. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  26. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  28. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  29. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  30. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +164 -20
  31. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  32. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +181 -77
  33. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  34. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  35. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  36. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  37. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  38. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  39. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  40. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  42. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  43. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  44. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  45. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  46. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  47. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  48. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  49. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  50. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +642 -170
  51. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  52. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  53. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  54. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  55. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  56. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +40 -4
  57. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2102 -760
  58. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  59. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  60. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +34 -3
  61. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +318 -119
  62. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  63. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  65. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  66. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  67. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  68. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  69. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  70. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  71. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  72. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  73. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  74. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  75. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  76. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  77. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  79. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  80. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  81. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  82. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  83. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  84. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  85. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  86. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +61 -51
  87. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  88. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  89. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  90. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  91. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  92. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +23 -37
  93. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +71 -52
  94. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  95. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  96. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  97. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  98. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +16 -16
  99. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +261 -116
  100. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  101. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  102. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  103. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  104. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +121 -7
  105. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4750 -1161
  106. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  107. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  108. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  109. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  110. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  111. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +6 -3
  112. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +112 -45
  113. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  114. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  115. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -11
  116. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +165 -49
  117. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  118. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  119. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  120. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  121. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  122. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  123. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  124. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  125. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  126. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +88 -24
  127. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2276 -842
  128. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  129. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  130. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +178 -82
  131. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2438 -645
  132. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  133. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  134. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  135. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  136. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  137. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  138. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  139. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  140. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  141. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  142. package/node_modules/@llblab/pi-telegram/docs/architecture.md +43 -35
  143. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  144. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  145. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  146. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  147. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  148. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  149. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  150. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  151. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +390 -82
  152. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +262 -99
  153. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  154. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  155. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  156. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  157. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  158. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  159. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  160. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  161. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  162. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1209 -431
  163. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  164. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  165. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  166. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5329 -1712
  167. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  168. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  169. package/node_modules/@llblab/pi-telegram/lib/locks.ts +703 -212
  170. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  171. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  172. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  173. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  174. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  175. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  176. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  177. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  178. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  179. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  180. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  181. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  182. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  183. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +125 -125
  184. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  185. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  186. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  187. package/node_modules/@llblab/pi-telegram/lib/polling.ts +165 -149
  188. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  189. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  190. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  191. package/node_modules/@llblab/pi-telegram/lib/queue.ts +523 -289
  192. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  193. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  194. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  195. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8527 -2149
  196. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  197. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  198. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  199. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  200. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  201. package/node_modules/@llblab/pi-telegram/lib/sync.ts +488 -359
  202. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  203. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +322 -122
  204. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  205. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  206. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  207. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  208. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  209. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5329 -1817
  210. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  211. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  212. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5194 -1600
  213. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  214. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  215. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  216. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  217. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  218. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  219. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  220. package/package.json +3 -3
  221. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  222. 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,