@llblab/pi-kit 0.29.2 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/BACKLOG.md +2 -1
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +2 -2
  4. package/node_modules/@llblab/pi-telegram/AGENTS.md +9 -7
  5. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -18
  6. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +15 -0
  7. package/node_modules/@llblab/pi-telegram/README.md +7 -11
  8. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  9. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  10. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  11. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  12. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  13. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  14. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  15. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  16. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +163 -20
  17. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  18. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +182 -79
  19. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  20. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  21. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  22. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  23. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  24. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  25. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  26. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  28. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  29. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  30. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  31. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  32. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  33. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  34. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  35. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  36. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +639 -171
  37. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  38. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  39. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  40. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  42. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +33 -3
  43. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2063 -782
  44. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  45. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  46. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +29 -3
  47. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +289 -108
  48. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  49. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  50. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  51. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  52. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  53. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  54. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  55. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  56. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  57. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  58. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  59. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  60. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  61. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  62. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  63. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  65. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  66. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  67. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  68. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  69. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  70. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  71. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  72. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +65 -51
  73. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  74. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  75. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  76. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  77. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +21 -37
  79. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +59 -50
  80. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  81. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  82. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  83. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  84. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +14 -16
  85. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +252 -127
  86. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  87. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  88. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  89. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  90. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +120 -7
  91. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4667 -1152
  92. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  93. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  94. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  95. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  96. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  97. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +1 -3
  98. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +67 -43
  99. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  100. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  101. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -13
  102. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +148 -53
  103. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  104. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  105. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  106. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  107. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  108. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  109. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  110. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  111. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  112. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +82 -20
  113. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2247 -855
  114. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  115. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  116. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +169 -80
  117. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2418 -641
  118. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  119. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  120. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  121. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  122. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  123. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  124. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  125. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  126. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  127. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  128. package/node_modules/@llblab/pi-telegram/docs/architecture.md +31 -29
  129. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  130. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  131. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  132. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  133. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  134. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  135. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  136. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  137. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +389 -82
  138. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +263 -102
  139. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  140. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  141. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  142. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  143. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  144. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  145. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  146. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  147. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  148. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1206 -433
  149. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  150. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  151. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  152. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5311 -1763
  153. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  154. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  155. package/node_modules/@llblab/pi-telegram/lib/locks.ts +677 -203
  156. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  157. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  158. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  159. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  160. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  161. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  162. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  163. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  164. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  165. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  166. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  167. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  168. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  169. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +128 -125
  170. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  171. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  172. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  173. package/node_modules/@llblab/pi-telegram/lib/polling.ts +144 -147
  174. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  175. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  176. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  177. package/node_modules/@llblab/pi-telegram/lib/queue.ts +516 -301
  178. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  179. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  180. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  181. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8509 -2186
  182. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  183. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  184. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  185. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  186. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  187. package/node_modules/@llblab/pi-telegram/lib/sync.ts +448 -361
  188. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  189. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +302 -127
  190. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  191. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  192. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  193. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  194. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  195. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5318 -1845
  196. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  197. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  198. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5184 -1612
  199. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  200. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  201. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  202. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  203. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  204. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  205. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  206. package/package.json +2 -2
  207. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  208. package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
@@ -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
  },