@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
@@ -91,16 +91,14 @@ function createTelegramActivityContext(event, isActive) {
91
91
  };
92
92
  }
93
93
  function canCoalesceActivityEvents(previous, next) {
94
- if (previous.activityId !== next.activityId ||
95
- previous.type !== next.type) {
94
+ if (previous.activityId !== next.activityId || previous.type !== next.type) {
96
95
  return false;
97
96
  }
98
97
  if (previous.type === "assistant-text-delta" &&
99
98
  next.type === "assistant-text-delta") {
100
99
  return previous.contentIndex === next.contentIndex;
101
100
  }
102
- if (previous.type === "reasoning-delta" &&
103
- next.type === "reasoning-delta") {
101
+ if (previous.type === "reasoning-delta" && next.type === "reasoning-delta") {
104
102
  return previous.contentIndex === next.contentIndex;
105
103
  }
106
104
  if (previous.type === "tool-update" && next.type === "tool-update") {
@@ -113,8 +111,7 @@ function coalesceActivityEvents(previous, next) {
113
111
  next.type === "assistant-text-delta") {
114
112
  return { ...next, delta: previous.delta + next.delta };
115
113
  }
116
- if (previous.type === "reasoning-delta" &&
117
- next.type === "reasoning-delta") {
114
+ if (previous.type === "reasoning-delta" && next.type === "reasoning-delta") {
118
115
  return { ...next, delta: previous.delta + next.delta };
119
116
  }
120
117
  return next;
@@ -152,6 +149,7 @@ export function createTelegramActivityDispatcher(deps = {}) {
152
149
  }
153
150
  };
154
151
  return {
152
+ hasPending: () => Array.from(queues.values()).some((queue) => queue.active && (queue.running || queue.events.length > 0)),
155
153
  dispatch(event) {
156
154
  if (stopped)
157
155
  return;
@@ -194,6 +192,7 @@ export function createTelegramActivityBridgeRuntime(deps) {
194
192
  let runtime;
195
193
  const getRuntime = () => runtime;
196
194
  return {
195
+ hasPending: () => getRuntime()?.hasPending?.(),
197
196
  onSessionStart() {
198
197
  runtime?.onSessionShutdown();
199
198
  runtime = createTelegramActivityRuntime({
@@ -298,7 +297,9 @@ export function createTelegramActivityRuntime(deps) {
298
297
  sequence,
299
298
  source: activitySource,
300
299
  ...(activityTarget ? { target: activityTarget } : {}),
301
- ...(activityReplyToMessageId !== undefined ? { replyToMessageId: activityReplyToMessageId } : {}),
300
+ ...(activityReplyToMessageId !== undefined
301
+ ? { replyToMessageId: activityReplyToMessageId }
302
+ : {}),
302
303
  timestamp: now(),
303
304
  };
304
305
  try {
@@ -342,13 +343,15 @@ export function createTelegramActivityRuntime(deps) {
342
343
  clearActivity();
343
344
  };
344
345
  return {
346
+ hasPending: () => deps.dispatcher.hasPending?.(),
345
347
  recordInputSource(source) {
346
348
  pendingInputSource = source;
347
349
  },
348
350
  onAgentStart(activeTelegramTarget, replyToMessageId) {
349
351
  abandonCompaction();
350
352
  ensureActivity(activeTelegramTarget);
351
- activityReplyToMessageId = activitySource === "telegram" ? replyToMessageId : undefined;
353
+ activityReplyToMessageId =
354
+ activitySource === "telegram" ? replyToMessageId : undefined;
352
355
  emit({ type: "agent-start" });
353
356
  },
354
357
  onAssistantEvent(event) {
@@ -470,12 +473,15 @@ export function createTelegramActivityRuntime(deps) {
470
473
  export function createTelegramActivityPublicationRuntime() {
471
474
  let generation = 0;
472
475
  let tail = Promise.resolve();
476
+ let outstanding = 0, unconfirmed = false;
473
477
  const pending = new Set();
474
478
  const reserve = () => {
475
479
  const admittedGeneration = generation;
476
480
  let state = "pending";
477
481
  let resolve;
478
- const ready = new Promise((accept) => { resolve = accept; });
482
+ const ready = new Promise((accept) => {
483
+ resolve = accept;
484
+ });
479
485
  const cancel = () => {
480
486
  if (state !== "pending")
481
487
  return;
@@ -484,10 +490,15 @@ export function createTelegramActivityPublicationRuntime() {
484
490
  resolve(undefined);
485
491
  };
486
492
  pending.add(cancel);
487
- const result = tail.then(async () => {
493
+ outstanding += 1;
494
+ const result = tail
495
+ .then(async () => {
488
496
  const task = await ready;
489
497
  if (admittedGeneration === generation && task)
490
498
  await task();
499
+ })
500
+ .finally(() => {
501
+ outstanding -= 1;
491
502
  });
492
503
  tail = result.catch(() => { });
493
504
  return {
@@ -507,8 +518,26 @@ export function createTelegramActivityPublicationRuntime() {
507
518
  return {
508
519
  reserve,
509
520
  enqueue: (task) => reserve().publish(task),
521
+ hasPending: () => outstanding > 0,
522
+ hasUnconfirmed: () => unconfirmed,
523
+ beginWork() {
524
+ const admittedGeneration = generation;
525
+ let settled = false;
526
+ outstanding += 1;
527
+ return {
528
+ settle(outcome) {
529
+ if (settled)
530
+ return;
531
+ settled = true;
532
+ outstanding -= 1;
533
+ if (outcome === "unconfirmed" && admittedGeneration === generation)
534
+ unconfirmed = true;
535
+ },
536
+ };
537
+ },
510
538
  reset() {
511
539
  generation += 1;
540
+ unconfirmed = false;
512
541
  for (const cancel of pending)
513
542
  cancel();
514
543
  tail = Promise.resolve();
@@ -12,7 +12,10 @@ export function createTelegramAgentMessageRuntime(deps) {
12
12
  const leaderTarget = deps.getLeaderTarget();
13
13
  if (leaderTarget?.threadId) {
14
14
  targets.push({
15
- target: { chatId: leaderTarget.chatId, threadId: leaderTarget.threadId },
15
+ target: {
16
+ chatId: leaderTarget.chatId,
17
+ threadId: leaderTarget.threadId,
18
+ },
16
19
  threadName: deps.getDisplayTitle?.(leaderTarget) ?? deps.getLeaderThreadName(),
17
20
  });
18
21
  }
@@ -65,8 +68,9 @@ export function createTelegramAgentMessageRuntime(deps) {
65
68
  if (allowedChatId === undefined || !ctx) {
66
69
  throw new Error("Telegram agent turn routing is unavailable.");
67
70
  }
68
- const sourceTitle = (input.sourceTarget ? deps.getDisplayTitle?.(input.sourceTarget) : undefined)
69
- ?? input.sourceThreadName;
71
+ const sourceTitle = (input.sourceTarget
72
+ ? deps.getDisplayTitle?.(input.sourceTarget)
73
+ : undefined) ?? input.sourceThreadName;
70
74
  const sourceLabel = sourceTitle
71
75
  ?.replace(/[\r\n\[\]]+/g, " ")
72
76
  .trim()
@@ -3,14 +3,20 @@
3
3
  * Zones: telegram, pi agent, orchestration
4
4
  * Owns pi-facing tool, command, and lifecycle hook registration for the entrypoint
5
5
  */
6
- import * as Activity from "./activity.ts";
7
6
  import * as ActivityVerbosity from "./activity-verbosity.ts";
7
+ import * as Activity from "./activity.ts";
8
+ import * as BusApi from "./bus-api.ts";
9
+ import type { TelegramBusFollowerCommandOwner } from "./bus-follower.ts";
10
+ import type { TelegramBusLiveRebindWorkState } from "./bus.ts";
8
11
  import * as ChannelPosts from "./channel-posts.ts";
9
12
  import * as Commands from "./commands.ts";
10
13
  import * as Config from "./config.ts";
14
+ import * as Delivery from "./delivery.ts";
15
+ import * as GenerativeApps from "./generative-apps.ts";
11
16
  import * as Keyboard from "./keyboard.ts";
12
17
  import * as Lifecycle from "./lifecycle.ts";
13
18
  import * as Locks from "./locks.ts";
19
+ import * as Menu from "./menu.ts";
14
20
  import * as Model from "./model.ts";
15
21
  import * as OutboundAttachments from "./outbound-attachments.ts";
16
22
  import * as OutboundHandlers from "./outbound.ts";
@@ -18,13 +24,28 @@ import * as Pi from "./pi.ts";
18
24
  import * as Preview from "./preview.ts";
19
25
  import * as Prompts from "./prompts.ts";
20
26
  import * as Queue from "./queue.ts";
27
+ import * as Replies from "./replies.ts";
21
28
  import * as Routing from "./routing.ts";
22
29
  import * as Runtime from "./runtime.ts";
23
30
  import * as Setup from "./setup.ts";
24
31
  import * as Status from "./status.ts";
25
- import * as TelegramApi from "./telegram-api.ts";
26
32
  import type { TelegramTarget } from "./target.ts";
27
- import * as GenerativeApps from "./generative-apps.ts";
33
+ import * as TelegramApi from "./telegram-api.ts";
34
+ /** Compose any registered held command over captured recipient effects; no ordinary follower API fallback. */
35
+ export declare function createTelegramFollowerSelectedCommandBinding<TContext, TModel extends Model.MenuModel = Model.MenuModel>(deps: {
36
+ ctx: TContext;
37
+ operationId: string;
38
+ registrationGeneration: string;
39
+ name: string;
40
+ target: TelegramTarget & {
41
+ threadId: number;
42
+ };
43
+ recipient: Pick<TelegramBusFollowerCommandOwner<TContext>, "isCurrent" | "assertRecipientCurrent">;
44
+ command: Pick<ReturnType<typeof Commands.createTelegramCommandHandlerTargetRuntime<Commands.TelegramCommandRuntimeMessage, TContext>>, "prepareHeldCommand" | "canPrepareHeldCommand">;
45
+ menu?: Omit<Menu.TelegramMenuActionRuntimeDeps<TContext, TModel>, "sendTextReply" | "sendInteractiveMessage" | "editInteractiveMessage">;
46
+ deliver: Parameters<typeof BusApi.createTelegramSelectedMenuTextApi>[0]["deliver"];
47
+ recordOwnership: NonNullable<Replies.TelegramRenderedMessageDeliveryRuntimeDeps<Menu.TelegramReplyMarkup>["recordOwnership"]>;
48
+ }): TelegramBusFollowerCommandOwner<TContext> | undefined;
28
49
  type ActivePiModel = NonNullable<Pi.ExtensionContext["model"]>;
29
50
  type TelegramRuntimeEventRecorder = (category: string, error: unknown, details?: Record<string, unknown>) => void;
30
51
  type TelegramBridgeStatusUpdater = Status.TelegramStatusRuntime<Pi.ExtensionContext>["updateStatus"];
@@ -139,6 +160,9 @@ type TelegramAssistantOutputAuthority<TTransportStamp> = ReturnType<Routing.Tele
139
160
  export interface TelegramBridgePublicationRuntime {
140
161
  enqueue: Activity.TelegramActivityPublicationRuntime["enqueue"];
141
162
  reserve: Activity.TelegramActivityPublicationRuntime["reserve"];
163
+ hasPending: Activity.TelegramActivityPublicationRuntime["hasPending"];
164
+ beginWork: Activity.TelegramActivityPublicationRuntime["beginWork"];
165
+ hasUnconfirmed: Activity.TelegramActivityPublicationRuntime["hasUnconfirmed"];
142
166
  capture: () => {
143
167
  target?: Queue.TelegramQueueTarget;
144
168
  isCurrent: () => boolean;
@@ -156,6 +180,21 @@ export declare function createTelegramActivityBindingRuntime<TTransportStamp>(de
156
180
  assistantOutput: Omit<Parameters<typeof createTelegramAssistantOutputBindingRuntime<TTransportStamp>>[0], "waitForActivityIdle" | "enqueue">;
157
181
  activityVerbosity: Omit<Parameters<typeof ActivityVerbosity.createTelegramActivityVerbosityRuntime<TelegramAssistantOutputAuthority<TTransportStamp>>>[0], "captureAuthority" | "isAuthorityActive" | "recordFailure" | "enqueue">;
158
182
  }): TelegramActivityBindingRuntime;
183
+ /** Fresh current work projection; a snapshot is not a cleanup grant or target-application ACK. */
184
+ export declare function createTelegramLiveTargetWorkObserver<TContext>(deps: {
185
+ queue: Pick<Queue.TelegramQueueStateStore<TContext>, "getQueuedItems">;
186
+ activeTurn: Pick<Queue.TelegramActiveTurnStore, "get">;
187
+ lifecycle: Pick<Runtime.TelegramBridgeRuntime["lifecycle"], "hasDispatchPending" | "isCompactionInProgress" | "getActiveToolExecutions">;
188
+ isIdle(ctx: TContext): boolean;
189
+ hasPendingMessages(ctx: TContext): boolean;
190
+ hasPendingControl(): boolean;
191
+ publication: Pick<TelegramBridgePublicationRuntime, "hasPending" | "hasUnconfirmed">;
192
+ activity: Pick<Activity.TelegramActivityRuntime, "hasPending">;
193
+ delivery: Pick<ReturnType<typeof Delivery.createTelegramDeliveryLifecycleHooks>, "hasPendingTarget">;
194
+ api: Pick<TelegramApi.TelegramApiTargetActivityRuntime, "hasPendingTarget">;
195
+ }): (target: TelegramTarget & {
196
+ threadId: number;
197
+ }, ctx: TContext) => TelegramBusLiveRebindWorkState;
159
198
  interface TelegramCommandsAndToolsBindingDeps {
160
199
  pi: Pi.ExtensionAPI;
161
200
  agentDir: string;
@@ -165,7 +204,6 @@ interface TelegramCommandsAndToolsBindingDeps {
165
204
  activeTurnRuntime: Queue.TelegramActiveTurnStore<Queue.PendingTelegramTurn>;
166
205
  lockedPollingRuntime: Locks.TelegramLockedPollingRuntime<Pi.ExtensionContext>;
167
206
  stopPolling?: () => Promise<void | string>;
168
- recoverPollingStart?: Commands.TelegramBridgeCommandRegistrationDeps["recoverPollingStart"];
169
207
  getDisconnectThreadName?: () => string | undefined;
170
208
  onTransportChanged?: () => Promise<void> | void;
171
209
  getStatusLines: (options?: Status.TelegramBridgeStatusLineOptions) => string[];
@@ -199,7 +237,7 @@ interface TelegramCommandsAndToolsBindingDeps {
199
237
  connectionIntent: NonNullable<Commands.TelegramBridgeCommandRegistrationDeps["connectionIntent"]>;
200
238
  recordRuntimeEvent: TelegramRuntimeEventRecorder;
201
239
  }
202
- export declare function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, recoverPollingStart, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, isContextCurrent, getSessionGeneration, connectionIntent, }: TelegramCommandsAndToolsBindingDeps): void;
240
+ export declare function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, isContextCurrent, getSessionGeneration, connectionIntent, }: TelegramCommandsAndToolsBindingDeps): void;
203
241
  interface TelegramLifecycleBindingDeps {
204
242
  pi: Pi.ExtensionAPI;
205
243
  publicationRuntime: TelegramBridgePublicationRuntime;
@@ -3,14 +3,17 @@
3
3
  * Zones: telegram, pi agent, orchestration
4
4
  * Owns pi-facing tool, command, and lifecycle hook registration for the entrypoint
5
5
  */
6
- import * as Activity from "./activity.js";
7
6
  import * as ActivityVerbosity from "./activity-verbosity.js";
7
+ import * as Activity from "./activity.js";
8
+ import * as BusApi from "./bus-api.js";
8
9
  import * as ChannelPosts from "./channel-posts.js";
9
10
  import * as CommandTemplates from "./command-templates.js";
10
11
  import * as Commands from "./commands.js";
11
12
  import * as Config from "./config.js";
12
13
  import * as Delivery from "./delivery.js";
14
+ import * as GenerativeApps from "./generative-apps.js";
13
15
  import * as Lifecycle from "./lifecycle.js";
16
+ import * as Menu from "./menu.js";
14
17
  import * as OutboundAttachments from "./outbound-attachments.js";
15
18
  import * as OutboundHandlers from "./outbound.js";
16
19
  import * as Pi from "./pi.js";
@@ -21,7 +24,91 @@ import * as Routing from "./routing.js";
21
24
  import * as Runtime from "./runtime.js";
22
25
  import * as Setup from "./setup.js";
23
26
  import * as TelegramApi from "./telegram-api.js";
24
- import * as GenerativeApps from "./generative-apps.js";
27
+ /** Compose any registered held command over captured recipient effects; no ordinary follower API fallback. */
28
+ export function createTelegramFollowerSelectedCommandBinding(deps) {
29
+ const { ctx, operationId, registrationGeneration, name, recipient, command, deliver, recordOwnership, } = deps, target = { ...deps.target };
30
+ const prepare = command.prepareHeldCommand, canPrepare = command.canPrepareHeldCommand;
31
+ const isCurrent = recipient.isCurrent, assertRecipientCurrent = recipient.assertRecipientCurrent, menu = deps.menu, menuPorts = menu && { ...menu };
32
+ if ([
33
+ prepare,
34
+ canPrepare,
35
+ isCurrent,
36
+ assertRecipientCurrent,
37
+ deliver,
38
+ recordOwnership,
39
+ ].some((port) => typeof port !== "function") ||
40
+ (menuPorts &&
41
+ [
42
+ menuPorts.getModelMenuState,
43
+ menuPorts.storeModelMenuState,
44
+ menuPorts.getActiveModel,
45
+ menuPorts.getThinkingLevel,
46
+ menuPorts.buildStatusHtml,
47
+ menuPorts.isIdle,
48
+ menuPorts.canOfferInFlightModelSwitch,
49
+ ].some((port) => typeof port !== "function")))
50
+ return undefined;
51
+ const portsCurrent = () => deps.ctx === ctx &&
52
+ deps.operationId === operationId &&
53
+ deps.registrationGeneration === registrationGeneration &&
54
+ deps.name === name &&
55
+ deps.recipient === recipient &&
56
+ recipient.isCurrent === isCurrent &&
57
+ recipient.assertRecipientCurrent === assertRecipientCurrent &&
58
+ deps.command === command &&
59
+ command.prepareHeldCommand === prepare &&
60
+ command.canPrepareHeldCommand === canPrepare &&
61
+ deps.deliver === deliver &&
62
+ deps.recordOwnership === recordOwnership &&
63
+ deps.menu === menu &&
64
+ (!menuPorts ||
65
+ Object.entries(menuPorts).every(([key, value]) => Reflect.get(menu, key) === value));
66
+ const current = () => portsCurrent() && isCurrent.call(recipient) && portsCurrent();
67
+ if (!current())
68
+ return undefined;
69
+ const api = BusApi.createTelegramSelectedMenuTextApi({
70
+ operationId,
71
+ registrationGeneration,
72
+ target,
73
+ assertAuthority: assertRecipientCurrent,
74
+ deliver,
75
+ });
76
+ const rendered = Replies.createTelegramRenderedMessageDeliveryRuntime({
77
+ sendMessage: api.sendMessage,
78
+ editMessage: api.editMessageText,
79
+ recordOwnership,
80
+ async sendRichMessage() {
81
+ throw new Error("Selected command has no rich or ordinary API fallback.");
82
+ },
83
+ });
84
+ const menus = menuPorts &&
85
+ Menu.createTelegramMenuActionRuntime({
86
+ ...menuPorts,
87
+ sendTextReply: rendered.sendTextReply,
88
+ sendInteractiveMessage: rendered.sendInteractiveMessage,
89
+ editInteractiveMessage: rendered.editInteractiveMessage,
90
+ });
91
+ const effects = {
92
+ sendTextReply: rendered.sendTextReply,
93
+ ...(menus ? { showStatus: menus.sendStatusMessage } : {}),
94
+ };
95
+ if (!canPrepare.call(command, name, effects) || !current())
96
+ return undefined;
97
+ return {
98
+ isCurrent: current,
99
+ assertRecipientCurrent,
100
+ prepare(readiness, context, input) {
101
+ if (!current() ||
102
+ context !== ctx ||
103
+ input.target.chatId !== target.chatId ||
104
+ input.target.threadId !== target.threadId ||
105
+ input.assertRecipientCurrent !== assertRecipientCurrent)
106
+ return undefined;
107
+ const plan = prepare.call(command, name, readiness, context, input, effects);
108
+ return current() ? plan : undefined;
109
+ },
110
+ };
111
+ }
25
112
  export function createTelegramQueueBindingRuntime(deps) {
26
113
  const settleDiscardedItems = (items, ctx) => {
27
114
  const durableItems = items.filter((item) => (item.admissionReceipts?.length ?? 0) > 0);
@@ -34,6 +121,7 @@ export function createTelegramQueueBindingRuntime(deps) {
34
121
  };
35
122
  const mutation = Queue.createTelegramQueueMutationController({
36
123
  ...deps.store,
124
+ hasPendingDispatch: deps.lifecycle.hasDispatchPending,
37
125
  allocateLaneOrder: deps.queue.allocateItemOrder,
38
126
  onItemsDiscarded(items, ctx) {
39
127
  if (!settleDiscardedItems(items, ctx)) {
@@ -99,7 +187,9 @@ export function createTelegramQueueBindingRuntime(deps) {
99
187
  export function createTelegramGenerativeAppLiveSurfaceBinding() {
100
188
  let current;
101
189
  return {
102
- get() { return current; },
190
+ get() {
191
+ return current;
192
+ },
103
193
  set(runtime) {
104
194
  current?.shutdown();
105
195
  current = runtime;
@@ -313,6 +403,9 @@ export function createTelegramActivityBindingRuntime(deps) {
313
403
  publicationRuntime: {
314
404
  enqueue: publication.enqueue,
315
405
  reserve: publication.reserve,
406
+ hasPending: publication.hasPending,
407
+ beginWork: publication.beginWork,
408
+ hasUnconfirmed: publication.hasUnconfirmed,
316
409
  capture() {
317
410
  const authority = assistantOutputBinding.authority.captureAuthority();
318
411
  return {
@@ -323,7 +416,37 @@ export function createTelegramActivityBindingRuntime(deps) {
323
416
  },
324
417
  };
325
418
  }
326
- export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, recoverPollingStart, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, isContextCurrent, getSessionGeneration, connectionIntent, }) {
419
+ /** Fresh current work projection; a snapshot is not a cleanup grant or target-application ACK. */
420
+ export function createTelegramLiveTargetWorkObserver(deps) {
421
+ return (target, ctx) => {
422
+ const work = Queue.observeTelegramTargetQueueWork(target, deps.activeTurn.get(), deps.queue.getQueuedItems());
423
+ const flags = [
424
+ deps.isIdle(ctx),
425
+ deps.hasPendingMessages(ctx),
426
+ deps.lifecycle.hasDispatchPending(),
427
+ deps.lifecycle.isCompactionInProgress(),
428
+ deps.hasPendingControl(),
429
+ ];
430
+ const tools = deps.lifecycle.getActiveToolExecutions();
431
+ const publication = deps.publication.hasPending(), unconfirmed = deps.publication.hasUnconfirmed?.(), activity = deps.activity.hasPending?.(), delivery = deps.delivery.hasPendingTarget(target), api = deps.api.hasPendingTarget(target);
432
+ const unknown = work.unknown ||
433
+ flags.some((flag) => typeof flag !== "boolean") ||
434
+ !Number.isSafeInteger(tools) ||
435
+ tools < 0 ||
436
+ unconfirmed !== false ||
437
+ [publication, activity, delivery, api].some((flag) => typeof flag !== "boolean");
438
+ return {
439
+ sessionBusy: flags[0] !== true || flags.slice(1).some(Boolean) || tools > 0,
440
+ targetWork: work.protected,
441
+ deliveryPending: publication === true ||
442
+ activity === true ||
443
+ delivery === true ||
444
+ api === true,
445
+ unknown,
446
+ };
447
+ };
448
+ }
449
+ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, isContextCurrent, getSessionGeneration, connectionIntent, }) {
327
450
  GenerativeApps.registerTelegramBindTool(pi, {
328
451
  agentDir,
329
452
  getActiveProfileName: configStore.getActiveProfileName,
@@ -333,13 +456,18 @@ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, pe
333
456
  : {}),
334
457
  isDeliveryHandleCurrent: Delivery.isTelegramDeliveryHandleCurrent,
335
458
  editView: (handle, view) => Delivery.editTelegramView(handle, view),
336
- planOutput: OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, Config.createTelegramConfigControls(configStore).getAssistantRenderingMode),
459
+ planOutput: OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, Config.createTelegramConfigControls(configStore)
460
+ .getAssistantRenderingMode),
337
461
  sendMarkdownReply,
338
462
  sendView: (view, options) => Delivery.sendTelegramView(view, options),
339
463
  recordRuntimeEvent,
340
464
  });
341
- ChannelPosts.registerTelegramChannelPostMutationTool(pi, { mutate: mutateChannelPost });
342
- ChannelPosts.registerTelegramChannelPostListTool(pi, { list: listChannelPosts });
465
+ ChannelPosts.registerTelegramChannelPostMutationTool(pi, {
466
+ mutate: mutateChannelPost,
467
+ });
468
+ ChannelPosts.registerTelegramChannelPostListTool(pi, {
469
+ list: listChannelPosts,
470
+ });
343
471
  OutboundAttachments.registerTelegramOutboundAttachmentTool(pi, {
344
472
  getActiveTurn: activeTurnRuntime.get,
345
473
  getDefaultChatId,
@@ -355,7 +483,8 @@ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, pe
355
483
  resolveAgentTarget,
356
484
  routeAgentMessage,
357
485
  canSendDirect,
358
- planMessage: OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, Config.createTelegramConfigControls(configStore).getAssistantRenderingMode),
486
+ planMessage: OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, Config.createTelegramConfigControls(configStore)
487
+ .getAssistantRenderingMode),
359
488
  sendMarkdownMessage: (chatId, markdown, options) => sendMarkdownReply(chatId, undefined, markdown, options),
360
489
  sendChannelMarkdownMessage,
361
490
  sendChannelMediaMessage,
@@ -455,7 +584,6 @@ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, pe
455
584
  startPolling: lockedPollingRuntime.start,
456
585
  recordConnectionEvent: (error, phase) => recordRuntimeEvent("connection", error, { phase }),
457
586
  stopPolling: stopPolling ?? lockedPollingRuntime.stop,
458
- recoverPollingStart,
459
587
  getDisconnectThreadName,
460
588
  queueAgentConnectionContext,
461
589
  updateStatus,
@@ -598,7 +726,9 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
598
726
  pendingFinalPublication = undefined;
599
727
  };
600
728
  const recordPublicationFailure = (error) => {
601
- recordRuntimeEvent("delivery", error, { phase: "agent-end-background-delivery" });
729
+ recordRuntimeEvent("delivery", error, {
730
+ phase: "agent-end-background-delivery",
731
+ });
602
732
  };
603
733
  const scheduleActiveTurnDelivery = (task) => {
604
734
  void publicationRuntime.enqueue(task).catch(recordPublicationFailure);
@@ -629,7 +759,8 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
629
759
  getActiveTurn: activeTurnRuntime.get,
630
760
  loadConfig: configStore.load,
631
761
  extractAssistant: Replies.extractRunAssistantMessage,
632
- isAssistantAlreadyPublished: (assistant) => !!assistant.text && assistantOutputRuntime.hasAdmittedTelegramIntermediate(assistant.text),
762
+ isAssistantAlreadyPublished: (assistant) => !!assistant.text &&
763
+ assistantOutputRuntime.hasAdmittedTelegramIntermediate(assistant.text),
633
764
  getFoldQueuedPromptsIntoHistory: lifecycle.shouldFoldQueuedPromptsIntoHistory,
634
765
  resetRuntimeState: agentEndResetter,
635
766
  isSessionActive: isSessionContextActive,
@@ -646,7 +777,9 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
646
777
  pending?.reservation.cancel();
647
778
  const reservation = matches && pending ? pending.reservation : publicationRuntime.reserve();
648
779
  return {
649
- schedule: (task) => { void reservation.publish(task).catch(recordPublicationFailure); },
780
+ schedule: (task) => {
781
+ void reservation.publish(task).catch(recordPublicationFailure);
782
+ },
650
783
  cancel: reservation.cancel,
651
784
  };
652
785
  },
@@ -672,8 +805,7 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
672
805
  setActiveToolExecutions: lifecycle.setActiveToolExecutions,
673
806
  triggerPendingModelSwitchAbort: modelSwitchController.triggerPendingAbort,
674
807
  });
675
- Lifecycle.setResetTransportReplyDedup(Replies.resetTransportReplyDedup);
676
- const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(agentLifecycleHooks.onAgentStart, scheduleActiveTurnDelivery);
808
+ const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(agentLifecycleHooks.onAgentStart, Replies.resetTransportReplyDedup, scheduleActiveTurnDelivery);
677
809
  let uiPromptActive = false;
678
810
  const startAgentActivityTypingLoop = (ctx) => {
679
811
  if (uiPromptActive || !canSendAgentActivity(ctx))
@@ -704,7 +836,9 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
704
836
  if (turn && isTurnTransportActive?.(turn) === false)
705
837
  return;
706
838
  try {
707
- await sendMarkdownReply(target.chatId, replyToMessageId, text, { target });
839
+ await sendMarkdownReply(target.chatId, replyToMessageId, text, {
840
+ target,
841
+ });
708
842
  }
709
843
  catch (error) {
710
844
  recordRuntimeEvent("delivery", error, { phase: "compaction-notice" });
@@ -878,16 +1012,25 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
878
1012
  previewRuntime.seal();
879
1013
  activityRuntime.onAssistantMessageEnd(event.message.stopReason);
880
1014
  }
881
- if (event.message.role !== "assistant" || event.message.stopReason === "toolUse" || event.message.stopReason === "aborted")
1015
+ if (event.message.role !== "assistant" ||
1016
+ event.message.stopReason === "toolUse" ||
1017
+ event.message.stopReason === "aborted")
882
1018
  return;
883
1019
  const turn = activeTurnRuntime.get();
884
1020
  if (!turn || turn.guestQueryId || pendingFinalPublication?.turn === turn)
885
1021
  return;
886
1022
  cancelPendingFinalPublication();
887
- const assistant = Replies.extractLatestAssistantMessageText([event.message]);
888
- if (!assistant.text && assistant.stopReason !== "error" && turn.queuedAttachments.length === 0)
1023
+ const assistant = Replies.extractLatestAssistantMessageText([
1024
+ event.message,
1025
+ ]);
1026
+ if (!assistant.text &&
1027
+ assistant.stopReason !== "error" &&
1028
+ turn.queuedAttachments.length === 0)
889
1029
  return;
890
- pendingFinalPublication = { turn, reservation: publicationRuntime.reserve() };
1030
+ pendingFinalPublication = {
1031
+ turn,
1032
+ reservation: publicationRuntime.reserve(),
1033
+ };
891
1034
  },
892
1035
  onUiPromptStart(event, ctx) {
893
1036
  if (!isSessionContextActive(ctx))
@@ -910,7 +1053,8 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
910
1053
  async onAgentEnd(event, ctx) {
911
1054
  if (!isSessionContextActive(ctx))
912
1055
  return;
913
- if (pendingFinalPublication && pendingFinalPublication.turn !== activeTurnRuntime.get()) {
1056
+ if (pendingFinalPublication &&
1057
+ pendingFinalPublication.turn !== activeTurnRuntime.get()) {
914
1058
  cancelPendingFinalPublication();
915
1059
  return;
916
1060
  }
@@ -3,6 +3,7 @@
3
3
  * Zones: multi-instance bus, telegram api transport, live instance routing
4
4
  * Wraps the direct Telegram Bot API runtime so follower instances can route outbound calls through the bus leader
5
5
  */
6
+ import { type TelegramBusSelectedMenuDeliveryObservation, type TelegramBusSelectedMenuTextEffect } from "./bus.ts";
6
7
  import type { TelegramBridgeApiRuntime } from "./telegram-api.ts";
7
8
  export type TelegramBusApiCall = (method: string, args: unknown[]) => Promise<unknown>;
8
9
  export interface TelegramBusAwareApiRuntimeDeps {
@@ -14,4 +15,15 @@ export interface TelegramBusAwareApiRuntimeDeps {
14
15
  threadId?: number;
15
16
  } | undefined;
16
17
  }
18
+ /** Bound selected-menu projection only; no ordinary/raw API fallback and no activation during preparation. */
19
+ export declare function createTelegramSelectedMenuTextApi(deps: {
20
+ operationId: string;
21
+ registrationGeneration: string;
22
+ target: {
23
+ chatId: number;
24
+ threadId: number;
25
+ };
26
+ assertAuthority: () => void;
27
+ deliver(effect: TelegramBusSelectedMenuTextEffect, assertAuthority: () => void): Promise<TelegramBusSelectedMenuDeliveryObservation>;
28
+ }): Pick<TelegramBridgeApiRuntime, "sendMessage" | "editMessageText">;
17
29
  export declare function createTelegramBusAwareApiRuntime(deps: TelegramBusAwareApiRuntimeDeps): TelegramBridgeApiRuntime;