@llblab/pi-telegram 0.52.2 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/AGENTS.md +9 -7
  2. package/BACKLOG.md +1 -18
  3. package/CHANGELOG.md +11 -0
  4. package/README.md +7 -11
  5. package/api/commands.ts +3 -0
  6. package/dist/api/commands.d.ts +1 -1
  7. package/dist/lib/activity-verbosity.d.ts +1 -5
  8. package/dist/lib/activity-verbosity.js +15 -42
  9. package/dist/lib/activity.d.ts +9 -0
  10. package/dist/lib/activity.js +39 -10
  11. package/dist/lib/agent-messages.js +7 -3
  12. package/dist/lib/bindings.d.ts +43 -5
  13. package/dist/lib/bindings.js +163 -20
  14. package/dist/lib/bus-api.d.ts +12 -0
  15. package/dist/lib/bus-api.js +182 -79
  16. package/dist/lib/bus-follower.d.ts +111 -31
  17. package/dist/lib/bus-follower.js +1324 -359
  18. package/dist/lib/bus-leader.d.ts +41 -91
  19. package/dist/lib/bus-leader.js +969 -637
  20. package/dist/lib/bus-transport.js +16 -9
  21. package/dist/lib/bus.d.ts +215 -18
  22. package/dist/lib/bus.js +1048 -327
  23. package/dist/lib/channel-posts.d.ts +5 -0
  24. package/dist/lib/channel-posts.js +434 -261
  25. package/dist/lib/command-templates.d.ts +26 -5
  26. package/dist/lib/command-templates.js +92 -176
  27. package/dist/lib/commands.d.ts +168 -82
  28. package/dist/lib/commands.js +1409 -291
  29. package/dist/lib/config.d.ts +1 -8
  30. package/dist/lib/config.js +34 -76
  31. package/dist/lib/delivery.d.ts +2 -2
  32. package/dist/lib/delivery.js +16 -16
  33. package/dist/lib/extension.js +639 -171
  34. package/dist/lib/generative-app-worker.mjs +19 -7
  35. package/dist/lib/generative-apps.d.ts +0 -5
  36. package/dist/lib/generative-apps.js +61 -49
  37. package/dist/lib/inbound.d.ts +5 -0
  38. package/dist/lib/inbound.js +35 -59
  39. package/dist/lib/journal.d.ts +33 -3
  40. package/dist/lib/journal.js +2063 -782
  41. package/dist/lib/lifecycle.d.ts +2 -6
  42. package/dist/lib/lifecycle.js +75 -58
  43. package/dist/lib/locks.d.ts +29 -3
  44. package/dist/lib/locks.js +289 -108
  45. package/dist/lib/logging.js +50 -14
  46. package/dist/lib/media.d.ts +22 -1
  47. package/dist/lib/media.js +46 -28
  48. package/dist/lib/menu-model.d.ts +19 -5
  49. package/dist/lib/menu-model.js +53 -11
  50. package/dist/lib/menu-queue.d.ts +5 -4
  51. package/dist/lib/menu-queue.js +45 -63
  52. package/dist/lib/menu-settings.d.ts +5 -4
  53. package/dist/lib/menu-settings.js +114 -78
  54. package/dist/lib/menu-status.d.ts +1 -1
  55. package/dist/lib/menu-status.js +8 -25
  56. package/dist/lib/menu-thinking.d.ts +6 -1
  57. package/dist/lib/menu-thinking.js +14 -21
  58. package/dist/lib/menu.d.ts +8 -61
  59. package/dist/lib/menu.js +92 -164
  60. package/dist/lib/model.d.ts +1 -1
  61. package/dist/lib/model.js +2 -16
  62. package/dist/lib/outbound-attachments.d.ts +6 -27
  63. package/dist/lib/outbound-attachments.js +45 -38
  64. package/dist/lib/outbound-buttons.js +22 -16
  65. package/dist/lib/outbound-markup.d.ts +0 -2
  66. package/dist/lib/outbound-markup.js +51 -64
  67. package/dist/lib/outbound-voice.js +6 -3
  68. package/dist/lib/outbound.d.ts +6 -6
  69. package/dist/lib/outbound.js +61 -51
  70. package/dist/lib/ownership.js +6 -2
  71. package/dist/lib/paths.d.ts +5 -0
  72. package/dist/lib/paths.js +37 -21
  73. package/dist/lib/pi.d.ts +2 -0
  74. package/dist/lib/pi.js +8 -1
  75. package/dist/lib/polling.d.ts +21 -37
  76. package/dist/lib/polling.js +59 -50
  77. package/dist/lib/preview.js +39 -19
  78. package/dist/lib/process-identity.d.ts +24 -0
  79. package/dist/lib/process-identity.js +133 -0
  80. package/dist/lib/prompts.js +2 -7
  81. package/dist/lib/queue.d.ts +14 -16
  82. package/dist/lib/queue.js +249 -126
  83. package/dist/lib/recovery.d.ts +2 -83
  84. package/dist/lib/recovery.js +20 -274
  85. package/dist/lib/replies.d.ts +16 -24
  86. package/dist/lib/replies.js +99 -116
  87. package/dist/lib/routing.d.ts +120 -7
  88. package/dist/lib/routing.js +4667 -1152
  89. package/dist/lib/runtime.d.ts +1 -11
  90. package/dist/lib/sections.d.ts +4 -29
  91. package/dist/lib/sections.js +7 -20
  92. package/dist/lib/skills.js +4 -3
  93. package/dist/lib/status.js +86 -57
  94. package/dist/lib/sync.d.ts +1 -3
  95. package/dist/lib/sync.js +67 -43
  96. package/dist/lib/target.d.ts +4 -0
  97. package/dist/lib/target.js +18 -0
  98. package/dist/lib/telegram-api.d.ts +18 -13
  99. package/dist/lib/telegram-api.js +148 -53
  100. package/dist/lib/text-groups.js +4 -39
  101. package/dist/lib/thread-cleanup-manager.d.ts +6 -27
  102. package/dist/lib/thread-cleanup-manager.js +369 -186
  103. package/dist/lib/thread-display.d.ts +1 -2
  104. package/dist/lib/thread-display.js +58 -38
  105. package/dist/lib/thread-naming.d.ts +28 -8
  106. package/dist/lib/thread-naming.js +175 -66
  107. package/dist/lib/thread-reconciler.d.ts +42 -16
  108. package/dist/lib/thread-reconciler.js +90 -50
  109. package/dist/lib/threads.d.ts +82 -20
  110. package/dist/lib/threads.js +2247 -855
  111. package/dist/lib/time-injection.js +1 -2
  112. package/dist/lib/turns.js +1 -19
  113. package/dist/lib/updates.d.ts +169 -80
  114. package/dist/lib/updates.js +2418 -641
  115. package/dist/lib/voice.d.ts +7 -22
  116. package/dist/lib/voice.js +7 -22
  117. package/dist/lib/wire.js +1 -1
  118. package/dist/lib/workspace-admission.d.ts +3 -1
  119. package/dist/lib/workspace-admission.js +425 -211
  120. package/dist/lib/workspace-identity.js +8 -13
  121. package/dist/lib/workspace-retirement.d.ts +1 -1
  122. package/dist/lib/workspace-retirement.js +328 -140
  123. package/dist/lib/workspace-slots.js +8 -4
  124. package/dist/package.json +1 -1
  125. package/docs/architecture.md +31 -29
  126. package/docs/inbound.md +1 -1
  127. package/docs/multi-instance-bus.md +15 -3
  128. package/docs/outbound.md +1 -1
  129. package/docs/public-api.md +29 -1
  130. package/docs/ui-style.md +25 -8
  131. package/lib/activity-verbosity.ts +59 -82
  132. package/lib/activity.ts +146 -102
  133. package/lib/agent-messages.ts +12 -5
  134. package/lib/bindings.ts +389 -82
  135. package/lib/bus-api.ts +263 -102
  136. package/lib/bus-follower.ts +2440 -601
  137. package/lib/bus-leader.ts +2209 -1290
  138. package/lib/bus-transport.ts +46 -12
  139. package/lib/bus.ts +1673 -444
  140. package/lib/channel-posts.ts +848 -327
  141. package/lib/command-templates.ts +152 -265
  142. package/lib/commands.ts +2398 -555
  143. package/lib/config.ts +177 -170
  144. package/lib/delivery.ts +41 -29
  145. package/lib/extension.ts +1206 -433
  146. package/lib/generative-app-worker.mjs +19 -7
  147. package/lib/generative-apps.ts +324 -211
  148. package/lib/inbound.ts +95 -97
  149. package/lib/journal.ts +5311 -1763
  150. package/lib/keyboard.ts +1 -3
  151. package/lib/lifecycle.ts +128 -78
  152. package/lib/locks.ts +677 -203
  153. package/lib/logging.ts +147 -58
  154. package/lib/media.ts +82 -43
  155. package/lib/menu-model.ts +85 -18
  156. package/lib/menu-queue.ts +88 -129
  157. package/lib/menu-settings.ts +260 -155
  158. package/lib/menu-status.ts +20 -60
  159. package/lib/menu-thinking.ts +41 -56
  160. package/lib/menu.ts +112 -285
  161. package/lib/model.ts +8 -22
  162. package/lib/outbound-attachments.ts +156 -125
  163. package/lib/outbound-buttons.ts +76 -49
  164. package/lib/outbound-markup.ts +96 -94
  165. package/lib/outbound-voice.ts +33 -27
  166. package/lib/outbound.ts +125 -125
  167. package/lib/ownership.ts +19 -10
  168. package/lib/paths.ts +148 -49
  169. package/lib/pi.ts +35 -15
  170. package/lib/polling.ts +144 -147
  171. package/lib/preview.ts +93 -40
  172. package/lib/process-identity.ts +164 -0
  173. package/lib/prompts.ts +4 -11
  174. package/lib/queue.ts +511 -300
  175. package/lib/recovery.ts +50 -454
  176. package/lib/rendering.ts +1 -3
  177. package/lib/replies.ts +248 -209
  178. package/lib/routing.ts +8509 -2186
  179. package/lib/runtime.ts +1 -12
  180. package/lib/sections.ts +24 -89
  181. package/lib/setup.ts +1 -3
  182. package/lib/skills.ts +9 -4
  183. package/lib/status.ts +215 -110
  184. package/lib/sync.ts +448 -361
  185. package/lib/target.ts +22 -3
  186. package/lib/telegram-api.ts +302 -127
  187. package/lib/text-groups.ts +5 -39
  188. package/lib/thread-cleanup-manager.ts +833 -331
  189. package/lib/thread-display.ts +213 -97
  190. package/lib/thread-naming.ts +232 -87
  191. package/lib/thread-reconciler.ts +155 -72
  192. package/lib/threads.ts +5318 -1845
  193. package/lib/time-injection.ts +1 -4
  194. package/lib/turns.ts +15 -28
  195. package/lib/updates.ts +5184 -1612
  196. package/lib/voice.ts +7 -35
  197. package/lib/wire.ts +1 -1
  198. package/lib/workspace-admission.ts +996 -416
  199. package/lib/workspace-identity.ts +14 -17
  200. package/lib/workspace-retirement.ts +878 -376
  201. package/lib/workspace-slots.ts +22 -13
  202. package/package.json +1 -1
  203. package/scripts/check-downgrade.mjs +0 -472
  204. package/scripts/measure-workspace.mjs +0 -108
package/lib/bindings.ts CHANGED
@@ -4,16 +4,21 @@
4
4
  * Owns pi-facing tool, command, and lifecycle hook registration for the entrypoint
5
5
  */
6
6
 
7
- import * as Activity from "./activity.ts";
8
7
  import * as ActivityVerbosity from "./activity-verbosity.ts";
8
+ import * as Activity from "./activity.ts";
9
+ import * as BusApi from "./bus-api.ts";
10
+ import type { TelegramBusFollowerCommandOwner } from "./bus-follower.ts";
11
+ import type { TelegramBusLiveRebindWorkState } from "./bus.ts";
9
12
  import * as ChannelPosts from "./channel-posts.ts";
10
13
  import * as CommandTemplates from "./command-templates.ts";
11
14
  import * as Commands from "./commands.ts";
12
15
  import * as Config from "./config.ts";
13
16
  import * as Delivery from "./delivery.ts";
17
+ import * as GenerativeApps from "./generative-apps.ts";
14
18
  import * as Keyboard from "./keyboard.ts";
15
19
  import * as Lifecycle from "./lifecycle.ts";
16
20
  import * as Locks from "./locks.ts";
21
+ import * as Menu from "./menu.ts";
17
22
  import * as Model from "./model.ts";
18
23
  import * as OutboundAttachments from "./outbound-attachments.ts";
19
24
  import * as OutboundHandlers from "./outbound.ts";
@@ -26,9 +31,159 @@ import * as Routing from "./routing.ts";
26
31
  import * as Runtime from "./runtime.ts";
27
32
  import * as Setup from "./setup.ts";
28
33
  import * as Status from "./status.ts";
29
- import * as TelegramApi from "./telegram-api.ts";
30
34
  import type { TelegramTarget } from "./target.ts";
31
- import * as GenerativeApps from "./generative-apps.ts";
35
+ import * as TelegramApi from "./telegram-api.ts";
36
+
37
+ /** Compose any registered held command over captured recipient effects; no ordinary follower API fallback. */
38
+ export function createTelegramFollowerSelectedCommandBinding<
39
+ TContext,
40
+ TModel extends Model.MenuModel = Model.MenuModel,
41
+ >(deps: {
42
+ ctx: TContext;
43
+ operationId: string;
44
+ registrationGeneration: string;
45
+ name: string;
46
+ target: TelegramTarget & { threadId: number };
47
+ recipient: Pick<
48
+ TelegramBusFollowerCommandOwner<TContext>,
49
+ "isCurrent" | "assertRecipientCurrent"
50
+ >;
51
+ command: Pick<
52
+ ReturnType<
53
+ typeof Commands.createTelegramCommandHandlerTargetRuntime<
54
+ Commands.TelegramCommandRuntimeMessage,
55
+ TContext
56
+ >
57
+ >,
58
+ "prepareHeldCommand" | "canPrepareHeldCommand"
59
+ >;
60
+ menu?: Omit<
61
+ Menu.TelegramMenuActionRuntimeDeps<TContext, TModel>,
62
+ "sendTextReply" | "sendInteractiveMessage" | "editInteractiveMessage"
63
+ >;
64
+ deliver: Parameters<
65
+ typeof BusApi.createTelegramSelectedMenuTextApi
66
+ >[0]["deliver"];
67
+ recordOwnership: NonNullable<
68
+ Replies.TelegramRenderedMessageDeliveryRuntimeDeps<Menu.TelegramReplyMarkup>["recordOwnership"]
69
+ >;
70
+ }): TelegramBusFollowerCommandOwner<TContext> | undefined {
71
+ const {
72
+ ctx,
73
+ operationId,
74
+ registrationGeneration,
75
+ name,
76
+ recipient,
77
+ command,
78
+ deliver,
79
+ recordOwnership,
80
+ } = deps,
81
+ target = { ...deps.target };
82
+ const prepare = command.prepareHeldCommand,
83
+ canPrepare = command.canPrepareHeldCommand;
84
+ const isCurrent = recipient.isCurrent,
85
+ assertRecipientCurrent = recipient.assertRecipientCurrent,
86
+ menu = deps.menu,
87
+ menuPorts = menu && { ...menu };
88
+ if (
89
+ [
90
+ prepare,
91
+ canPrepare,
92
+ isCurrent,
93
+ assertRecipientCurrent,
94
+ deliver,
95
+ recordOwnership,
96
+ ].some((port) => typeof port !== "function") ||
97
+ (menuPorts &&
98
+ [
99
+ menuPorts.getModelMenuState,
100
+ menuPorts.storeModelMenuState,
101
+ menuPorts.getActiveModel,
102
+ menuPorts.getThinkingLevel,
103
+ menuPorts.buildStatusHtml,
104
+ menuPorts.isIdle,
105
+ menuPorts.canOfferInFlightModelSwitch,
106
+ ].some((port) => typeof port !== "function"))
107
+ )
108
+ return undefined;
109
+ const portsCurrent = () =>
110
+ deps.ctx === ctx &&
111
+ deps.operationId === operationId &&
112
+ deps.registrationGeneration === registrationGeneration &&
113
+ deps.name === name &&
114
+ deps.recipient === recipient &&
115
+ recipient.isCurrent === isCurrent &&
116
+ recipient.assertRecipientCurrent === assertRecipientCurrent &&
117
+ deps.command === command &&
118
+ command.prepareHeldCommand === prepare &&
119
+ command.canPrepareHeldCommand === canPrepare &&
120
+ deps.deliver === deliver &&
121
+ deps.recordOwnership === recordOwnership &&
122
+ deps.menu === menu &&
123
+ (!menuPorts ||
124
+ Object.entries(menuPorts).every(
125
+ ([key, value]) => Reflect.get(menu!, key) === value,
126
+ ));
127
+ const current = () =>
128
+ portsCurrent() && isCurrent.call(recipient) && portsCurrent();
129
+ if (!current()) return undefined;
130
+ const api = BusApi.createTelegramSelectedMenuTextApi({
131
+ operationId,
132
+ registrationGeneration,
133
+ target,
134
+ assertAuthority: assertRecipientCurrent,
135
+ deliver,
136
+ });
137
+ const rendered =
138
+ Replies.createTelegramRenderedMessageDeliveryRuntime<Menu.TelegramReplyMarkup>(
139
+ {
140
+ sendMessage: api.sendMessage,
141
+ editMessage: api.editMessageText,
142
+ recordOwnership,
143
+ async sendRichMessage() {
144
+ throw new Error(
145
+ "Selected command has no rich or ordinary API fallback.",
146
+ );
147
+ },
148
+ },
149
+ );
150
+ const menus =
151
+ menuPorts &&
152
+ Menu.createTelegramMenuActionRuntime({
153
+ ...menuPorts,
154
+ sendTextReply: rendered.sendTextReply,
155
+ sendInteractiveMessage: rendered.sendInteractiveMessage,
156
+ editInteractiveMessage: rendered.editInteractiveMessage,
157
+ });
158
+ const effects = {
159
+ sendTextReply: rendered.sendTextReply,
160
+ ...(menus ? { showStatus: menus.sendStatusMessage } : {}),
161
+ };
162
+ if (!canPrepare.call(command, name, effects) || !current()) return undefined;
163
+ return {
164
+ isCurrent: current,
165
+ assertRecipientCurrent,
166
+ prepare(readiness, context, input) {
167
+ if (
168
+ !current() ||
169
+ context !== ctx ||
170
+ input.target.chatId !== target.chatId ||
171
+ input.target.threadId !== target.threadId ||
172
+ input.assertRecipientCurrent !== assertRecipientCurrent
173
+ )
174
+ return undefined;
175
+ const plan = prepare.call(
176
+ command,
177
+ name,
178
+ readiness,
179
+ context,
180
+ input,
181
+ effects,
182
+ );
183
+ return current() ? plan : undefined;
184
+ },
185
+ };
186
+ }
32
187
 
33
188
  type ActivePiModel = NonNullable<Pi.ExtensionContext["model"]>;
34
189
 
@@ -120,9 +275,7 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
120
275
  allocateLaneOrder: deps.queue.allocateItemOrder,
121
276
  onItemsDiscarded(items, ctx) {
122
277
  if (!settleDiscardedItems(items, ctx)) {
123
- throw new Error(
124
- "Telegram queue items could not be discarded durably.",
125
- );
278
+ throw new Error("Telegram queue items could not be discarded durably.");
126
279
  }
127
280
  },
128
281
  updateStatus: deps.updateStatus,
@@ -188,19 +341,21 @@ export interface TelegramGenerativeAppLiveSurfaceBinding {
188
341
  | GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
189
342
  | undefined;
190
343
  set: (
191
- runtime: GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
344
+ runtime:
345
+ | GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
192
346
  | undefined,
193
347
  ) => void;
194
348
  shutdown: () => void;
195
349
  }
196
350
 
197
- export function createTelegramGenerativeAppLiveSurfaceBinding():
198
- TelegramGenerativeAppLiveSurfaceBinding {
351
+ export function createTelegramGenerativeAppLiveSurfaceBinding(): TelegramGenerativeAppLiveSurfaceBinding {
199
352
  let current:
200
353
  | GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
201
354
  | undefined;
202
355
  return {
203
- get() { return current; },
356
+ get() {
357
+ return current;
358
+ },
204
359
  set(runtime) {
205
360
  current?.shutdown();
206
361
  current = runtime;
@@ -214,17 +369,25 @@ TelegramGenerativeAppLiveSurfaceBinding {
214
369
 
215
370
  export function createTelegramGenerativeAppBoundButtonActionInvoker<
216
371
  TQuery extends {
217
- message?: { chat?: { id?: number }; message_id?: number; message_thread_id?: number };
372
+ message?: {
373
+ chat?: { id?: number };
374
+ message_id?: number;
375
+ message_thread_id?: number;
376
+ };
218
377
  },
219
378
  >(deps: {
220
379
  agentDir: string;
221
380
  assertExecutionCurrent: (query: TQuery) => void;
222
- getExecutionFence: (query: TQuery) => GenerativeApps.GenerativeAppExecutionFence | undefined;
381
+ getExecutionFence: (
382
+ query: TQuery,
383
+ ) => GenerativeApps.GenerativeAppExecutionFence | undefined;
223
384
  getActiveProfileName?: () => string | undefined;
224
385
  getLiveSurfaceRuntime?: () =>
225
386
  | GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
226
387
  | undefined;
227
- planOutput: ReturnType<typeof OutboundHandlers.createTelegramOutboundReplyPlanner>;
388
+ planOutput: ReturnType<
389
+ typeof OutboundHandlers.createTelegramOutboundReplyPlanner
390
+ >;
228
391
  sendMarkdownReply: (
229
392
  chatId: number,
230
393
  replyToMessageId: number,
@@ -258,16 +421,18 @@ export function createTelegramGenerativeAppBoundButtonActionInvoker<
258
421
  throw new Error("Generative App callback target is unavailable.");
259
422
  }
260
423
  const liveSurfaces = deps.getLiveSurfaceRuntime?.();
261
- handedOff = liveSurfaces?.take(GenerativeApps.getTelegramBindLiveSurfaceKey(
262
- boundAction.app,
263
- deps.getActiveProfileName?.() ?? "default",
264
- {
265
- chatId,
266
- ...(query.message?.message_thread_id !== undefined
267
- ? { threadId: query.message.message_thread_id }
268
- : {}),
269
- },
270
- ));
424
+ handedOff = liveSurfaces?.take(
425
+ GenerativeApps.getTelegramBindLiveSurfaceKey(
426
+ boundAction.app,
427
+ deps.getActiveProfileName?.() ?? "default",
428
+ {
429
+ chatId,
430
+ ...(query.message?.message_thread_id !== undefined
431
+ ? { threadId: query.message.message_thread_id }
432
+ : {}),
433
+ },
434
+ ),
435
+ );
271
436
  const result = await GenerativeApps.invokeGenerativeApp({
272
437
  agentDir: deps.agentDir,
273
438
  ...(deps.getExecutionFence(query)
@@ -424,7 +589,8 @@ export function createTelegramAssistantOutputBindingRuntime<
424
589
  typeof OutboundHandlers.createTelegramAssistantOutputSender<TTransportStamp>
425
590
  >[0];
426
591
  waitForActivityIdle?: () => Promise<void>;
427
- prepareTelegramPreview?: () => Activity.TelegramAssistantOutputPreparation | undefined;
592
+ prepareTelegramPreview?: () =>
593
+ Activity.TelegramAssistantOutputPreparation | undefined;
428
594
  enqueue?: Activity.TelegramActivityPublicationRuntime["enqueue"];
429
595
  recordRuntimeEvent: TelegramRuntimeEventRecorder;
430
596
  }): TelegramAssistantOutputBindingRuntime<TTransportStamp> {
@@ -438,7 +604,8 @@ export function createTelegramAssistantOutputBindingRuntime<
438
604
  const runtime = Activity.createTelegramAssistantOutputRuntime({
439
605
  ...authority,
440
606
  enqueue: deps.enqueue,
441
- prepareSend: (event) => event.source === "telegram" ? deps.prepareTelegramPreview?.() : undefined,
607
+ prepareSend: (event) =>
608
+ event.source === "telegram" ? deps.prepareTelegramPreview?.() : undefined,
442
609
  async send(event, authority, isAuthorityActive) {
443
610
  await deps.waitForActivityIdle?.();
444
611
  if (!isAuthorityActive()) return;
@@ -468,7 +635,13 @@ type TelegramAssistantOutputAuthority<TTransportStamp> = ReturnType<
468
635
  export interface TelegramBridgePublicationRuntime {
469
636
  enqueue: Activity.TelegramActivityPublicationRuntime["enqueue"];
470
637
  reserve: Activity.TelegramActivityPublicationRuntime["reserve"];
471
- capture: () => { target?: Queue.TelegramQueueTarget; isCurrent: () => boolean };
638
+ hasPending: Activity.TelegramActivityPublicationRuntime["hasPending"];
639
+ beginWork: Activity.TelegramActivityPublicationRuntime["beginWork"];
640
+ hasUnconfirmed: Activity.TelegramActivityPublicationRuntime["hasUnconfirmed"];
641
+ capture: () => {
642
+ target?: Queue.TelegramQueueTarget;
643
+ isCurrent: () => boolean;
644
+ };
472
645
  }
473
646
 
474
647
  export interface TelegramActivityBindingRuntime {
@@ -497,11 +670,10 @@ export function createTelegramActivityBindingRuntime<TTransportStamp>(deps: {
497
670
  >;
498
671
  }): TelegramActivityBindingRuntime {
499
672
  const publication = Activity.createTelegramActivityPublicationRuntime();
500
- const assistantOutputBinding =
501
- createTelegramAssistantOutputBindingRuntime({
502
- ...deps.assistantOutput,
503
- enqueue: publication.enqueue,
504
- });
673
+ const assistantOutputBinding = createTelegramAssistantOutputBindingRuntime({
674
+ ...deps.assistantOutput,
675
+ enqueue: publication.enqueue,
676
+ });
505
677
  const activityVerbosityRuntime =
506
678
  ActivityVerbosity.createTelegramActivityVerbosityRuntime({
507
679
  ...deps.activityVerbosity,
@@ -547,17 +719,88 @@ export function createTelegramActivityBindingRuntime<TTransportStamp>(deps: {
547
719
  publicationRuntime: {
548
720
  enqueue: publication.enqueue,
549
721
  reserve: publication.reserve,
722
+ hasPending: publication.hasPending,
723
+ beginWork: publication.beginWork,
724
+ hasUnconfirmed: publication.hasUnconfirmed,
550
725
  capture() {
551
726
  const authority = assistantOutputBinding.authority.captureAuthority();
552
727
  return {
553
728
  target: authority.target ? { ...authority.target } : undefined,
554
- isCurrent: () => assistantOutputBinding.authority.isAuthorityActive(authority),
729
+ isCurrent: () =>
730
+ assistantOutputBinding.authority.isAuthorityActive(authority),
555
731
  };
556
732
  },
557
733
  },
558
734
  };
559
735
  }
560
736
 
737
+ /** Fresh current work projection; a snapshot is not a cleanup grant or target-application ACK. */
738
+ export function createTelegramLiveTargetWorkObserver<TContext>(deps: {
739
+ queue: Pick<Queue.TelegramQueueStateStore<TContext>, "getQueuedItems">;
740
+ activeTurn: Pick<Queue.TelegramActiveTurnStore, "get">;
741
+ lifecycle: Pick<
742
+ Runtime.TelegramBridgeRuntime["lifecycle"],
743
+ "hasDispatchPending" | "isCompactionInProgress" | "getActiveToolExecutions"
744
+ >;
745
+ isIdle(ctx: TContext): boolean;
746
+ hasPendingMessages(ctx: TContext): boolean;
747
+ hasPendingControl(): boolean;
748
+ publication: Pick<
749
+ TelegramBridgePublicationRuntime,
750
+ "hasPending" | "hasUnconfirmed"
751
+ >;
752
+ activity: Pick<Activity.TelegramActivityRuntime, "hasPending">;
753
+ delivery: Pick<
754
+ ReturnType<typeof Delivery.createTelegramDeliveryLifecycleHooks>,
755
+ "hasPendingTarget"
756
+ >;
757
+ api: Pick<TelegramApi.TelegramApiTargetActivityRuntime, "hasPendingTarget">;
758
+ }): (
759
+ target: TelegramTarget & { threadId: number },
760
+ ctx: TContext,
761
+ ) => TelegramBusLiveRebindWorkState {
762
+ return (target, ctx) => {
763
+ const work = Queue.observeTelegramTargetQueueWork(
764
+ target,
765
+ deps.activeTurn.get(),
766
+ deps.queue.getQueuedItems(),
767
+ );
768
+ const flags = [
769
+ deps.isIdle(ctx),
770
+ deps.hasPendingMessages(ctx),
771
+ deps.lifecycle.hasDispatchPending(),
772
+ deps.lifecycle.isCompactionInProgress(),
773
+ deps.hasPendingControl(),
774
+ ];
775
+ const tools = deps.lifecycle.getActiveToolExecutions();
776
+ const publication = deps.publication.hasPending(),
777
+ unconfirmed = deps.publication.hasUnconfirmed?.(),
778
+ activity = deps.activity.hasPending?.(),
779
+ delivery = deps.delivery.hasPendingTarget(target),
780
+ api = deps.api.hasPendingTarget(target);
781
+ const unknown =
782
+ work.unknown ||
783
+ flags.some((flag) => typeof flag !== "boolean") ||
784
+ !Number.isSafeInteger(tools) ||
785
+ tools < 0 ||
786
+ unconfirmed !== false ||
787
+ [publication, activity, delivery, api].some(
788
+ (flag) => typeof flag !== "boolean",
789
+ );
790
+ return {
791
+ sessionBusy:
792
+ flags[0] !== true || flags.slice(1).some(Boolean) || tools > 0,
793
+ targetWork: work.protected,
794
+ deliveryPending:
795
+ publication === true ||
796
+ activity === true ||
797
+ delivery === true ||
798
+ api === true,
799
+ unknown,
800
+ };
801
+ };
802
+ }
803
+
561
804
  interface TelegramCommandsAndToolsBindingDeps {
562
805
  pi: Pi.ExtensionAPI;
563
806
  agentDir: string;
@@ -567,7 +810,6 @@ interface TelegramCommandsAndToolsBindingDeps {
567
810
  activeTurnRuntime: Queue.TelegramActiveTurnStore<Queue.PendingTelegramTurn>;
568
811
  lockedPollingRuntime: Locks.TelegramLockedPollingRuntime<Pi.ExtensionContext>;
569
812
  stopPolling?: () => Promise<void | string>;
570
- recoverPollingStart?: Commands.TelegramBridgeCommandRegistrationDeps["recoverPollingStart"];
571
813
  getDisconnectThreadName?: () => string | undefined;
572
814
  onTransportChanged?: () => Promise<void> | void;
573
815
  getStatusLines: (
@@ -590,8 +832,12 @@ interface TelegramCommandsAndToolsBindingDeps {
590
832
  OutboundAttachments.TelegramOutboundMessageToolRegistrationDeps["sendChannelMediaMessage"]
591
833
  >;
592
834
  listChannelPosts: ChannelPosts.TelegramChannelPostJournalStore["list"];
593
- mutateChannelPost(input: { action: "edit" | "delete"; operationId: string;
594
- mutationId: string; markdown?: string }): Promise<ChannelPosts.TelegramChannelPostRecord>;
835
+ mutateChannelPost(input: {
836
+ action: "edit" | "delete";
837
+ operationId: string;
838
+ mutationId: string;
839
+ markdown?: string;
840
+ }): Promise<ChannelPosts.TelegramChannelPostRecord>;
595
841
  callMultipart: OutboundHandlers.TelegramVoiceReplySenderDeps["sendMultipart"];
596
842
  getDefaultChatId: () => number | undefined;
597
843
  getDefaultTarget?: () => OutboundAttachments.TelegramQueuedOutboundAttachmentTurnView["target"];
@@ -599,13 +845,16 @@ interface TelegramCommandsAndToolsBindingDeps {
599
845
  routeAgentMessage?: OutboundAttachments.TelegramOutboundMessageToolRegistrationDeps["routeAgentMessage"];
600
846
  canSendDirect: () => boolean;
601
847
  setGenerativeAppLiveSurfaceRuntime?: (
602
- runtime: GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
848
+ runtime:
849
+ | GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
603
850
  | undefined,
604
851
  ) => void;
605
852
  updateStatus: TelegramBridgeStatusUpdater;
606
853
  isContextCurrent: (ctx: Pi.ExtensionContext) => boolean;
607
854
  getSessionGeneration: () => number;
608
- connectionIntent: NonNullable<Commands.TelegramBridgeCommandRegistrationDeps["connectionIntent"]>;
855
+ connectionIntent: NonNullable<
856
+ Commands.TelegramBridgeCommandRegistrationDeps["connectionIntent"]
857
+ >;
609
858
  recordRuntimeEvent: TelegramRuntimeEventRecorder;
610
859
  }
611
860
 
@@ -618,7 +867,6 @@ export function registerTelegramCommandsAndTools({
618
867
  activeTurnRuntime,
619
868
  lockedPollingRuntime,
620
869
  stopPolling,
621
- recoverPollingStart,
622
870
  getDisconnectThreadName,
623
871
  onTransportChanged,
624
872
  getStatusLines,
@@ -649,23 +897,27 @@ export function registerTelegramCommandsAndTools({
649
897
  ? { setLiveSurfaceRuntime: setGenerativeAppLiveSurfaceRuntime }
650
898
  : {}),
651
899
  isDeliveryHandleCurrent: Delivery.isTelegramDeliveryHandleCurrent,
652
- editView: (handle, view) => Delivery.editTelegramView(
653
- handle as Delivery.TelegramDeliveryHandle,
654
- view as Delivery.TelegramDeliveryView,
655
- ),
900
+ editView: (handle, view) =>
901
+ Delivery.editTelegramView(
902
+ handle as Delivery.TelegramDeliveryHandle,
903
+ view as Delivery.TelegramDeliveryView,
904
+ ),
656
905
  planOutput: OutboundHandlers.createTelegramOutboundReplyPlanner(
657
906
  buttonActionStore,
658
- Config.createTelegramConfigControls(configStore).getAssistantRenderingMode,
907
+ Config.createTelegramConfigControls(configStore)
908
+ .getAssistantRenderingMode,
659
909
  ),
660
910
  sendMarkdownReply,
661
- sendView: (view, options) => Delivery.sendTelegramView(
662
- view as Delivery.TelegramDeliveryView,
663
- options,
664
- ),
911
+ sendView: (view, options) =>
912
+ Delivery.sendTelegramView(view as Delivery.TelegramDeliveryView, options),
665
913
  recordRuntimeEvent,
666
914
  });
667
- ChannelPosts.registerTelegramChannelPostMutationTool(pi, { mutate: mutateChannelPost });
668
- ChannelPosts.registerTelegramChannelPostListTool(pi, { list: listChannelPosts });
915
+ ChannelPosts.registerTelegramChannelPostMutationTool(pi, {
916
+ mutate: mutateChannelPost,
917
+ });
918
+ ChannelPosts.registerTelegramChannelPostListTool(pi, {
919
+ list: listChannelPosts,
920
+ });
669
921
  OutboundAttachments.registerTelegramOutboundAttachmentTool(pi, {
670
922
  getActiveTurn: activeTurnRuntime.get,
671
923
  getDefaultChatId,
@@ -681,11 +933,11 @@ export function registerTelegramCommandsAndTools({
681
933
  resolveAgentTarget,
682
934
  routeAgentMessage,
683
935
  canSendDirect,
684
- planMessage:
685
- OutboundHandlers.createTelegramOutboundReplyPlanner(
686
- buttonActionStore,
687
- Config.createTelegramConfigControls(configStore).getAssistantRenderingMode,
688
- ),
936
+ planMessage: OutboundHandlers.createTelegramOutboundReplyPlanner(
937
+ buttonActionStore,
938
+ Config.createTelegramConfigControls(configStore)
939
+ .getAssistantRenderingMode,
940
+ ),
689
941
  sendMarkdownMessage: (chatId, markdown, options) =>
690
942
  sendMarkdownReply(chatId, undefined, markdown, options),
691
943
  sendChannelMarkdownMessage,
@@ -748,7 +1000,8 @@ export function registerTelegramCommandsAndTools({
748
1000
  );
749
1001
  }
750
1002
  await configStore.load();
751
- const latestProfile = configStore.getStoredConfig().profiles?.[profileName];
1003
+ const latestProfile =
1004
+ configStore.getStoredConfig().profiles?.[profileName];
752
1005
  configStore.setProfile(profileName, {
753
1006
  ...profile,
754
1007
  threadDisplayMode: latestProfile?.threadDisplayMode,
@@ -782,7 +1035,10 @@ export function registerTelegramCommandsAndTools({
782
1035
  if (completion.status === "success") {
783
1036
  queueAgentConnectionContext(true);
784
1037
  if (profileName) {
785
- ctx.ui.notify(`Profile "${profileName}" saved and connected.`, "info");
1038
+ ctx.ui.notify(
1039
+ `Profile "${profileName}" saved and connected.`,
1040
+ "info",
1041
+ );
786
1042
  }
787
1043
  }
788
1044
  },
@@ -791,9 +1047,9 @@ export function registerTelegramCommandsAndTools({
791
1047
  hasBotToken: configStore.hasBotToken,
792
1048
  getBotTokenDiagnostic: configStore.getBotTokenDiagnostic,
793
1049
  startPolling: lockedPollingRuntime.start,
794
- recordConnectionEvent: (error, phase) => recordRuntimeEvent("connection", error, { phase }),
1050
+ recordConnectionEvent: (error, phase) =>
1051
+ recordRuntimeEvent("connection", error, { phase }),
795
1052
  stopPolling: stopPolling ?? lockedPollingRuntime.stop,
796
- recoverPollingStart,
797
1053
  getDisconnectThreadName,
798
1054
  queueAgentConnectionContext,
799
1055
  updateStatus,
@@ -839,7 +1095,11 @@ interface TelegramLifecycleBindingDeps {
839
1095
  diagnostics?: { onSessionStart(): void; onSessionShutdown(): Promise<void> };
840
1096
  assistantOutputRuntime: Pick<
841
1097
  Activity.TelegramAssistantOutputRuntime,
842
- "start" | "beginTurn" | "hasAdmittedTelegramIntermediate" | "waitForIdle" | "stop"
1098
+ | "start"
1099
+ | "beginTurn"
1100
+ | "hasAdmittedTelegramIntermediate"
1101
+ | "waitForIdle"
1102
+ | "stop"
843
1103
  >;
844
1104
  sessionLifecycleRuntime: Pick<
845
1105
  Lifecycle.TelegramLifecycleRegistrationDeps,
@@ -866,7 +1126,10 @@ interface TelegramLifecycleBindingDeps {
866
1126
  deferredQueueDispatchRuntime: Queue.TelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>;
867
1127
  modelContextAvailabilityRuntime: Prompts.TelegramModelContextAvailabilityRuntime;
868
1128
  disconnectOnQuit?: () => Promise<unknown>;
869
- onSessionStarted?: (event: Pi.SessionStartEvent, ctx: Pi.ExtensionContext) => void;
1129
+ onSessionStarted?: (
1130
+ event: Pi.SessionStartEvent,
1131
+ ctx: Pi.ExtensionContext,
1132
+ ) => void;
870
1133
  shutdownGenerativeAppLiveSurfaces?: () => void;
871
1134
  resolveAutomaticThreadCleanupEnabled?: () => boolean | Promise<boolean>;
872
1135
  buttonActionStore: OutboundHandlers.TelegramButtonActionStore;
@@ -1113,16 +1376,20 @@ export function registerTelegramLifecycleRuntimeHooks({
1113
1376
  { replyToPrompt: false },
1114
1377
  );
1115
1378
  };
1116
- let pendingFinalPublication: {
1117
- turn: Queue.PendingTelegramTurn;
1118
- reservation: Activity.TelegramActivityPublicationReservation;
1119
- } | undefined;
1379
+ let pendingFinalPublication:
1380
+ | {
1381
+ turn: Queue.PendingTelegramTurn;
1382
+ reservation: Activity.TelegramActivityPublicationReservation;
1383
+ }
1384
+ | undefined;
1120
1385
  const cancelPendingFinalPublication = (): void => {
1121
1386
  pendingFinalPublication?.reservation.cancel();
1122
1387
  pendingFinalPublication = undefined;
1123
1388
  };
1124
1389
  const recordPublicationFailure = (error: unknown): void => {
1125
- recordRuntimeEvent("delivery", error, { phase: "agent-end-background-delivery" });
1390
+ recordRuntimeEvent("delivery", error, {
1391
+ phase: "agent-end-background-delivery",
1392
+ });
1126
1393
  };
1127
1394
  const scheduleActiveTurnDelivery = (task: () => Promise<void>): void => {
1128
1395
  void publicationRuntime.enqueue(task).catch(recordPublicationFailure);
@@ -1159,7 +1426,8 @@ export function registerTelegramLifecycleRuntimeHooks({
1159
1426
  loadConfig: configStore.load,
1160
1427
  extractAssistant: Replies.extractRunAssistantMessage,
1161
1428
  isAssistantAlreadyPublished: (assistant) =>
1162
- !!assistant.text && assistantOutputRuntime.hasAdmittedTelegramIntermediate(assistant.text),
1429
+ !!assistant.text &&
1430
+ assistantOutputRuntime.hasAdmittedTelegramIntermediate(assistant.text),
1163
1431
  getFoldQueuedPromptsIntoHistory:
1164
1432
  lifecycle.shouldFoldQueuedPromptsIntoHistory,
1165
1433
  resetRuntimeState: agentEndResetter,
@@ -1175,9 +1443,12 @@ export function registerTelegramLifecycleRuntimeHooks({
1175
1443
  pendingFinalPublication = undefined;
1176
1444
  const matches = pending?.turn === activeTurnRuntime.get();
1177
1445
  if (!matches) pending?.reservation.cancel();
1178
- const reservation = matches && pending ? pending.reservation : publicationRuntime.reserve();
1446
+ const reservation =
1447
+ matches && pending ? pending.reservation : publicationRuntime.reserve();
1179
1448
  return {
1180
- schedule: (task) => { void reservation.publish(task).catch(recordPublicationFailure); },
1449
+ schedule: (task) => {
1450
+ void reservation.publish(task).catch(recordPublicationFailure);
1451
+ },
1181
1452
  cancel: reservation.cancel,
1182
1453
  };
1183
1454
  },
@@ -1203,9 +1474,9 @@ export function registerTelegramLifecycleRuntimeHooks({
1203
1474
  setActiveToolExecutions: lifecycle.setActiveToolExecutions,
1204
1475
  triggerPendingModelSwitchAbort: modelSwitchController.triggerPendingAbort,
1205
1476
  });
1206
- Lifecycle.setResetTransportReplyDedup(Replies.resetTransportReplyDedup);
1207
1477
  const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(
1208
1478
  agentLifecycleHooks.onAgentStart,
1479
+ Replies.resetTransportReplyDedup,
1209
1480
  scheduleActiveTurnDelivery,
1210
1481
  );
1211
1482
  let uiPromptActive = false;
@@ -1230,23 +1501,32 @@ export function registerTelegramLifecycleRuntimeHooks({
1230
1501
  };
1231
1502
  let observedAutomaticCompaction = false;
1232
1503
  let agentWorkActive = false;
1233
- const prepareCompactionNotice = (text: string, ctx: Pi.ExtensionContext): (() => Promise<void>) => {
1504
+ const prepareCompactionNotice = (
1505
+ text: string,
1506
+ ctx: Pi.ExtensionContext,
1507
+ ): (() => Promise<void>) => {
1234
1508
  const authority = publicationRuntime.capture();
1235
1509
  const turn = activeTurnRuntime.get();
1236
1510
  const selectedTarget = turn?.target ?? authority.target;
1237
1511
  const target = selectedTarget ? { ...selectedTarget } : undefined;
1238
1512
  const replyToMessageId = turn?.replyToMessageId;
1239
1513
  return async () => {
1240
- if (!target || !isSessionContextActive(ctx) || !authority.isCurrent()) return;
1514
+ if (!target || !isSessionContextActive(ctx) || !authority.isCurrent())
1515
+ return;
1241
1516
  if (turn && isTurnTransportActive?.(turn) === false) return;
1242
1517
  try {
1243
- await sendMarkdownReply(target.chatId, replyToMessageId, text, { target });
1518
+ await sendMarkdownReply(target.chatId, replyToMessageId, text, {
1519
+ target,
1520
+ });
1244
1521
  } catch (error) {
1245
1522
  recordRuntimeEvent("delivery", error, { phase: "compaction-notice" });
1246
1523
  }
1247
1524
  };
1248
1525
  };
1249
- const sendCompactionNotice = (text: string, ctx: Pi.ExtensionContext): void => {
1526
+ const sendCompactionNotice = (
1527
+ text: string,
1528
+ ctx: Pi.ExtensionContext,
1529
+ ): void => {
1250
1530
  scheduleActiveTurnDelivery(prepareCompactionNotice(text, ctx));
1251
1531
  };
1252
1532
  const compactionObserver = Lifecycle.createTelegramCompactionObserverRuntime({
@@ -1325,7 +1605,11 @@ export function registerTelegramLifecycleRuntimeHooks({
1325
1605
  if (shouldNotify) observedAutomaticCompaction = true;
1326
1606
  activityRuntime.onCompactionStart(Pi.getSessionCompactionReason(event));
1327
1607
  compactionObserver.onSessionBeforeCompact(event, ctx);
1328
- if (shouldNotify) sendCompactionNotice(Commands.TELEGRAM_COMPACTION_STARTED_MARKDOWN, ctx);
1608
+ if (shouldNotify)
1609
+ sendCompactionNotice(
1610
+ Commands.TELEGRAM_COMPACTION_STARTED_MARKDOWN,
1611
+ ctx,
1612
+ );
1329
1613
  },
1330
1614
  async onSessionCompact(event, ctx) {
1331
1615
  if (!isSessionContextActive(ctx)) return;
@@ -1333,7 +1617,10 @@ export function registerTelegramLifecycleRuntimeHooks({
1333
1617
  compactionObserver.onSessionCompact(event, ctx);
1334
1618
  if (observedAutomaticCompaction) {
1335
1619
  observedAutomaticCompaction = false;
1336
- sendCompactionNotice(Commands.TELEGRAM_COMPACTION_COMPLETED_MARKDOWN, ctx);
1620
+ sendCompactionNotice(
1621
+ Commands.TELEGRAM_COMPACTION_COMPLETED_MARKDOWN,
1622
+ ctx,
1623
+ );
1337
1624
  }
1338
1625
  },
1339
1626
  async onSessionCompactFailed(event, ctx) {
@@ -1401,13 +1688,29 @@ export function registerTelegramLifecycleRuntimeHooks({
1401
1688
  previewRuntime.seal();
1402
1689
  activityRuntime.onAssistantMessageEnd(event.message.stopReason);
1403
1690
  }
1404
- if (event.message.role !== "assistant" || event.message.stopReason === "toolUse" || event.message.stopReason === "aborted") return;
1691
+ if (
1692
+ event.message.role !== "assistant" ||
1693
+ event.message.stopReason === "toolUse" ||
1694
+ event.message.stopReason === "aborted"
1695
+ )
1696
+ return;
1405
1697
  const turn = activeTurnRuntime.get();
1406
- if (!turn || turn.guestQueryId || pendingFinalPublication?.turn === turn) return;
1698
+ if (!turn || turn.guestQueryId || pendingFinalPublication?.turn === turn)
1699
+ return;
1407
1700
  cancelPendingFinalPublication();
1408
- const assistant = Replies.extractLatestAssistantMessageText([event.message]);
1409
- if (!assistant.text && assistant.stopReason !== "error" && turn.queuedAttachments.length === 0) return;
1410
- pendingFinalPublication = { turn, reservation: publicationRuntime.reserve() };
1701
+ const assistant = Replies.extractLatestAssistantMessageText([
1702
+ event.message,
1703
+ ]);
1704
+ if (
1705
+ !assistant.text &&
1706
+ assistant.stopReason !== "error" &&
1707
+ turn.queuedAttachments.length === 0
1708
+ )
1709
+ return;
1710
+ pendingFinalPublication = {
1711
+ turn,
1712
+ reservation: publicationRuntime.reserve(),
1713
+ };
1411
1714
  },
1412
1715
  onUiPromptStart(event, ctx) {
1413
1716
  if (!isSessionContextActive(ctx)) return;
@@ -1427,7 +1730,10 @@ export function registerTelegramLifecycleRuntimeHooks({
1427
1730
  },
1428
1731
  async onAgentEnd(event, ctx) {
1429
1732
  if (!isSessionContextActive(ctx)) return;
1430
- if (pendingFinalPublication && pendingFinalPublication.turn !== activeTurnRuntime.get()) {
1733
+ if (
1734
+ pendingFinalPublication &&
1735
+ pendingFinalPublication.turn !== activeTurnRuntime.get()
1736
+ ) {
1431
1737
  cancelPendingFinalPublication();
1432
1738
  return;
1433
1739
  }
@@ -1440,7 +1746,8 @@ export function registerTelegramLifecycleRuntimeHooks({
1440
1746
  try {
1441
1747
  await agentLifecycleHooks.onAgentSettled(event, ctx);
1442
1748
  } finally {
1443
- if (pendingFinalPublication === pending) cancelPendingFinalPublication();
1749
+ if (pendingFinalPublication === pending)
1750
+ cancelPendingFinalPublication();
1444
1751
  }
1445
1752
  if (!isSessionContextActive(ctx)) return;
1446
1753
  agentWorkActive = false;