@llblab/pi-kit 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/BACKLOG.md +3 -1
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +4 -4
  4. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +12 -0
  5. package/node_modules/@llblab/pi-state-flow/README.md +2 -2
  6. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +2 -2
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +17 -8
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +7 -3
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +2 -1
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +3 -2
  11. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  12. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -3
  13. package/node_modules/@llblab/pi-state-flow/docs/usage.md +3 -2
  14. package/node_modules/@llblab/pi-state-flow/lib/context.ts +17 -8
  15. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +6 -3
  16. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +4 -2
  17. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  18. package/node_modules/@llblab/pi-telegram/AGENTS.md +13 -10
  19. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -1
  20. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +23 -0
  21. package/node_modules/@llblab/pi-telegram/README.md +10 -11
  22. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  23. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  24. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  25. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  26. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  28. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  29. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  30. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +164 -20
  31. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  32. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +181 -77
  33. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  34. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  35. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  36. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  37. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  38. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  39. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  40. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  42. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  43. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  44. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  45. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  46. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  47. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  48. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  49. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  50. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +642 -170
  51. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  52. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  53. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  54. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  55. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  56. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +40 -4
  57. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2102 -760
  58. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  59. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  60. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +34 -3
  61. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +318 -119
  62. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  63. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  65. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  66. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  67. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  68. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  69. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  70. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  71. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  72. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  73. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  74. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  75. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  76. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  77. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  79. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  80. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  81. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  82. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  83. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  84. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  85. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  86. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +61 -51
  87. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  88. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  89. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  90. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  91. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  92. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +23 -37
  93. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +71 -52
  94. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  95. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  96. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  97. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  98. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +16 -16
  99. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +261 -116
  100. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  101. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  102. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  103. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  104. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +121 -7
  105. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4750 -1161
  106. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  107. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  108. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  109. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  110. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  111. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +6 -3
  112. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +112 -45
  113. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  114. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  115. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -11
  116. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +165 -49
  117. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  118. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  119. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  120. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  121. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  122. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  123. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  124. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  125. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  126. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +88 -24
  127. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2276 -842
  128. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  129. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  130. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +178 -82
  131. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2438 -645
  132. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  133. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  134. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  135. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  136. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  137. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  138. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  139. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  140. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  141. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  142. package/node_modules/@llblab/pi-telegram/docs/architecture.md +43 -35
  143. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  144. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  145. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  146. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  147. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  148. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  149. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  150. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  151. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +390 -82
  152. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +262 -99
  153. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  154. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  155. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  156. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  157. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  158. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  159. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  160. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  161. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  162. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1209 -431
  163. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  164. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  165. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  166. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5329 -1712
  167. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  168. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  169. package/node_modules/@llblab/pi-telegram/lib/locks.ts +703 -212
  170. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  171. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  172. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  173. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  174. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  175. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  176. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  177. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  178. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  179. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  180. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  181. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  182. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  183. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +125 -125
  184. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  185. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  186. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  187. package/node_modules/@llblab/pi-telegram/lib/polling.ts +165 -149
  188. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  189. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  190. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  191. package/node_modules/@llblab/pi-telegram/lib/queue.ts +523 -289
  192. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  193. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  194. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  195. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8527 -2149
  196. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  197. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  198. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  199. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  200. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  201. package/node_modules/@llblab/pi-telegram/lib/sync.ts +488 -359
  202. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  203. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +322 -122
  204. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  205. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  206. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  207. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  208. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  209. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5329 -1817
  210. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  211. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  212. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5194 -1600
  213. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  214. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  215. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  216. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  217. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  218. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  219. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  220. package/package.json +3 -3
  221. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  222. package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
@@ -4,9 +4,9 @@
4
4
  * Owns native assistant replies, rendered UI delivery, guest placeholder rotation, reply transport wiring, and plain text replies
5
5
  */
6
6
  import { assertTelegramInlineKeyboardCallbackData } from "./keyboard.js";
7
- import { getTelegramTargetThreadParams, } from "./target.js";
8
- import { isTelegramApiCommitUnknownError } from "./telegram-api.js";
9
7
  import { renderTelegramMessage, } from "./rendering.js";
8
+ import { getTelegramTargetThreadParams, } from "./target.js";
9
+ import { assertTelegramApiCallAuthority, isTelegramApiCommitUnknownError, TelegramApiAuthorityError, } from "./telegram-api.js";
10
10
  export { renderTelegramMessage, };
11
11
  export function renderTelegramMarkdownToHtmlDraft(markdown) {
12
12
  return renderTelegramMessage(markdown, { mode: "markdown" })
@@ -15,21 +15,6 @@ export function renderTelegramMarkdownToHtmlDraft(markdown) {
15
15
  }
16
16
  export const TELEGRAM_RICH_MESSAGE_MAX_CHARS = 32768;
17
17
  export const TELEGRAM_RICH_MESSAGE_MAX_BLOCKS = 500;
18
- export function createReplyDedupRuntime() {
19
- const replied = new Map();
20
- return {
21
- shouldReply(promptMessageId) {
22
- if (replied.has(promptMessageId))
23
- return false;
24
- replied.set(promptMessageId, true);
25
- return true;
26
- },
27
- reset() {
28
- replied.clear();
29
- },
30
- };
31
- }
32
- // --- Transport-level dedup ---
33
18
  const lastRepliedToMessageIdByTarget = new Map();
34
19
  const replyDedupPreservedOnNextReset = new Map();
35
20
  let replyDedupGeneration = 0;
@@ -80,9 +65,11 @@ export async function withTelegramReplyParameters(chatId, messageId, target, sen
80
65
  return await send(parameters);
81
66
  }
82
67
  catch (error) {
83
- if (parameters && !isTelegramApiCommitUnknownError(error)
84
- && generation === replyDedupGeneration
85
- && lastRepliedToMessageIdByTarget.get(key) === messageId) {
68
+ if (parameters &&
69
+ !isTelegramApiCommitUnknownError(error) &&
70
+ !(error instanceof TelegramApiAuthorityError && error.requestIssued) &&
71
+ generation === replyDedupGeneration &&
72
+ lastRepliedToMessageIdByTarget.get(key) === messageId) {
86
73
  if (previous === undefined)
87
74
  lastRepliedToMessageIdByTarget.delete(key);
88
75
  else
@@ -172,32 +159,49 @@ export function buildTelegramReplyTransport(deps) {
172
159
  };
173
160
  }
174
161
  export async function sendTelegramRenderedChunks(chatId, chunks, deps, options) {
175
- assertTelegramInlineKeyboardCallbackData(options?.replyMarkup);
162
+ return sendTelegramChunkSequence(chatId, chunks, options?.replyToMessageId, options, deps.recordOwnership, (chunk, fields, callOptions) => deps.sendMessage({
163
+ chat_id: chatId,
164
+ text: chunk.text,
165
+ parse_mode: chunk.parseMode,
166
+ ...fields,
167
+ }, callOptions));
168
+ }
169
+ /**
170
+ * Ordered chunk delivery: only the first chunk replies, only the last carries markup, and authority is rechecked
171
+ * before issuance and after every response/ownership publication.
172
+ */
173
+ async function sendTelegramChunkSequence(chatId, chunks, replyToMessageId, options, recordOwnership, send) {
174
+ const target = options?.target && { ...options.target }, assertAuthority = options?.assertAuthority, replyMarkup = options?.replyMarkup;
175
+ assertTelegramInlineKeyboardCallbackData(replyMarkup);
176
176
  let lastMessageId;
177
177
  for (const [index, chunk] of chunks.entries()) {
178
- const sent = await withTelegramReplyParameters(chatId, index === 0 ? options?.replyToMessageId : undefined, options?.target, (replyParameters) => deps.sendMessage({
179
- chat_id: chatId,
180
- text: chunk.text,
181
- parse_mode: chunk.parseMode,
182
- reply_markup: index === chunks.length - 1 ? options?.replyMarkup : undefined,
183
- ...(replyParameters ? { reply_parameters: replyParameters } : {}),
184
- ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
185
- }));
186
- lastMessageId = sent.message_id;
187
- deps.recordOwnership?.({
188
- chatId,
189
- messageId: sent.message_id,
190
- target: options?.target,
178
+ assertTelegramApiCallAuthority(assertAuthority, false);
179
+ const sent = await withTelegramReplyParameters(chatId, index === 0 ? replyToMessageId : undefined, target, async (replyParameters) => {
180
+ const sent = await send(chunk, {
181
+ reply_markup: index === chunks.length - 1 ? replyMarkup : undefined,
182
+ ...(replyParameters ? { reply_parameters: replyParameters } : {}),
183
+ ...(target ? getTelegramTargetThreadParams(target) : {}),
184
+ }, assertAuthority ? { assertAuthority } : undefined);
185
+ assertTelegramApiCallAuthority(assertAuthority, true);
186
+ return sent;
191
187
  });
188
+ assertTelegramApiCallAuthority(assertAuthority, true);
189
+ lastMessageId = sent.message_id;
190
+ recordOwnership?.({ chatId, messageId: sent.message_id, target });
191
+ assertTelegramApiCallAuthority(assertAuthority, true);
192
192
  }
193
193
  return lastMessageId;
194
194
  }
195
195
  export async function editTelegramRenderedMessage(chatId, messageId, chunks, deps, options) {
196
+ options = { ...options, target: options?.target && { ...options.target } };
197
+ const assertAuthority = options.assertAuthority;
198
+ assertTelegramApiCallAuthority(assertAuthority, false);
196
199
  assertTelegramInlineKeyboardCallbackData(options?.replyMarkup);
197
200
  if (chunks.length === 0)
198
201
  return messageId;
199
202
  const [firstChunk, ...remainingChunks] = chunks;
200
- deps.recordOwnership?.({ chatId, messageId, target: options?.target });
203
+ if (!assertAuthority)
204
+ deps.recordOwnership?.({ chatId, messageId, target: options.target });
201
205
  await deps.editMessage({
202
206
  chat_id: chatId,
203
207
  message_id: messageId,
@@ -205,22 +209,29 @@ export async function editTelegramRenderedMessage(chatId, messageId, chunks, dep
205
209
  parse_mode: firstChunk.parseMode,
206
210
  reply_markup: remainingChunks.length === 0 ? options?.replyMarkup : undefined,
207
211
  ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
208
- });
212
+ }, assertAuthority ? { assertAuthority } : undefined);
213
+ assertTelegramApiCallAuthority(assertAuthority, true);
214
+ if (assertAuthority)
215
+ deps.recordOwnership?.({ chatId, messageId, target: options.target });
216
+ assertTelegramApiCallAuthority(assertAuthority, true);
209
217
  if (remainingChunks.length > 0) {
210
218
  return sendTelegramRenderedChunks(chatId, remainingChunks, deps, {
211
219
  replyMarkup: options?.replyMarkup,
212
220
  target: options?.target,
221
+ assertAuthority,
213
222
  });
214
223
  }
215
224
  return messageId;
216
225
  }
217
226
  export async function sendTelegramPlainReply(text, deps, options) {
227
+ options = { ...options, target: options?.target && { ...options.target } };
218
228
  const chunks = deps.renderTelegramMessage(text, {
219
229
  mode: options?.parseMode === "HTML" ? "html" : "plain",
220
230
  });
221
231
  return deps.sendRenderedChunks(chunks, {
222
232
  target: options?.target,
223
233
  replyToMessageId: options?.replyToMessageId,
234
+ assertAuthority: options.assertAuthority,
224
235
  });
225
236
  }
226
237
  function normalizeIndentedTelegramNativeMarkdownList(line) {
@@ -242,22 +253,28 @@ function normalizeTelegramNativeMarkdownLine(line) {
242
253
  result = result.replace(/(^|[^\\$])\$([A-Z][A-Z0-9]{1,})(?!\$)(?=\b|[.,;:)/-])/g, (_match, prefix, ticker) => `${prefix}\\$${ticker}`);
243
254
  return result.replace(/\u0000(\d+)\u0000/g, (_match, index) => codeSpans[Number(index)] ?? "");
244
255
  }
256
+ export function parseTelegramMarkdownFenceOpening(line) {
257
+ const markerText = line.match(/^ {0,3}(`{3,}|~{3,})/)?.[1];
258
+ return markerText
259
+ ? { marker: markerText[0], length: markerText.length }
260
+ : undefined;
261
+ }
262
+ export function isTelegramMarkdownFenceClosing(line, fence) {
263
+ return new RegExp(`^ {0,3}${fence.marker}{${fence.length},}\\s*$`).test(line);
264
+ }
265
+ /** Fence state after one line: outside a fence a marker opens one; inside, a matching marker closes it. */
266
+ function advanceTelegramMarkdownFence(fence, line) {
267
+ if (!fence)
268
+ return parseTelegramMarkdownFenceOpening(line);
269
+ return isTelegramMarkdownFenceClosing(line, fence) ? undefined : fence;
270
+ }
245
271
  function hasClosingDisplayMathDelimiter(lines, startIndex) {
246
272
  let fence;
247
273
  for (let index = startIndex + 1; index < lines.length; index += 1) {
248
274
  const line = lines[index] ?? "";
249
- const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
250
275
  if (!fence && line.trim() === "$$")
251
276
  return true;
252
- if (!fence && fenceMatch) {
253
- const markerText = fenceMatch[1] ?? "```";
254
- fence = { marker: markerText[0], length: markerText.length };
255
- continue;
256
- }
257
- if (fence &&
258
- new RegExp(`^ {0,3}${fence.marker}{${fence.length},}\\s*$`).test(line)) {
259
- fence = undefined;
260
- }
277
+ fence = advanceTelegramMarkdownFence(fence, line);
261
278
  }
262
279
  return false;
263
280
  }
@@ -267,7 +284,6 @@ export function normalizeTelegramNativeMarkdown(markdown) {
267
284
  let displayMath = false;
268
285
  return lines
269
286
  .map((line, index) => {
270
- const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
271
287
  const inFence = fence !== undefined;
272
288
  if (!inFence && line.trim() === "$$") {
273
289
  if (displayMath) {
@@ -281,17 +297,9 @@ export function normalizeTelegramNativeMarkdown(markdown) {
281
297
  }
282
298
  if (displayMath)
283
299
  return line;
284
- if (!inFence && fenceMatch) {
285
- const markerText = fenceMatch[1] ?? "```";
286
- fence = {
287
- marker: markerText[0],
288
- length: markerText.length,
289
- };
290
- return line;
291
- }
292
- if (inFence &&
293
- new RegExp(`^ {0,3}${fence?.marker}{${fence?.length},}\\s*$`).test(line)) {
294
- fence = undefined;
300
+ const nextFence = advanceTelegramMarkdownFence(fence, line);
301
+ if (nextFence !== fence) {
302
+ fence = nextFence;
295
303
  return line;
296
304
  }
297
305
  if (!inFence)
@@ -349,21 +357,12 @@ function splitTelegramNativeMarkdownBlocks(markdown) {
349
357
  current.length = 0;
350
358
  };
351
359
  for (const line of markdown.split("\n")) {
352
- const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
353
360
  if (!fence && line.trim().length === 0) {
354
361
  flush();
355
362
  continue;
356
363
  }
357
364
  current.push(line);
358
- if (!fence && fenceMatch) {
359
- const markerText = fenceMatch[1] ?? "```";
360
- fence = { marker: markerText[0], length: markerText.length };
361
- continue;
362
- }
363
- if (fence &&
364
- new RegExp(`^ {0,3}${fence.marker}{${fence.length},}\\s*$`).test(line)) {
365
- fence = undefined;
366
- }
365
+ fence = advanceTelegramMarkdownFence(fence, line);
367
366
  }
368
367
  flush();
369
368
  return blocks;
@@ -376,21 +375,12 @@ function splitTelegramNativeMarkdownCountedBlocks(block) {
376
375
  let current = [];
377
376
  let fence;
378
377
  for (const line of block.split("\n")) {
379
- const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
380
378
  if (!fence && current.length >= TELEGRAM_RICH_MESSAGE_MAX_BLOCKS) {
381
379
  chunks.push(current.join("\n"));
382
380
  current = [];
383
381
  }
384
382
  current.push(line);
385
- if (!fence && fenceMatch) {
386
- const markerText = fenceMatch[1] ?? "```";
387
- fence = { marker: markerText[0], length: markerText.length };
388
- continue;
389
- }
390
- if (fence &&
391
- new RegExp(`^ {0,3}${fence.marker}{${fence.length},}\\s*$`).test(line)) {
392
- fence = undefined;
393
- }
383
+ fence = advanceTelegramMarkdownFence(fence, line);
394
384
  }
395
385
  if (current.length > 0)
396
386
  chunks.push(current.join("\n"));
@@ -427,12 +417,10 @@ function splitTelegramNativeMarkdownLongFenceBlock(block) {
427
417
  const lines = block.split("\n");
428
418
  const opening = lines[0] ?? "";
429
419
  const closing = lines[lines.length - 1] ?? "";
430
- const openingMatch = opening?.match(/^ {0,3}(`{3,}|~{3,})/);
431
- if (!openingMatch || !closing || lines.length < 2)
420
+ const openingFence = parseTelegramMarkdownFenceOpening(opening);
421
+ if (!openingFence || !closing || lines.length < 2)
432
422
  return undefined;
433
- const markerText = openingMatch[1] ?? "```";
434
- const marker = markerText[0];
435
- if (!new RegExp(`^ {0,3}${marker}{${markerText.length},}\\s*$`).test(closing)) {
423
+ if (!isTelegramMarkdownFenceClosing(closing, openingFence)) {
436
424
  return undefined;
437
425
  }
438
426
  const maxContentLength = TELEGRAM_RICH_MESSAGE_MAX_CHARS - opening.length - closing.length - 2;
@@ -478,33 +466,24 @@ function findTelegramNativeMarkdownSplitIndex(text, hardLimit = TELEGRAM_RICH_ME
478
466
  return hardLimit;
479
467
  }
480
468
  export async function sendTelegramNativeMarkdownReply(chatId, replyToMessageId, markdown, deps, options) {
481
- assertTelegramInlineKeyboardCallbackData(options?.replyMarkup);
482
- let lastMessageId;
483
- const chunks = splitTelegramNativeMarkdown(markdown);
484
- for (const [index, chunk] of chunks.entries()) {
485
- const sent = await withTelegramReplyParameters(chatId, index === 0 ? replyToMessageId : undefined, options?.target, (replyParameters) => deps.sendRichMessage({
486
- chat_id: chatId,
487
- rich_message: { markdown: chunk },
488
- reply_markup: index === chunks.length - 1 ? options?.replyMarkup : undefined,
489
- ...(replyParameters ? { reply_parameters: replyParameters } : {}),
490
- ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
491
- }));
492
- lastMessageId = sent.message_id;
493
- deps.recordOwnership?.({
494
- chatId,
495
- messageId: sent.message_id,
496
- target: options?.target,
497
- });
498
- }
499
- return lastMessageId;
469
+ return sendTelegramChunkSequence(chatId, splitTelegramNativeMarkdown(markdown), replyToMessageId, options, deps.recordOwnership, (chunk, fields, callOptions) => deps.sendRichMessage({ chat_id: chatId, rich_message: { markdown: chunk }, ...fields }, callOptions));
500
470
  }
501
471
  async function sendTelegramNativeRichMessage(chatId, richMessage, deps, options) {
472
+ options = { ...options, target: options?.target && { ...options.target } };
473
+ const assertAuthority = options.assertAuthority;
474
+ assertTelegramApiCallAuthority(assertAuthority, false);
502
475
  const sent = await deps.sendRichMessage({
503
476
  chat_id: chatId,
504
477
  rich_message: richMessage,
505
478
  ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
479
+ }, assertAuthority ? { assertAuthority } : undefined);
480
+ assertTelegramApiCallAuthority(assertAuthority, true);
481
+ deps.recordOwnership?.({
482
+ chatId,
483
+ messageId: sent.message_id,
484
+ target: options?.target,
506
485
  });
507
- deps.recordOwnership?.({ chatId, messageId: sent.message_id, target: options?.target });
486
+ assertTelegramApiCallAuthority(assertAuthority, true);
508
487
  return sent.message_id;
509
488
  }
510
489
  export function createTelegramRenderedMessageDeliveryRuntime(deps) {
@@ -531,17 +510,23 @@ export function createTelegramRenderedMessageRuntime(deps) {
531
510
  renderTelegramMessage: deps.renderTelegramMessage,
532
511
  sendRenderedChunks: (chunks, chunkOptions) => deps.replyTransport.sendRenderedChunks(chatId, chunks, {
533
512
  target: chunkOptions?.target,
513
+ assertAuthority: chunkOptions?.assertAuthority,
534
514
  replyToMessageId: chunkOptions?.replyToMessageId ?? replyToMessageId,
535
515
  }),
536
516
  }, options);
537
517
  },
538
518
  sendMarkdownReply: async (chatId, replyToMessageId, markdown, options) => {
519
+ options = {
520
+ ...options,
521
+ target: options?.target && { ...options.target },
522
+ };
539
523
  const renderingMode = deps.getAssistantRenderingMode?.() ?? "rich";
540
524
  if (renderingMode === "html") {
541
525
  return deps.replyTransport.sendRenderedChunks(chatId, deps.renderTelegramMessage(markdown, { mode: "markdown" }), {
542
526
  replyMarkup: options?.replyMarkup,
543
527
  target: options?.target,
544
528
  replyToMessageId,
529
+ assertAuthority: options.assertAuthority,
545
530
  });
546
531
  }
547
532
  return sendTelegramNativeMarkdownReply(chatId, replyToMessageId, markdown, {
@@ -549,14 +534,23 @@ export function createTelegramRenderedMessageRuntime(deps) {
549
534
  sendRichMessage: deps.sendRichMessage,
550
535
  }, options);
551
536
  },
552
- editInteractiveMessage: async (chatId, messageId, text, mode, replyMarkup) => {
553
- await deps.replyTransport.editRenderedMessage(chatId, messageId, deps.renderTelegramMessage(text, { mode }), { replyMarkup });
537
+ editInteractiveMessage: async (chatId, messageId, text, mode, replyMarkup, options) => {
538
+ options = {
539
+ ...options,
540
+ target: options?.target && { ...options.target },
541
+ };
542
+ await deps.replyTransport.editRenderedMessage(chatId, messageId, deps.renderTelegramMessage(text, { mode }), { ...options, replyMarkup });
554
543
  },
555
544
  sendInteractiveMessage: async (chatId, text, mode, replyMarkup, options) => {
545
+ options = {
546
+ ...options,
547
+ target: options?.target && { ...options.target },
548
+ };
556
549
  return deps.replyTransport.sendRenderedChunks(chatId, deps.renderTelegramMessage(text, { mode }), {
557
550
  replyMarkup,
558
551
  target: options?.target,
559
552
  replyToMessageId: options?.replyToMessageId,
553
+ assertAuthority: options.assertAuthority,
560
554
  });
561
555
  },
562
556
  sendSectionRichMessage: (chatId, message, options) => sendTelegramNativeRichMessage(chatId, message, {
@@ -565,17 +559,6 @@ export function createTelegramRenderedMessageRuntime(deps) {
565
559
  }, options),
566
560
  };
567
561
  }
568
- // --- Dedup-wrapped Reply Wrappers ---
569
- /** Wrap a sendTextReply with reply dedup so only the first message
570
- * in a turn carries reply metadata. */
571
- export function dedupSendTextReply(dedup, inner) {
572
- return async (chatId, replyToMessageId, text, options) => {
573
- const effectiveReplyTo = dedup.shouldReply(replyToMessageId)
574
- ? replyToMessageId
575
- : undefined;
576
- return inner(chatId, effectiveReplyTo, text, options);
577
- };
578
- }
579
562
  /**
580
563
  * Guest reply sender: answers guest queries with native Rich Markdown content.
581
564
  * Guest queries use InlineQueryResult input_message_content rather than chat
@@ -43,6 +43,71 @@ export declare function resolveTelegramGuestPromptPeer(input: {
43
43
  export declare function resolveTelegramGuestFileScope(input: Parameters<typeof resolveTelegramGuestPromptPeer>[0]): string;
44
44
  import * as Threads from "./threads.ts";
45
45
  import * as Updates from "./updates.ts";
46
+ /** Prepare the selected leader's exact retained originals; no copy, adoption, acceptance proof or dispatch. */
47
+ export declare function prepareTelegramLiveLeaderOriginal(input: {
48
+ request: Threads.TelegramWorkspaceRestoreRequest;
49
+ messages: readonly unknown[];
50
+ isCurrent(): boolean;
51
+ }): Updates.TelegramLiveDeferredInputPreparation | undefined;
52
+ export type TelegramLiveRebindCleanupIssue = {
53
+ status: "not-ready";
54
+ } | {
55
+ status: "finished";
56
+ operationId: string;
57
+ cleanup: ThreadReconciler.TelegramLiveRebindCleanupOutcome;
58
+ recorded: boolean;
59
+ };
60
+ /** Default live-rebind cleanup pacing: quick early attempts, then minutely, within a 15-minute window. */
61
+ export declare const TELEGRAM_LIVE_REBIND_CLEANUP_SCHEDULE: Readonly<{
62
+ delaysMs: readonly number[];
63
+ intervalMs: 60000;
64
+ windowMs: number;
65
+ }>;
66
+ /** Chooser-scoped saved command reference; it is never retained after the chooser record. */
67
+ type TelegramLiveRebindSelectedCommandReference = {
68
+ operationId: string;
69
+ preparedSource: Bus.TelegramBusPreparedCommandSource;
70
+ selectedCommand: Bus.TelegramBusSelectedCommandInput;
71
+ };
72
+ /** One warm live attempt with exact post-release peer donor settlement; no startup recovery, Restore ACK or cleanup. */
73
+ export declare function createTelegramLiveRebindCoordinator(input: {
74
+ request: Threads.TelegramWorkspaceRestoreRequest;
75
+ /** Captures caller source/reference, admission, profile, transport and Pi lifetime. */
76
+ authority: Threads.TelegramWorkspaceRestoreAuthority;
77
+ messages: readonly unknown[];
78
+ restoreStore: Threads.TelegramWorkspaceRestore;
79
+ threadStore: Pick<Threads.TelegramTopicTargetStore, "withWorkspaceRestoreSnapshot">;
80
+ getRecipient(): (Threads.TelegramWorkspaceRestoreRecipient & {
81
+ bindingKey: string;
82
+ }) | undefined;
83
+ leader?: {
84
+ apply(intent: Threads.TelegramWorkspaceLiveRebindIntent, mode: "apply" | "inspect", isCurrent: () => boolean): Promise<void>;
85
+ /** Synchronous local identity only; safe inside a Workspace publication fence. */
86
+ isApplied(intent: Threads.TelegramWorkspaceLiveRebindIntent): boolean;
87
+ } & ({
88
+ /** Confirm ordinary queue admission of these same bound messages, never handler replay. */
89
+ continue(messages: readonly unknown[], isCurrent: () => boolean): Promise<boolean>;
90
+ /** Observe only the same issued admission through its existing receipt owner; never re-enqueue. */
91
+ observeAdmission?(): boolean;
92
+ complete?: never;
93
+ } | {
94
+ /** Issue one semantic completion; the adapter owns detached delivery and errors, not a removal ACK. */
95
+ complete(messages: readonly unknown[], isCurrent: () => boolean): void;
96
+ continue?: never;
97
+ });
98
+ follower?: {
99
+ run: ReturnType<typeof Bus.createTelegramBusLiveRebindController>;
100
+ /** Explicit staged singleton command branch; absence preserves ordinary live input behavior. */
101
+ selectedCommand?: Bus.TelegramBusSelectedCommandInput;
102
+ };
103
+ recordRuntimeEvent?: (category: string, error: unknown, details: Record<string, unknown>) => void;
104
+ }): {
105
+ /** Correlation only; a later cleanup attempt rereads and revalidates the canonical row itself. */
106
+ operationId: string;
107
+ /** Body-free copy of the authenticated saved command reference: correlation only, never authority, an ACK or a cleanup permit. */
108
+ selectedCommandReference(): TelegramLiveRebindSelectedCommandReference | undefined;
109
+ advance(): Promise<"protected" | "unknown" | "released">;
110
+ };
46
111
  /** One admitted Restore attempt through recipient readiness, never source dispatch or cleanup. */
47
112
  export declare function advanceTelegramWorkspaceRestore(input: {
48
113
  request: Threads.TelegramWorkspaceRestoreRequest;
@@ -68,8 +133,8 @@ export declare function advanceTelegramWorkspaceRestore(input: {
68
133
  inspectOnly?: true;
69
134
  }): Promise<Threads.TelegramWorkspaceRestoreIntent | undefined>;
70
135
  export declare const TELEGRAM_ALL_TAB_COMMAND_MAX_AGE_MS: number;
71
- /** One user-facing answer for expired or previous-process routing controls. */
72
- export declare const TELEGRAM_ROUTING_CHOICE_EXPIRED = "\u231B Routing choice expired.";
136
+ /** The toast for expired or previous-process routing controls; the chooser notice is written separately. */
137
+ export declare const TELEGRAM_ROUTING_CHOICE_EXPIRED = "Routing choice expired";
73
138
  export declare function isTelegramAllTabCommandExpired(message: {
74
139
  date?: number;
75
140
  message_thread_id?: number;
@@ -121,6 +186,7 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
121
186
  captureWorkspaceExternalProtection?: ReturnType<typeof createTelegramWorkspaceExternalProtectionCapture>;
122
187
  /** Strict committed abandonment plus retained original; shared by Restore and temporary-input cancellation. */
123
188
  inspectRestoreSourceAbandonment?: (updateId: number, journalBindingKey: string) => Threads.TelegramTemporaryThreadCancellationEvidence | undefined;
189
+ inspectRoutingInputGroupExpiry?: (input: Threads.TelegramTemporaryThreadInput) => readonly Threads.TelegramTemporaryThreadCancellationEvidence[] | undefined;
124
190
  /** Strict active-journal observation only; a hint or missing source never substitutes for this ACK. */
125
191
  inspectRestoreSourceCompletion?: (expected: Updates.TelegramDeferredSourceEvidence & {
126
192
  completionSha256: string;
@@ -131,17 +197,36 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
131
197
  journalBindingKey: string;
132
198
  }) => TelegramUpdateJournalQueuedReceiptEvidence | undefined;
133
199
  hasWorkspaceRestoreAuthority?: () => boolean;
200
+ /** Exact live root authority; capability advertisement alone cannot replace owned profile/session/recipient fences. */
201
+ hasWorkspaceLiveRebindAuthority?: () => boolean;
134
202
  /** Strict complete namespace plus exact current/historical references; only an empty result clears journal protection. */
135
- inspectTemporaryThreadSources?: (target: Queue.TelegramQueueTarget, requiredJournalBindingKeys: readonly string[]) => readonly number[] | undefined;
203
+ inspectTemporaryThreadSources?: (target: Queue.TelegramQueueTarget, requiredJournalBindingKeys: readonly string[], ownInputs?: readonly Threads.TelegramTemporaryThreadInput[]) => readonly number[] | undefined;
136
204
  /** Quiet period after the last cancelled input before one cleanup attempt; defaults to 1000 ms. */
137
205
  temporaryThreadCleanupDelayMs?: number;
206
+ /** Live-rebind cleanup attempt pacing; the bounded window ends in `not-issued`, never a time-based deletion. */
207
+ liveRebindCleanupSchedule?: {
208
+ delaysMs: readonly number[];
209
+ intervalMs: number;
210
+ windowMs: number;
211
+ };
138
212
  getSessionGeneration?: () => number;
139
213
  workspaceRestoreRecipient?: {
140
214
  getSessionId: (ctx: TContext) => string | undefined;
141
215
  getCwd: (ctx: TContext) => string | undefined;
142
216
  getLeaderIdentity: Threads.TelegramLeaderThreadStateRuntime["getIdentity"];
217
+ /** Read-only current leader work; source/chooser lifetime is not recipient authority. */
218
+ observeLeaderWork?: (oldTarget: Queue.TelegramQueueTarget & {
219
+ threadId: number;
220
+ }, ctx: TContext) => Bus.TelegramBusLiveRebindWorkState;
143
221
  followerRegistry: Pick<Bus.TelegramBusFollowerRegistry, "get" | "register">;
144
222
  runFollower: ReturnType<typeof Bus.createTelegramBusWorkspaceRestoreController>;
223
+ liveFollower?: {
224
+ /** Pure trusted assembly availability (Commands/native/menu and local protocol), never future target activation. */
225
+ isSelectedCommandAvailable?(follower: Readonly<Bus.TelegramBusFollowerView>, name: string): boolean;
226
+ /** Exact session journal identity from the existing registration/journal owners, never the profile routing key. */
227
+ getJournalBindingKey(follower: Bus.TelegramBusFollowerView): string | undefined;
228
+ run: ReturnType<typeof Bus.createTelegramBusLiveRebindController>;
229
+ };
145
230
  };
146
231
  bridgeRuntime: TelegramBridgeRuntime;
147
232
  activeTurnRuntime: Queue.TelegramActiveTurnStore;
@@ -153,9 +238,11 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
153
238
  currentModelRuntime: Model.CurrentModelRuntime<TContext, TModel>;
154
239
  modelSwitchController: Model.TelegramModelSwitchController<TContext, Model.ScopedTelegramModel<TModel>>;
155
240
  menuActions: Menu.TelegramMenuActionRuntime<TContext, TModel>;
241
+ /** Thinking controls refuse while the active Telegram turn expects a voice reply. */
242
+ isVoiceReplyActive?: () => boolean;
156
243
  updateSettingsMenuMessage?: (state: Menu.TelegramModelMenuState<TModel>, ctx: TContext) => Promise<void>;
157
- openQueueMenu: (chatId: number, replyToMessageId: number, ctx: TContext) => Promise<void>;
158
- openSettingsMenu?: (chatId: number, replyToMessageId: number, ctx: TContext) => Promise<void>;
244
+ openQueueMenu: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
245
+ openSettingsMenu?: (chatId: number, replyToMessageId: number, ctx: TContext, threadId?: number, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
159
246
  settingsMenuCallbackHandler?: (query: TCallbackQuery, ctx: TContext) => Promise<boolean>;
160
247
  queueMenuCallbackHandler: (query: TCallbackQuery, ctx: TContext) => Promise<boolean>;
161
248
  buttonActionStore?: OutboundHandlers.TelegramButtonActionStore;
@@ -183,12 +270,16 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
183
270
  };
184
271
  }) => void;
185
272
  stopTypingLoop?: () => void;
186
- answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
187
- editInteractiveMessage?: (chatId: number, messageId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: Menu.TelegramReplyMarkup) => Promise<void>;
273
+ answerCallbackQuery: (callbackQueryId: string, text?: string, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
274
+ editInteractiveMessage?: (chatId: number, messageId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: Menu.TelegramReplyMarkup, options?: {
275
+ target?: Queue.TelegramQueueTarget;
276
+ assertAuthority?: TelegramApiCallOptions["assertAuthority"];
277
+ }) => Promise<void>;
188
278
  editMessageReplyMarkup?: (chatId: number, messageId: number, replyMarkup: OutboundHandlers.TelegramOutboundButtonMarkup) => Promise<void>;
189
279
  sendInteractiveMessage?: (chatId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: Menu.TelegramReplyMarkup, options?: {
190
280
  target?: Queue.TelegramQueueTarget;
191
281
  replyToMessageId?: number;
282
+ assertAuthority?: TelegramApiCallOptions["assertAuthority"];
192
283
  }) => Promise<number | undefined>;
193
284
  deleteMessage?: (chatId: number, messageId: number) => Promise<void>;
194
285
  answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
@@ -201,8 +292,11 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
201
292
  sendTextReply: (chatId: number, replyToMessageId: number, text: string, options?: {
202
293
  parseMode?: "HTML";
203
294
  target?: Queue.TelegramQueueTarget;
295
+ assertAuthority?: TelegramApiCallOptions["assertAuthority"];
204
296
  }) => Promise<number | undefined>;
205
297
  setMyCommands: Commands.TelegramBotCommandRegistrationDeps["setMyCommands"];
298
+ /** Captures independent recipient authority, never the source execution or dialog handle. */
299
+ captureThreadNameRecipientAuthority?: (target: Queue.TelegramQueueTarget, ctx: TContext) => (() => void) | undefined;
206
300
  validateThreadName?: (threadName: string) => string | undefined;
207
301
  renameCurrentThread?: Commands.TelegramThreadDisplayNameRenamePort;
208
302
  resetCurrentThreadName?: Commands.TelegramThreadDisplayNameResetPort;
@@ -222,6 +316,7 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
222
316
  onError: (error: unknown) => void;
223
317
  }) => void;
224
318
  recordRuntimeEvent?: (category: string, error: unknown, details?: Record<string, unknown>) => void;
319
+ beginCommandEffectWork?: Commands.TelegramCommandRuntimeDeps<TMessage, TContext>["beginCommandEffectWork"];
225
320
  sectionRegistry?: TelegramSectionRegistry;
226
321
  sendSectionRichMessage?: (chatId: number, message: TelegramInputRichMessage, options?: {
227
322
  target?: {
@@ -248,6 +343,25 @@ export declare function createTelegramInboundRouteRuntime<TUpdate extends Update
248
343
  onUpdateCompleted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onUpdateCompleted"]>;
249
344
  onWorkspaceRestoreRecipientObserved(follower: Bus.TelegramBusFollowerView, isCurrent: () => boolean, ctx: TContext | undefined): Promise<void> | undefined;
250
345
  waitForRestoreSettlement(): Promise<void>;
346
+ /** Commands-owned registry port for dormant scoped recipient assembly, not ordinary update replay. */
347
+ prepareHeldCommand: ReturnType<typeof Commands.createTelegramCommandHandlerTargetRuntime<TMessage, TContext>>["prepareHeldCommand"];
348
+ canPrepareHeldCommand: ReturnType<typeof Commands.createTelegramCommandHandlerTargetRuntime<TMessage, TContext>>["canPrepareHeldCommand"];
349
+ /** Fresh body-free recipient observation only; never reconstructs an attempt or grants cleanup. */
350
+ observeLiveRebindLeaderWork(intent: Threads.TelegramWorkspaceLiveRebindIntent, ctx: TContext): Promise<Bus.TelegramBusLiveRebindWorkObservation | undefined>;
351
+ /** Fresh non-destructive candidate only; no retained idle, cleanup action or deletion grant. */
352
+ prepareLiveRebindLeaderCleanup(intent: Threads.TelegramWorkspaceLiveRebindIntent, ctx: TContext): Promise<ThreadReconciler.TelegramLiveRebindCleanupPreparation | undefined>;
353
+ /** Requires the authenticated peer's exact retained released carrier; never recreates save/apply/release. */
354
+ prepareLiveRebindFollowerCleanup(intent: Threads.TelegramWorkspaceLiveRebindIntent, ctx: TContext): Promise<ThreadReconciler.TelegramLiveRebindCleanupPreparation | undefined>;
355
+ /**
356
+ * One cleanup attempt under fresh admission: a fresh clear sample, then the durable issue marker, one unretried
357
+ * deletion and the terminal record. `not-ready` leaves the released row unissued; `undefined` is lost authority.
358
+ */
359
+ issueLiveRebindLeaderCleanup(intent: Threads.TelegramWorkspaceLiveRebindIntent, ctx: TContext): Promise<TelegramLiveRebindCleanupIssue | undefined>;
360
+ issueLiveRebindFollowerCleanup(intent: Threads.TelegramWorkspaceLiveRebindIntent, ctx: TContext): Promise<TelegramLiveRebindCleanupIssue | undefined>;
361
+ /** Coordinator-only prompt admission; caller retains Workspace/canonical and exact recipient authority. */
362
+ continueLiveRebindPrompt(messages: readonly TMessage[], ctx: TContext, target: Queue.TelegramQueueTarget & {
363
+ threadId: number;
364
+ }, isCurrent: () => boolean): Promise<boolean>;
251
365
  };
252
366
  export interface TelegramAssistantOutputAuthority<TTransportStamp> {
253
367
  transportStamp: TTransportStamp;