@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/bus-api.ts CHANGED
@@ -6,13 +6,11 @@
6
6
 
7
7
  import {
8
8
  markTelegramBusCrossTargetDelivery,
9
+ parseTelegramBusSelectedMenuTextEffect,
9
10
  stripTelegramBusApiMetadata,
11
+ type TelegramBusSelectedMenuDeliveryObservation,
12
+ type TelegramBusSelectedMenuTextEffect,
10
13
  } from "./bus.ts";
11
- import {
12
- buildTelegramAnswerGuestQueryBody,
13
- formatTelegramCallbackAnswerText,
14
- isTelegramMessageNotModifiedError,
15
- } from "./telegram-api.ts";
16
14
  import type { TelegramAttachmentSource } from "./media.ts";
17
15
  import type {
18
16
  TelegramAnswerGuestQueryOptions,
@@ -27,6 +25,13 @@ import type {
27
25
  TelegramSentMessage,
28
26
  TelegramUpdate,
29
27
  } from "./telegram-api.ts";
28
+ import {
29
+ buildTelegramAnswerGuestQueryBody,
30
+ isTelegramApiCommitUnknownError,
31
+ isTelegramMessageNotModifiedError,
32
+ TelegramApiAuthorityError,
33
+ TelegramApiCommitUnknownError,
34
+ } from "./telegram-api.ts";
30
35
 
31
36
  export type TelegramBusApiCall = (
32
37
  method: string,
@@ -84,6 +89,167 @@ function rejectTelegramDirectOwnership(method: string): Promise<never> {
84
89
  );
85
90
  }
86
91
 
92
+ /** Bound selected-menu projection only; no ordinary/raw API fallback and no activation during preparation. */
93
+ export function createTelegramSelectedMenuTextApi(deps: {
94
+ operationId: string;
95
+ registrationGeneration: string;
96
+ target: { chatId: number; threadId: number };
97
+ assertAuthority: () => void;
98
+ deliver(
99
+ effect: TelegramBusSelectedMenuTextEffect,
100
+ assertAuthority: () => void,
101
+ ): Promise<TelegramBusSelectedMenuDeliveryObservation>;
102
+ }): Pick<TelegramBridgeApiRuntime, "sendMessage" | "editMessageText"> {
103
+ const { operationId, registrationGeneration, assertAuthority, deliver } =
104
+ deps,
105
+ target = { ...deps.target };
106
+ if (
107
+ typeof assertAuthority !== "function" ||
108
+ typeof deliver !== "function" ||
109
+ !operationId?.trim() ||
110
+ operationId.length > 128 ||
111
+ !registrationGeneration?.trim() ||
112
+ registrationGeneration.length > 128 ||
113
+ !Number.isSafeInteger(target.chatId) ||
114
+ target.chatId <= 0 ||
115
+ !Number.isSafeInteger(target.threadId) ||
116
+ target.threadId <= 0
117
+ )
118
+ throw new TelegramApiAuthorityError(false);
119
+ const invoke = async (
120
+ edit: boolean,
121
+ raw: Record<string, unknown>,
122
+ options?: Pick<TelegramApiCallOptions, "assertAuthority">,
123
+ ) => {
124
+ let issued = false;
125
+ const assertCurrent = () => {
126
+ try {
127
+ assertAuthority();
128
+ } catch {
129
+ throw new TelegramApiAuthorityError(issued);
130
+ }
131
+ };
132
+ try {
133
+ if (
134
+ options?.assertAuthority !== assertAuthority ||
135
+ Reflect.ownKeys(raw).some(
136
+ (key) =>
137
+ typeof key !== "string" ||
138
+ ![
139
+ "chat_id",
140
+ "message_thread_id",
141
+ "text",
142
+ "parse_mode",
143
+ "reply_markup",
144
+ ...(edit ? ["message_id"] : ["reply_parameters"]),
145
+ ].includes(key),
146
+ )
147
+ )
148
+ throw new TelegramApiAuthorityError(false);
149
+ const body = structuredClone(raw),
150
+ reply = body.reply_parameters;
151
+ if (
152
+ body.chat_id !== target.chatId ||
153
+ body.message_thread_id !== target.threadId ||
154
+ (reply !== undefined &&
155
+ (!reply ||
156
+ typeof reply !== "object" ||
157
+ Array.isArray(reply) ||
158
+ Object.keys(reply).some(
159
+ (key) =>
160
+ !["message_id", "allow_sending_without_reply"].includes(key),
161
+ ) ||
162
+ (reply as Record<string, unknown>).allow_sending_without_reply !==
163
+ true ||
164
+ !Number.isSafeInteger(
165
+ (reply as Record<string, unknown>).message_id,
166
+ ) ||
167
+ ((reply as Record<string, unknown>).message_id as number) <= 0))
168
+ )
169
+ throw new TelegramApiAuthorityError(false);
170
+ const effect = parseTelegramBusSelectedMenuTextEffect({
171
+ kind: edit ? "edit-text" : "send-text",
172
+ text: body.text,
173
+ ...(body.parse_mode !== undefined
174
+ ? { parseMode: body.parse_mode }
175
+ : {}),
176
+ ...(body.reply_markup !== undefined
177
+ ? { replyMarkup: body.reply_markup }
178
+ : {}),
179
+ ...(edit
180
+ ? { messageId: body.message_id }
181
+ : reply !== undefined
182
+ ? {
183
+ replyToMessageId: (reply as Record<string, unknown>).message_id,
184
+ }
185
+ : {}),
186
+ });
187
+ if (!effect) throw new TelegramApiAuthorityError(false);
188
+ assertCurrent();
189
+ issued = true;
190
+ const observed = await deliver.call(deps, effect, assertAuthority);
191
+ assertCurrent();
192
+ if (
193
+ observed.operationId !== operationId ||
194
+ observed.registrationGeneration !== registrationGeneration ||
195
+ observed.effect !== effect.kind ||
196
+ observed.recipient.target.chatId !== target.chatId ||
197
+ observed.recipient.target.threadId !== target.threadId ||
198
+ !Number.isSafeInteger(observed.messageId) ||
199
+ observed.messageId <= 0 ||
200
+ (effect.kind === "edit-text" && observed.messageId !== effect.messageId)
201
+ )
202
+ throw new Error("Selected-menu transport result is unconfirmed.");
203
+ assertCurrent();
204
+ return observed.messageId;
205
+ } catch (error) {
206
+ if (
207
+ error instanceof TelegramApiAuthorityError ||
208
+ isTelegramApiCommitUnknownError(error)
209
+ )
210
+ throw error;
211
+ if (issued)
212
+ throw new TelegramApiCommitUnknownError(
213
+ edit ? "editMessageText" : "sendMessage",
214
+ error,
215
+ );
216
+ throw new TelegramApiAuthorityError(false);
217
+ }
218
+ };
219
+ return {
220
+ sendMessage: async (body, options) => ({
221
+ message_id: await invoke(false, body, options),
222
+ }),
223
+ editMessageText: async (body, options) => {
224
+ await invoke(true, body, options);
225
+ return "edited";
226
+ },
227
+ };
228
+ }
229
+
230
+ /** Followers route every chat action through the leader's generic `sendChatAction` call. */
231
+ function sendTelegramFollowerChatAction(
232
+ deps: Pick<
233
+ TelegramBusAwareApiRuntimeDeps,
234
+ "callFollowerApi" | "getDefaultTarget"
235
+ >,
236
+ chatId: number,
237
+ action: string,
238
+ options?: { message_thread_id?: number },
239
+ ): Promise<boolean> {
240
+ const body = withDefaultThreadTarget(
241
+ {
242
+ chat_id: chatId,
243
+ action,
244
+ ...(options?.message_thread_id !== undefined
245
+ ? { message_thread_id: options.message_thread_id }
246
+ : {}),
247
+ },
248
+ deps.getDefaultTarget?.(),
249
+ );
250
+ return deps.callFollowerApi("call", ["sendChatAction", body]).then(asBoolean);
251
+ }
252
+
87
253
  export function createTelegramBusAwareApiRuntime(
88
254
  deps: TelegramBusAwareApiRuntimeDeps,
89
255
  ): TelegramBridgeApiRuntime {
@@ -93,13 +259,16 @@ export function createTelegramBusAwareApiRuntime(
93
259
  body: Record<string, unknown>,
94
260
  options?: TelegramApiCallOptions,
95
261
  ): Promise<TResponse> {
96
- return deps.ownsDirect()
97
- ? deps.directRuntime.call<TResponse>(method, body, options)
98
- : (deps.callFollowerApi("call", [
99
- method,
100
- body,
101
- options,
102
- ]) as Promise<TResponse>);
262
+ if (deps.ownsDirect())
263
+ return deps.directRuntime.call<TResponse>(method, body, options);
264
+ // A process-local authority callback cannot cross IPC as an enforceable grant.
265
+ if (options?.assertAuthority)
266
+ return Promise.reject(new TelegramApiAuthorityError(false));
267
+ return deps.callFollowerApi("call", [
268
+ method,
269
+ body,
270
+ options,
271
+ ]) as Promise<TResponse>;
103
272
  },
104
273
  callMultipart<TResponse>(
105
274
  method: string,
@@ -109,25 +278,31 @@ export function createTelegramBusAwareApiRuntime(
109
278
  fileName: string,
110
279
  options?: TelegramApiCallOptions,
111
280
  ): Promise<TResponse> {
112
- return deps.ownsDirect()
113
- ? deps.directRuntime.callMultipart<TResponse>(
114
- method,
115
- fields,
116
- fileField,
117
- filePath,
118
- fileName,
119
- options,
120
- )
121
- : (deps.callFollowerApi("callMultipart", [
122
- method,
123
- fields,
124
- fileField,
125
- filePath,
126
- fileName,
127
- options,
128
- ]) as Promise<TResponse>);
281
+ if (deps.ownsDirect())
282
+ return deps.directRuntime.callMultipart<TResponse>(
283
+ method,
284
+ fields,
285
+ fileField,
286
+ filePath,
287
+ fileName,
288
+ options,
289
+ );
290
+ if (options?.assertAuthority)
291
+ return Promise.reject(new TelegramApiAuthorityError(false));
292
+ return deps.callFollowerApi("callMultipart", [
293
+ method,
294
+ fields,
295
+ fileField,
296
+ filePath,
297
+ fileName,
298
+ options,
299
+ ]) as Promise<TResponse>;
129
300
  },
130
- downloadFile(fileId: string, suggestedName: string, source?: TelegramAttachmentSource): Promise<string> {
301
+ downloadFile(
302
+ fileId: string,
303
+ suggestedName: string,
304
+ source?: TelegramAttachmentSource,
305
+ ): Promise<string> {
131
306
  // The source names the file (kind-scope-message); dropping it falls back to the bare generated name.
132
307
  return deps.ownsDirect()
133
308
  ? deps.directRuntime.downloadFile(fileId, suggestedName, source)
@@ -150,73 +325,39 @@ export function createTelegramBusAwareApiRuntime(
150
325
  ? deps.directRuntime.getUpdates(body, signal)
151
326
  : rejectTelegramDirectOwnership("getUpdates");
152
327
  },
153
- setMyCommands(commands): Promise<boolean> {
154
- return deps.ownsDirect()
155
- ? deps.directRuntime.setMyCommands(commands)
156
- : deps
157
- .callFollowerApi("call", ["setMyCommands", { commands }])
158
- .then(asBoolean);
328
+ setMyCommands(commands, options): Promise<boolean> {
329
+ if (deps.ownsDirect())
330
+ return deps.directRuntime.setMyCommands(commands, options);
331
+ if (options?.assertAuthority)
332
+ return Promise.reject(new TelegramApiAuthorityError(false));
333
+ return deps
334
+ .callFollowerApi("call", ["setMyCommands", { commands }])
335
+ .then(asBoolean);
159
336
  },
160
337
  sendChatAction(
161
338
  chatId: number,
162
339
  action: string,
163
340
  options?: { message_thread_id?: number },
164
341
  ): Promise<boolean> {
165
- const body = withDefaultThreadTarget(
166
- {
167
- chat_id: chatId,
168
- action,
169
- ...(options?.message_thread_id !== undefined
170
- ? { message_thread_id: options.message_thread_id }
171
- : {}),
172
- },
173
- deps.getDefaultTarget?.(),
174
- );
175
342
  return deps.ownsDirect()
176
343
  ? deps.directRuntime.sendChatAction(chatId, action, options)
177
- : deps
178
- .callFollowerApi("call", ["sendChatAction", body])
179
- .then(asBoolean);
344
+ : sendTelegramFollowerChatAction(deps, chatId, action, options);
180
345
  },
181
346
  sendTypingAction(
182
347
  chatId: number,
183
348
  options?: { message_thread_id?: number },
184
349
  ): Promise<unknown> {
185
- const body = withDefaultThreadTarget(
186
- {
187
- chat_id: chatId,
188
- action: "typing",
189
- ...(options?.message_thread_id !== undefined
190
- ? { message_thread_id: options.message_thread_id }
191
- : {}),
192
- },
193
- deps.getDefaultTarget?.(),
194
- );
195
350
  return deps.ownsDirect()
196
351
  ? deps.directRuntime.sendTypingAction(chatId, options)
197
- : deps
198
- .callFollowerApi("call", ["sendChatAction", body])
199
- .then(asBoolean);
352
+ : sendTelegramFollowerChatAction(deps, chatId, "typing", options);
200
353
  },
201
354
  sendRecordVoiceAction(
202
355
  chatId: number,
203
356
  options?: { message_thread_id?: number },
204
357
  ): Promise<unknown> {
205
- const body = withDefaultThreadTarget(
206
- {
207
- chat_id: chatId,
208
- action: "record_voice",
209
- ...(options?.message_thread_id !== undefined
210
- ? { message_thread_id: options.message_thread_id }
211
- : {}),
212
- },
213
- deps.getDefaultTarget?.(),
214
- );
215
358
  return deps.ownsDirect()
216
359
  ? deps.directRuntime.sendRecordVoiceAction(chatId, options)
217
- : deps
218
- .callFollowerApi("call", ["sendChatAction", body])
219
- .then(asBoolean);
360
+ : sendTelegramFollowerChatAction(deps, chatId, "record_voice", options);
220
361
  },
221
362
  sendMessageDraft(
222
363
  chatId: number,
@@ -251,33 +392,41 @@ export function createTelegramBusAwareApiRuntime(
251
392
  .callFollowerApi("call", ["sendMessageDraft", scopedBody])
252
393
  .then(asBoolean);
253
394
  },
254
- sendMessage(body: TelegramSendMessageBody): Promise<TelegramSentMessage> {
255
- return deps.ownsDirect()
256
- ? deps.directRuntime.sendMessage(stripTelegramBusApiMetadata(body))
257
- : deps
258
- .callFollowerApi("call", [
259
- "sendMessage",
260
- markFollowerCrossTargetDelivery(
261
- body,
262
- deps.getDefaultTarget?.(),
263
- ),
264
- ])
265
- .then(asSentMessage);
395
+ sendMessage(
396
+ body: TelegramSendMessageBody,
397
+ options?: Pick<TelegramApiCallOptions, "assertAuthority">,
398
+ ): Promise<TelegramSentMessage> {
399
+ if (deps.ownsDirect())
400
+ return deps.directRuntime.sendMessage(
401
+ stripTelegramBusApiMetadata(body),
402
+ options,
403
+ );
404
+ if (options?.assertAuthority)
405
+ return Promise.reject(new TelegramApiAuthorityError(false));
406
+ return deps
407
+ .callFollowerApi("call", [
408
+ "sendMessage",
409
+ markFollowerCrossTargetDelivery(body, deps.getDefaultTarget?.()),
410
+ ])
411
+ .then(asSentMessage);
266
412
  },
267
413
  sendRichMessage(
268
414
  body: TelegramSendRichMessageBody,
415
+ options?: Pick<TelegramApiCallOptions, "assertAuthority">,
269
416
  ): Promise<TelegramSentMessage> {
270
- return deps.ownsDirect()
271
- ? deps.directRuntime.sendRichMessage(stripTelegramBusApiMetadata(body))
272
- : deps
273
- .callFollowerApi("call", [
274
- "sendRichMessage",
275
- markFollowerCrossTargetDelivery(
276
- body,
277
- deps.getDefaultTarget?.(),
278
- ),
279
- ])
280
- .then(asSentMessage);
417
+ if (deps.ownsDirect())
418
+ return deps.directRuntime.sendRichMessage(
419
+ stripTelegramBusApiMetadata(body),
420
+ options,
421
+ );
422
+ if (options?.assertAuthority)
423
+ return Promise.reject(new TelegramApiAuthorityError(false));
424
+ return deps
425
+ .callFollowerApi("call", [
426
+ "sendRichMessage",
427
+ markFollowerCrossTargetDelivery(body, deps.getDefaultTarget?.()),
428
+ ])
429
+ .then(asSentMessage);
281
430
  },
282
431
  sendRichMessageDraft(
283
432
  body: TelegramSendRichMessageDraftBody,
@@ -290,8 +439,11 @@ export function createTelegramBusAwareApiRuntime(
290
439
  },
291
440
  async editMessageText(
292
441
  body: TelegramEditMessageTextBody,
442
+ options?: Pick<TelegramApiCallOptions, "assertAuthority">,
293
443
  ): Promise<"edited" | "unchanged"> {
294
- if (deps.ownsDirect()) return deps.directRuntime.editMessageText(body);
444
+ if (deps.ownsDirect())
445
+ return deps.directRuntime.editMessageText(body, options);
446
+ if (options?.assertAuthority) throw new TelegramApiAuthorityError(false);
295
447
  try {
296
448
  await deps.callFollowerApi("call", ["editMessageText", body]);
297
449
  return "edited";
@@ -321,17 +473,26 @@ export function createTelegramBusAwareApiRuntime(
321
473
  async answerCallbackQuery(
322
474
  callbackQueryId: string,
323
475
  text?: string,
476
+ options?: Pick<TelegramApiCallOptions, "assertAuthority">,
324
477
  ): Promise<void> {
325
- const answer = formatTelegramCallbackAnswerText(text);
478
+ const assertAuthority = options?.assertAuthority;
326
479
  if (deps.ownsDirect()) {
327
- await deps.directRuntime.answerCallbackQuery(callbackQueryId, answer);
480
+ const authorityOptions:
481
+ [] | [Pick<TelegramApiCallOptions, "assertAuthority">] =
482
+ assertAuthority ? [{ assertAuthority }] : [];
483
+ await deps.directRuntime.answerCallbackQuery(
484
+ callbackQueryId,
485
+ text,
486
+ ...authorityOptions,
487
+ );
328
488
  return;
329
489
  }
490
+ if (assertAuthority) throw new TelegramApiAuthorityError(false);
330
491
  await deps.callFollowerApi("call", [
331
492
  "answerCallbackQuery",
332
493
  {
333
494
  callback_query_id: callbackQueryId,
334
- ...(answer !== undefined ? { text: answer } : {}),
495
+ ...(text !== undefined ? { text } : {}),
335
496
  },
336
497
  ]);
337
498
  },