@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
@@ -5,11 +5,12 @@
5
5
  */
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { isDeepStrictEqual } from "node:util";
8
- import { isWireRecord as isTelegramUpdateAdmissionRecord, isNonEmptyWireString as isTelegramUpdateAdmissionString, } from "./wire.js";
9
- import { createTelegramPrivateTarget, createTelegramThreadTarget, } from "./target.js";
10
- import { TELEGRAM_UPDATE_JOURNAL_VERSION, TELEGRAM_UPDATE_JOURNAL_EXCLUSION_VERSION, TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION, isTelegramUpdateJournalLegacyFamilyVersion, TELEGRAM_UPDATE_JOURNAL_FAILURE_CLASS_MAX_LENGTH, TELEGRAM_UPDATE_JOURNAL_FAILURE_SUMMARY_MAX_LENGTH, TELEGRAM_UPDATE_JOURNAL_QUEUE_OWNER_ID_MAX_LENGTH, areTelegramUpdateJournalQueueOwnersEqual, createTelegramUpdateJournalEntryDigest, getTelegramUpdateJournalBindingPath, isTelegramUpdateJournalQueueOwnerProcess, parseTelegramUpdateJournalQueueOwner, } from "./journal.js";
11
- import { areTelegramQueueAdmissionReceiptsEqual, createTelegramQueueHandoff, removeTelegramQueueItemByReceipt, } from "./queue.js";
12
8
  import { createTelegramUserPairingRuntime, getTelegramAuthorizationState, } from "./config.js";
9
+ import { TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION, TELEGRAM_UPDATE_JOURNAL_EXCLUSION_VERSION, TELEGRAM_UPDATE_JOURNAL_FAILURE_CLASS_MAX_LENGTH, TELEGRAM_UPDATE_JOURNAL_FAILURE_SUMMARY_MAX_LENGTH, TELEGRAM_UPDATE_JOURNAL_QUEUE_OWNER_ID_MAX_LENGTH, TELEGRAM_UPDATE_JOURNAL_VERSION, areTelegramUpdateJournalQueueOwnersEqual, createTelegramUpdateJournalEntryDigest, doesTelegramJournalUpdateNameThread, isTelegramJournalLoneCallbackUpdate, getTelegramUpdateJournalBindingPath, isTelegramUpdateJournalLegacyFamilyVersion, isTelegramUpdateJournalQueueOwnerProcess, parseTelegramUpdateJournalQueueOwner, } from "./journal.js";
10
+ import { isPiStaleContextError } from "./pi.js";
11
+ import { areTelegramQueueAdmissionReceiptsEqual, createTelegramQueueHandoff, removeTelegramQueueItemByReceipt, } from "./queue.js";
12
+ import { createTelegramPrivateTarget, createTelegramThreadTarget, } from "./target.js";
13
+ import { isWireRecord as isTelegramUpdateAdmissionRecord, isNonEmptyWireString as isTelegramUpdateAdmissionString, } from "./wire.js";
13
14
  export const TELEGRAM_PRIORITY_REACTIONS = [
14
15
  { id: 10, name: "like", emoji: "👍" },
15
16
  { id: 11, name: "lightning", emoji: "⚡" },
@@ -26,9 +27,6 @@ export const TELEGRAM_REMOVAL_REACTIONS = [
26
27
  ];
27
28
  export const TELEGRAM_PRIORITY_REACTION_EMOJIS = TELEGRAM_PRIORITY_REACTIONS.map((reaction) => reaction.emoji);
28
29
  export const TELEGRAM_REMOVAL_REACTION_EMOJIS = TELEGRAM_REMOVAL_REACTIONS.map((reaction) => reaction.emoji);
29
- function isTelegramMessageIdList(value) {
30
- return Array.isArray(value) && value.every((item) => Number.isInteger(item));
31
- }
32
30
  export function normalizeTelegramReactionEmoji(emoji) {
33
31
  return emoji.replace(/\uFE0F/g, "");
34
32
  }
@@ -65,13 +63,6 @@ function getTelegramQueueReactionTransition(oldReactions, newReactions) {
65
63
  }
66
64
  return transition;
67
65
  }
68
- export function extractDeletedTelegramMessageIds(update) {
69
- const deletedBusinessMessageIds = update.deleted_business_messages?.message_ids;
70
- if (isTelegramMessageIdList(deletedBusinessMessageIds)) {
71
- return deletedBusinessMessageIds;
72
- }
73
- return [];
74
- }
75
66
  export function getTelegramTopicLifecycleUpdate(message) {
76
67
  if (!message ||
77
68
  typeof message.chat.id !== "number" ||
@@ -111,21 +102,18 @@ export function getAuthorizedTelegramCallbackQuery(update, allowedUserId) {
111
102
  return query;
112
103
  return query.from.id === allowedUserId ? query : undefined;
113
104
  }
114
- export function getAuthorizedTelegramMessage(update, allowedUserId) {
115
- const message = update.message;
105
+ function authorizeTelegramHumanMessage(message, allowedUserId) {
116
106
  if (!message || !message.from || message.from.is_bot)
117
107
  return undefined;
118
108
  if (message.chat.type === "private")
119
109
  return message;
120
110
  return message.from.id === allowedUserId ? message : undefined;
121
111
  }
112
+ export function getAuthorizedTelegramMessage(update, allowedUserId) {
113
+ return authorizeTelegramHumanMessage(update.message, allowedUserId);
114
+ }
122
115
  export function getAuthorizedTelegramEditedMessage(update, allowedUserId) {
123
- const message = update.edited_message;
124
- if (!message || !message.from || message.from.is_bot)
125
- return undefined;
126
- if (message.chat.type === "private")
127
- return message;
128
- return message.from.id === allowedUserId ? message : undefined;
116
+ return authorizeTelegramHumanMessage(update.edited_message, allowedUserId);
129
117
  }
130
118
  export function getAuthorizedTelegramGuestMessage(update) {
131
119
  const guestMessage = update.guest_message;
@@ -221,21 +209,15 @@ export function bindTelegramUpdateAdmissionSource(update, report, controls) {
221
209
  }
222
210
  /**
223
211
  * Conservative read-only census: any retained entry naming this Thread, in any nested Telegram object or state,
224
- * is unresolved custody. It cannot see updates still in transit before journal append.
212
+ * is unresolved custody, except a lone button tap, which carries no input (the in-flight Cancel itself).
213
+ * It cannot see updates still in transit before journal append.
225
214
  */
226
215
  export function collectTelegramJournalThreadUpdateIds(entries, target) {
227
- const matches = (value, depth) => {
228
- if (depth > 8 || !value || typeof value !== "object")
229
- return false;
230
- const record = value;
231
- if (record.message_thread_id === target.threadId) {
232
- const chat = record.chat;
233
- if (!chat || typeof chat !== "object" || chat.id === target.chatId)
234
- return true;
235
- }
236
- return Object.values(record).some(child => matches(child, depth + 1));
237
- };
238
- return entries.filter(entry => matches(entry.update, 0)).map(entry => entry.updateId).sort((left, right) => left - right);
216
+ return entries
217
+ .filter((entry) => doesTelegramJournalUpdateNameThread(entry.update, target) &&
218
+ !isTelegramJournalLoneCallbackUpdate(entry.update))
219
+ .map((entry) => entry.updateId)
220
+ .sort((left, right) => left - right);
239
221
  }
240
222
  export function collectTelegramAdmissionSourceUpdateIds(values) {
241
223
  const sourceUpdateIds = new Set();
@@ -253,15 +235,62 @@ export function inspectTelegramDeferredSource(value) {
253
235
  return undefined;
254
236
  return getTelegramUpdateAdmissionBinding(value)?.inspectSource?.();
255
237
  }
238
+ /** Read an exact current original for peer preparation without another payload authority. */
239
+ export function inspectTelegramDeferredSourceSnapshot(value) {
240
+ const execution = getTelegramUpdateExecutionFence(value);
241
+ if (execution?.isCurrent() !== true)
242
+ return undefined;
243
+ const snapshot = getTelegramUpdateAdmissionBinding(value)?.inspectSourceSnapshot?.();
244
+ return execution.isCurrent() ? snapshot : undefined;
245
+ }
246
+ /** Exact warm removal ACK only; reporting, source absence and a replaced worker cannot supply it. */
247
+ export function inspectTelegramDeferredSourceCompletion(value) {
248
+ const execution = getTelegramUpdateExecutionFence(value);
249
+ if (execution?.isCurrent() !== true)
250
+ return undefined;
251
+ const source = getTelegramUpdateAdmissionBinding(value)?.inspectCompletion?.();
252
+ return execution.isCurrent() && source ? { ...source } : undefined;
253
+ }
254
+ /** Reuse only live bound deferred originals; raw IDs or cold/adopted input cannot mint this carrier. */
255
+ export function prepareTelegramLiveDeferredInput(values, isCurrent) {
256
+ const carriers = [...values], first = carriers[0], execution = getTelegramUpdateExecutionFence(first);
257
+ const ids = collectTelegramAdmissionSourceUpdateIds(carriers);
258
+ if (!execution ||
259
+ ids.length !== carriers.length ||
260
+ !ids.length ||
261
+ !isCurrent() ||
262
+ carriers.some((value) => getTelegramUpdateExecutionFence(value)?.signal !== execution.signal ||
263
+ getTelegramUpdateExecutionFence(value)?.isCurrent() !== true))
264
+ return undefined;
265
+ const unsettled = () => carriers.every((value) => getTelegramUpdateAdmissionBinding(value)?.isSourceUnsettled?.() ===
266
+ true);
267
+ if (!unsettled())
268
+ return undefined;
269
+ const prepared = getTelegramUpdateAdmissionBinding(first)?.prepareLiveInput?.(ids, () => isCurrent() &&
270
+ carriers.every((value) => getTelegramUpdateExecutionFence(value)?.isCurrent() === true));
271
+ return (prepared && {
272
+ ...prepared,
273
+ isCurrent: () => unsettled() && prepared.isCurrent(),
274
+ confirmSaved: () => unsettled() && prepared.confirmSaved(),
275
+ beginRelease: (canRelease) => unsettled() && prepared.beginRelease(() => unsettled() && canRelease()),
276
+ settleTransferred: (canSettle) => unsettled()
277
+ ? prepared.settleTransferred(() => unsettled() && canSettle())
278
+ : "protected",
279
+ cancel: () => unsettled() && prepared.cancel(),
280
+ });
281
+ }
256
282
  /** A route-owned pre-disposition acceptance publisher; wraps only this carrier, never the shared worker binding. */
257
283
  export function bindTelegramUpdateCompletionAcceptance(value, publish) {
258
284
  const binding = getTelegramUpdateAdmissionBinding(value);
259
- if (!binding || getTelegramUpdateExecutionFence(value)?.isCurrent() !== true) {
285
+ if (!binding ||
286
+ getTelegramUpdateExecutionFence(value)?.isCurrent() !== true) {
260
287
  throw new TelegramUpdateAdmissionOutcomeError("Telegram completion acceptance requires a live source carrier.");
261
288
  }
262
289
  let retained;
263
290
  let queued = false;
264
- const scoped = { ...binding, report(outcome) {
291
+ const scoped = {
292
+ ...binding,
293
+ report(outcome) {
265
294
  if (outcome.kind !== "complete") {
266
295
  if (outcome.kind === "queued")
267
296
  queued = true;
@@ -277,19 +306,26 @@ export function bindTelegramUpdateCompletionAcceptance(value, publish) {
277
306
  }
278
307
  if (!retained) {
279
308
  const published = validateTelegramUpdateAdmissionOutcome({ kind: "complete", expectedSource: publish() }, binding.sourceUpdateId, new Set([binding.sourceUpdateId]));
280
- if (published.kind !== "complete" || !published.expectedSource?.completionSha256) {
309
+ if (published.kind !== "complete" ||
310
+ !published.expectedSource?.completionSha256) {
281
311
  throw new TelegramUpdateAdmissionOutcomeError("Telegram completion acceptance lacks its scoped journal proof.");
282
312
  }
283
313
  retained = published.expectedSource;
284
314
  }
285
315
  assertTelegramUpdateExecutionCurrent(value);
286
- if (outcome.expectedSource && (outcome.expectedSource.updateId !== retained.updateId ||
287
- outcome.expectedSource.journalBindingKey !== retained.journalBindingKey || outcome.expectedSource.sourceSha256 !== retained.sourceSha256 ||
288
- outcome.expectedSource.completionSha256 !== undefined && outcome.expectedSource.completionSha256 !== retained.completionSha256)) {
316
+ if (outcome.expectedSource &&
317
+ (outcome.expectedSource.updateId !== retained.updateId ||
318
+ outcome.expectedSource.journalBindingKey !==
319
+ retained.journalBindingKey ||
320
+ outcome.expectedSource.sourceSha256 !== retained.sourceSha256 ||
321
+ (outcome.expectedSource.completionSha256 !== undefined &&
322
+ outcome.expectedSource.completionSha256 !==
323
+ retained.completionSha256))) {
289
324
  throw new TelegramUpdateAdmissionOutcomeError("Telegram completion acceptance conflicts with its reported source.");
290
325
  }
291
326
  binding.report({ kind: "complete", expectedSource: { ...retained } });
292
- } };
327
+ },
328
+ };
293
329
  return carryTelegramUpdateExecutionFence(value, bindTelegramUpdateAdmissionCarrier(value, scoped));
294
330
  }
295
331
  /** Report source completion; true means reported, not a durable settlement acknowledgement. */
@@ -300,7 +336,10 @@ export function reportTelegramUpdateCompleted(value, expectedSource) {
300
336
  const execution = getTelegramUpdateExecutionFence(value);
301
337
  if (execution && !execution.isCurrent())
302
338
  return false;
303
- binding.report(validateTelegramUpdateAdmissionOutcome({ kind: "complete", ...(expectedSource !== undefined ? { expectedSource } : {}) }, binding.sourceUpdateId, new Set([binding.sourceUpdateId])));
339
+ binding.report(validateTelegramUpdateAdmissionOutcome({
340
+ kind: "complete",
341
+ ...(expectedSource !== undefined ? { expectedSource } : {}),
342
+ }, binding.sourceUpdateId, new Set([binding.sourceUpdateId])));
304
343
  return true;
305
344
  }
306
345
  export function reportTelegramUpdateDeferred(value) {
@@ -319,7 +358,7 @@ export function abandonTelegramDeferredUpdate(value, authority) {
319
358
  }
320
359
  /** Capability for chooser publication; the eventual action still requires exact deferred authority. */
321
360
  export function supportsTelegramDeferredAbandonment(value, journalBindingKey) {
322
- return getTelegramUpdateAdmissionBinding(value)?.supportsAbandonment?.(journalBindingKey) === true;
361
+ return (getTelegramUpdateAdmissionBinding(value)?.supportsAbandonment?.(journalBindingKey) === true);
323
362
  }
324
363
  /** Observe protected attempts through a current carrier; the UI still owns human authorization. */
325
364
  export function inspectTelegramAbandoningUpdates(value, request) {
@@ -330,31 +369,97 @@ export function inspectTelegramHistoricalInputs(value, request) {
330
369
  return getTelegramUpdateAdmissionBinding(value)?.inspectHistorical?.(request);
331
370
  }
332
371
  export function isTelegramHistoricalInput(value, matchesOriginal) {
333
- return getTelegramUpdateAdmissionBinding(value)?.isHistorical?.(matchesOriginal) === true;
372
+ return (getTelegramUpdateAdmissionBinding(value)?.isHistorical?.(matchesOriginal) === true);
334
373
  }
335
374
  /** Routing's last-boundary hold when ownership changed after early classification. */
336
375
  export function reportTelegramHistoricalRoutingReview(value) {
337
- if (!isTelegramHistoricalInput(value) || getTelegramUpdateExecutionFence(value)?.isCurrent() !== true)
376
+ if (!isTelegramHistoricalInput(value) ||
377
+ getTelegramUpdateExecutionFence(value)?.isCurrent() !== true)
338
378
  return false;
339
- getTelegramUpdateAdmissionBinding(value).report({ kind: "deferred", routingReview: true });
379
+ getTelegramUpdateAdmissionBinding(value).report({
380
+ kind: "deferred",
381
+ routingReview: true,
382
+ });
340
383
  return true;
341
384
  }
342
385
  /** Arm only a positively published chooser; the journal owns the immutable hour deadline. */
343
- export function armTelegramRoutingInputs(values, operatorUserId) {
386
+ export function armTelegramRoutingInputs(values, operatorUserId, chooser) {
344
387
  const first = values[0], execution = getTelegramUpdateExecutionFence(first);
345
- if (!execution || values.some(value => getTelegramUpdateExecutionFence(value)?.signal !== execution.signal))
388
+ if (!execution ||
389
+ values.some((value) => getTelegramUpdateExecutionFence(value)?.signal !== execution.signal))
346
390
  return undefined;
347
391
  for (const value of values)
348
392
  assertTelegramUpdateExecutionCurrent(value);
349
393
  const ids = collectTelegramAdmissionSourceUpdateIds(values);
350
394
  if (!ids.length)
351
395
  return undefined;
352
- return getTelegramUpdateAdmissionBinding(first)?.armRoutingInput?.(operatorUserId, ids);
396
+ return getTelegramUpdateAdmissionBinding(first)?.armRoutingInput?.(operatorUserId, ids, chooser);
397
+ }
398
+ /** A routed carrier's saved routing clock; a revived chooser reuses its recorded message. */
399
+ export function getTelegramUpdateRoutingInput(value) {
400
+ return getTelegramUpdateAdmissionBinding(value)?.getRoutingInput?.();
353
401
  }
354
402
  /** Reserve source execution; an actual choice must also freeze its durable TTL before any effect. */
355
403
  export function acquireTelegramUpdateRouting(value, select = false, sourceUpdateIds) {
356
404
  assertTelegramUpdateExecutionCurrent(value);
357
- return getTelegramUpdateAdmissionBinding(value)?.acquireRouting?.(select, sourceUpdateIds) ?? (() => { });
405
+ return (getTelegramUpdateAdmissionBinding(value)?.acquireRouting?.(select, sourceUpdateIds) ?? (() => { }));
406
+ }
407
+ /** Capture existing warm completion authority; only explicit reporting may dispose this exact original. */
408
+ export function prepareTelegramDeferredSourceCompletion(value) {
409
+ const execution = getTelegramUpdateExecutionFence(value), binding = getTelegramUpdateAdmissionBinding(value);
410
+ if (!execution ||
411
+ execution.isCurrent() !== true ||
412
+ !binding?.prepareSourceCompletion ||
413
+ !binding.inspectCompletion ||
414
+ !binding.inspectSource)
415
+ return undefined;
416
+ const prepare = binding.prepareSourceCompletion, inspect = binding.inspectCompletion, readSource = binding.inspectSource, report = binding.report;
417
+ const available = prepare(), source = available && { ...available.source };
418
+ const current = () => !!available &&
419
+ available.isCurrent() &&
420
+ execution.isCurrent() &&
421
+ getTelegramUpdateExecutionFence(value) === execution &&
422
+ getTelegramUpdateAdmissionBinding(value) === binding &&
423
+ binding.prepareSourceCompletion === prepare &&
424
+ binding.inspectCompletion === inspect &&
425
+ binding.inspectSource === readSource &&
426
+ binding.report === report;
427
+ if (!source ||
428
+ !current() ||
429
+ !isDeepStrictEqual(readSource(), source) ||
430
+ !current())
431
+ return undefined;
432
+ let attempted = false, reported = false;
433
+ return {
434
+ get source() {
435
+ return { ...source };
436
+ },
437
+ isCurrent: current,
438
+ reportCompleted() {
439
+ if (attempted)
440
+ return false;
441
+ attempted = true;
442
+ if (!current() || !isDeepStrictEqual(readSource(), source) || !current())
443
+ return false;
444
+ reported = true;
445
+ // Acceptance remains separate from the asynchronously issued worker removal ACK, including after a lost report.
446
+ return reportTelegramUpdateCompleted(value, { ...source });
447
+ },
448
+ inspectCompletion() {
449
+ if (!reported || !current())
450
+ return undefined;
451
+ const completed = inspect();
452
+ return current() && isDeepStrictEqual(completed, source)
453
+ ? { ...source }
454
+ : undefined;
455
+ },
456
+ };
457
+ }
458
+ /** Read-only warm owner availability; no source freeze, admission, report or receipt reconstruction. */
459
+ export function prepareTelegramDeferredQueueAdmission(value) {
460
+ if (getTelegramUpdateExecutionFence(value)?.isCurrent() !== true)
461
+ return undefined;
462
+ return getTelegramUpdateAdmissionBinding(value)?.prepareQueueAdmission?.();
358
463
  }
359
464
  export function reportTelegramQueueAdmission(values, receipts) {
360
465
  const bindings = new Map();
@@ -492,12 +597,9 @@ export function buildTelegramUpdateExecutionPlan(action) {
492
597
  export function buildTelegramUpdateExecutionPlanFromUpdate(update, allowedUserId) {
493
598
  return buildTelegramUpdateExecutionPlan(buildTelegramUpdateFlowAction(update, allowedUserId));
494
599
  }
495
- const TELEGRAM_UNAUTHORIZED_DENIAL_COPY = "Access denied.";
496
- function formatTelegramUnauthorizedDenial(format) {
497
- return format === "html"
498
- ? `<b>🚫 ${TELEGRAM_UNAUTHORIZED_DENIAL_COPY}</b>`
499
- : `🚫 ${TELEGRAM_UNAUTHORIZED_DENIAL_COPY}`;
500
- }
600
+ /** The fleeting toast and the in-chat notice are separate copy, each written in its own form. */
601
+ const TELEGRAM_UNAUTHORIZED_DENIAL_TOAST = "Access denied";
602
+ const TELEGRAM_UNAUTHORIZED_DENIAL_NOTICE = "<b>🚫 Access denied.</b>";
501
603
  function getTelegramCallbackQueryId(query) {
502
604
  return typeof query.id === "string" ? query.id : undefined;
503
605
  }
@@ -529,7 +631,8 @@ function getForeignTelegramMessageOwnership(target, deps) {
529
631
  function getForeignTelegramCallbackOwnership(query, deps) {
530
632
  const messageTarget = getTelegramCallbackMessageTarget(query);
531
633
  const currentInstanceId = deps.getCurrentInstanceId?.();
532
- const knownOwner = messageTarget && deps.getMessageOwnership?.(messageTarget.chatId, messageTarget.messageId);
634
+ const knownOwner = messageTarget &&
635
+ deps.getMessageOwnership?.(messageTarget.chatId, messageTarget.messageId);
533
636
  // A locally published chooser stays local when Restore moves its Thread to a follower.
534
637
  if (currentInstanceId && knownOwner?.instanceId === currentInstanceId)
535
638
  return undefined;
@@ -644,8 +747,12 @@ export function createTelegramUpdateRuntime(deps) {
644
747
  export async function handleAuthorizedTelegramReactionUpdate(reactionUpdate, deps) {
645
748
  const reactionUser = reactionUpdate.user;
646
749
  const allowedUserId = deps.allowedUserId;
647
- if (allowedUserId === undefined || !Number.isSafeInteger(allowedUserId) || allowedUserId <= 0 ||
648
- !reactionUser || reactionUser.is_bot || reactionUser.id !== allowedUserId ||
750
+ if (allowedUserId === undefined ||
751
+ !Number.isSafeInteger(allowedUserId) ||
752
+ allowedUserId <= 0 ||
753
+ !reactionUser ||
754
+ reactionUser.is_bot ||
755
+ reactionUser.id !== allowedUserId ||
649
756
  reactionUpdate.actor_chat !== undefined)
650
757
  return;
651
758
  const foreignOwnership = getForeignTelegramMessageOwnership(getTelegramReactionMessageTarget(reactionUpdate), deps);
@@ -675,11 +782,6 @@ export async function handleAuthorizedTelegramReactionUpdate(reactionUpdate, dep
675
782
  deps.assertExecutionCurrent?.();
676
783
  deps.applyQueuedTelegramTurnReactionByMessageId(reactionUpdate.message_id, reactionTransition, deps.ctx, reactionScope);
677
784
  }
678
- function isTelegramStaleContextError(error) {
679
- return (error instanceof Error &&
680
- (error.message.includes("stale after session") ||
681
- error.message.includes("stale ctx")));
682
- }
683
785
  export async function executeTelegramUpdatePlan(plan, deps) {
684
786
  try {
685
787
  const assertExecutionCurrent = () => deps.execution?.assertCurrent();
@@ -712,7 +814,7 @@ export async function executeTelegramUpdatePlan(plan, deps) {
712
814
  const callbackQueryId = getTelegramCallbackQueryId(plan.query);
713
815
  if (callbackQueryId) {
714
816
  assertExecutionCurrent();
715
- await deps.answerCallbackQuery(callbackQueryId, formatTelegramUnauthorizedDenial("plain"));
817
+ await deps.answerCallbackQuery(callbackQueryId, TELEGRAM_UNAUTHORIZED_DENIAL_TOAST);
716
818
  }
717
819
  return;
718
820
  }
@@ -729,7 +831,7 @@ export async function executeTelegramUpdatePlan(plan, deps) {
729
831
  try {
730
832
  if (callbackQueryId) {
731
833
  assertExecutionCurrent();
732
- await deps.answerCallbackQuery(callbackQueryId, "This Telegram message belongs to another Pi instance.");
834
+ await deps.answerCallbackQuery(callbackQueryId, "This Telegram message belongs to another Pi instance");
733
835
  }
734
836
  }
735
837
  finally {
@@ -747,7 +849,7 @@ export async function executeTelegramUpdatePlan(plan, deps) {
747
849
  if (plan.kind === "guest") {
748
850
  if (plan.shouldDeny) {
749
851
  assertExecutionCurrent();
750
- await deps.answerGuestQuery(plan.guestMessage.guest_query_id, formatTelegramUnauthorizedDenial("html"), { parseMode: "HTML" });
852
+ await deps.answerGuestQuery(plan.guestMessage.guest_query_id, TELEGRAM_UNAUTHORIZED_DENIAL_NOTICE, { parseMode: "HTML" });
751
853
  return;
752
854
  }
753
855
  if (deps.handleAuthorizedTelegramGuestMessage) {
@@ -766,7 +868,7 @@ export async function executeTelegramUpdatePlan(plan, deps) {
766
868
  if (plan.shouldDeny || (plan.shouldPair && !pairedNow)) {
767
869
  if (replyTarget) {
768
870
  assertExecutionCurrent();
769
- await deps.sendTextReply(replyTarget.chatId, replyTarget.messageId, formatTelegramUnauthorizedDenial("html"), { parseMode: "HTML", target: replyTarget });
871
+ await deps.sendTextReply(replyTarget.chatId, replyTarget.messageId, TELEGRAM_UNAUTHORIZED_DENIAL_NOTICE, { parseMode: "HTML", target: replyTarget });
770
872
  }
771
873
  return;
772
874
  }
@@ -851,7 +953,7 @@ export async function executeTelegramUpdatePlan(plan, deps) {
851
953
  assertExecutionCurrent();
852
954
  }
853
955
  catch (error) {
854
- if (!isTelegramStaleContextError(error))
956
+ if (!isPiStaleContextError(error))
855
957
  throw error;
856
958
  }
857
959
  }
@@ -860,22 +962,35 @@ const TELEGRAM_UPDATE_RETRY_BASE_DELAY_MS = 1_000;
860
962
  const TELEGRAM_UPDATE_RETRY_MAX_DELAY_MS = 60_000;
861
963
  const TELEGRAM_UPDATE_WORKER_BATCH_SIZE = 64;
862
964
  function normalizeTelegramQueueSourceCompletions(value, sourceUpdateIds, journalBindingKey, full = true) {
863
- if (!Array.isArray(value) || (full ? value.length !== sourceUpdateIds.size : value.length > sourceUpdateIds.size) || value.length === 0)
965
+ if (!Array.isArray(value) ||
966
+ (full
967
+ ? value.length !== sourceUpdateIds.size
968
+ : value.length > sourceUpdateIds.size) ||
969
+ value.length === 0)
864
970
  throw new Error("Telegram scoped queue completion requires full source coverage.");
865
971
  const seen = new Set(), scopes = new Set();
866
- return value.map(source => {
867
- if (!Number.isSafeInteger(source?.updateId) || !sourceUpdateIds.has(source.updateId) || seen.has(source.updateId)) {
972
+ return value
973
+ .map((source) => {
974
+ if (!Number.isSafeInteger(source?.updateId) ||
975
+ !sourceUpdateIds.has(source.updateId) ||
976
+ seen.has(source.updateId)) {
868
977
  throw new Error("Telegram scoped queue completion has invalid source membership.");
869
978
  }
870
979
  const normalized = validateTelegramUpdateAdmissionOutcome({ kind: "complete", expectedSource: source }, source.updateId, sourceUpdateIds);
871
- if (normalized.kind !== "complete" || !normalized.expectedSource?.completionSha256 ||
872
- normalized.expectedSource.journalBindingKey !== journalBindingKey || scopes.has(normalized.expectedSource.completionSha256)) {
980
+ if (normalized.kind !== "complete" ||
981
+ !normalized.expectedSource?.completionSha256 ||
982
+ normalized.expectedSource.journalBindingKey !== journalBindingKey ||
983
+ scopes.has(normalized.expectedSource.completionSha256)) {
873
984
  throw new Error("Telegram scoped queue completion has invalid source scope.");
874
985
  }
875
986
  seen.add(source.updateId);
876
987
  scopes.add(normalized.expectedSource.completionSha256);
877
- return { ...normalized.expectedSource, completionSha256: normalized.expectedSource.completionSha256 };
878
- }).sort((a, b) => a.updateId - b.updateId);
988
+ return {
989
+ ...normalized.expectedSource,
990
+ completionSha256: normalized.expectedSource.completionSha256,
991
+ };
992
+ })
993
+ .sort((a, b) => a.updateId - b.updateId);
879
994
  }
880
995
  class TelegramUpdateAdmissionOutcomeError extends Error {
881
996
  constructor(message) {
@@ -884,6 +999,30 @@ class TelegramUpdateAdmissionOutcomeError extends Error {
884
999
  }
885
1000
  }
886
1001
  const TELEGRAM_UPDATE_WORKER_EXECUTION_ABORTED = Symbol("telegram.update-worker.execution-aborted");
1002
+ /** The receipt's only journal entry when it is exactly this prompt update, queued unchanged under this owner. */
1003
+ function findExactQueuedPromptEntry(snapshot, receiptId, updateId, queueOwner, update) {
1004
+ if (!isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version))
1005
+ return undefined;
1006
+ const entries = snapshot.entries.filter((entry) => entry.queueReceiptId === receiptId);
1007
+ const entry = entries[0];
1008
+ return entries.length === 1 &&
1009
+ entry?.updateId === updateId &&
1010
+ entry.state === "queued" &&
1011
+ entry.queueKind === "prompt" &&
1012
+ !entry.queueHandoff &&
1013
+ isDeepStrictEqual(entry.queueOwner, queueOwner) &&
1014
+ isDeepStrictEqual(entry.update, update)
1015
+ ? entry
1016
+ : undefined;
1017
+ }
1018
+ /** A saved chooser clock that proves an untouched, unexpired choice whose chooser message can be revived. */
1019
+ export function isRevivableTelegramRoutingInput(entry, nowMs) {
1020
+ const lifetime = entry.routingInput;
1021
+ return (entry.state === "pending" &&
1022
+ lifetime?.phase === "waiting" &&
1023
+ lifetime.chooser !== undefined &&
1024
+ nowMs < lifetime.expiresAtMs);
1025
+ }
887
1026
  function getTelegramUpdateWorkerStateSnapshot(state) {
888
1027
  return { ...state };
889
1028
  }
@@ -895,20 +1034,41 @@ function validateTelegramUpdateAdmissionOutcome(value, currentUpdateId, claimabl
895
1034
  if (value.expectedSource === undefined)
896
1035
  return { kind: "complete" };
897
1036
  const source = value.expectedSource;
898
- if (!isTelegramUpdateAdmissionRecord(source) || Object.keys(source).some(key => !["journalBindingKey", "updateId", "sourceSha256", "completionSha256"].includes(key)) ||
899
- source.updateId !== currentUpdateId || !isTelegramUpdateAdmissionString(source.journalBindingKey) ||
900
- typeof source.sourceSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(source.sourceSha256) ||
901
- source.completionSha256 !== undefined && (typeof source.completionSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(source.completionSha256))) {
1037
+ if (!isTelegramUpdateAdmissionRecord(source) ||
1038
+ Object.keys(source).some((key) => ![
1039
+ "journalBindingKey",
1040
+ "updateId",
1041
+ "sourceSha256",
1042
+ "completionSha256",
1043
+ ].includes(key)) ||
1044
+ source.updateId !== currentUpdateId ||
1045
+ !isTelegramUpdateAdmissionString(source.journalBindingKey) ||
1046
+ typeof source.sourceSha256 !== "string" ||
1047
+ !/^[a-f0-9]{64}$/u.test(source.sourceSha256) ||
1048
+ (source.completionSha256 !== undefined &&
1049
+ (typeof source.completionSha256 !== "string" ||
1050
+ !/^[a-f0-9]{64}$/u.test(source.completionSha256)))) {
902
1051
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${currentUpdateId} returned an invalid completion source.`);
903
1052
  }
904
- return { kind: "complete", expectedSource: { journalBindingKey: source.journalBindingKey,
905
- updateId: currentUpdateId, sourceSha256: source.sourceSha256,
906
- ...(source.completionSha256 !== undefined ? { completionSha256: source.completionSha256 } : {}) } };
1053
+ return {
1054
+ kind: "complete",
1055
+ expectedSource: {
1056
+ journalBindingKey: source.journalBindingKey,
1057
+ updateId: currentUpdateId,
1058
+ sourceSha256: source.sourceSha256,
1059
+ ...(source.completionSha256 !== undefined
1060
+ ? { completionSha256: source.completionSha256 }
1061
+ : {}),
1062
+ },
1063
+ };
907
1064
  }
908
1065
  if (value.kind === "deferred") {
909
1066
  if (value.routingReview !== undefined && value.routingReview !== true)
910
1067
  throw new TelegramUpdateAdmissionOutcomeError("Invalid historical review outcome.");
911
- return { kind: "deferred", ...(value.routingReview === true ? { routingReview: true } : {}) };
1068
+ return {
1069
+ kind: "deferred",
1070
+ ...(value.routingReview === true ? { routingReview: true } : {}),
1071
+ };
912
1072
  }
913
1073
  if (value.kind === "queued") {
914
1074
  if ((value.queueKind !== "prompt" && value.queueKind !== "control") ||
@@ -1002,9 +1162,30 @@ function normalizeTelegramUpdateQueueOwnerIdentity(value) {
1002
1162
  }
1003
1163
  return { ...value };
1004
1164
  }
1165
+ /** Clear claim, retry and failure projections after the worker forgets its local claims. */
1166
+ function clearTelegramUpdateWorkerClaimState(state, unsettledExecutionCount) {
1167
+ state.deferredClaimCount = 0;
1168
+ delete state.abandoningClaimCount;
1169
+ delete state.historicalClaimCount;
1170
+ state.queuedClaimCount = 0;
1171
+ state.foreignQueuedCount = 0;
1172
+ delete state.foreignQueuedOwner;
1173
+ state.retryWaitCount = 0;
1174
+ state.failedCount = 0;
1175
+ state.nextRetryUpdateId = undefined;
1176
+ state.nextRetryAtMs = undefined;
1177
+ state.nextRetryAttemptCount = undefined;
1178
+ state.nextRetryFailureClass = undefined;
1179
+ state.failedUpdateId = undefined;
1180
+ state.failedFailureId = undefined;
1181
+ state.failedAttemptCount = undefined;
1182
+ state.failedClass = undefined;
1183
+ state.failedSummary = undefined;
1184
+ state.terminalFailureAtMs = undefined;
1185
+ state.unsettledExecutionCount = unsettledExecutionCount;
1186
+ }
1005
1187
  export function createTelegramUpdateWorkerRuntime(deps) {
1006
- if ((deps.scheduleRetry === undefined) !==
1007
- (deps.cancelRetry === undefined)) {
1188
+ if ((deps.scheduleRetry === undefined) !== (deps.cancelRetry === undefined)) {
1008
1189
  throw new Error("Telegram update retry scheduling requires matching schedule and cancel ports.");
1009
1190
  }
1010
1191
  const getNowMs = deps.getNowMs ?? Date.now;
@@ -1021,9 +1202,11 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1021
1202
  }
1022
1203
  const yieldToEventLoop = deps.yieldToEventLoop ??
1023
1204
  (() => new Promise((resolve) => setTimeout(resolve, 0)));
1024
- const completeQueuedExact = deps.journal.completeQueuedExact?.bind(deps.journal);
1205
+ const queueCompletionPort = deps.journal.completeQueuedExact;
1206
+ const completionReaderPort = deps.journal.inspectSourceCompletion;
1207
+ const completeQueuedExact = queueCompletionPort?.bind(deps.journal);
1025
1208
  const completeExact = deps.journal.removeCompletedExact?.bind(deps.journal);
1026
- const inspectCompletion = deps.journal.inspectSourceCompletion?.bind(deps.journal);
1209
+ const inspectCompletion = completionReaderPort?.bind(deps.journal);
1027
1210
  const observeQueuedCompletion = deps.onQueueReceiptCompleted;
1028
1211
  const fallbackQueueOwnerInstanceId = `worker-${randomUUID()}`;
1029
1212
  const fallbackQueueOwnerProcessId = process.pid > 0 ? process.pid : 1;
@@ -1042,8 +1225,11 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1042
1225
  if (!deps.getQueueOwnerIdentity)
1043
1226
  return true;
1044
1227
  const live = deps.getQueueOwnerIdentity(expected.ctx), captured = expected.queueOwnerIdentity;
1045
- return !!live && live.instanceId === captured.instanceId && live.processId === captured.processId &&
1046
- live.processBirthId === captured.processBirthId && live.sessionGeneration === captured.sessionGeneration;
1228
+ return (!!live &&
1229
+ live.instanceId === captured.instanceId &&
1230
+ live.processId === captured.processId &&
1231
+ live.processBirthId === captured.processBirthId &&
1232
+ live.sessionGeneration === captured.sessionGeneration);
1047
1233
  };
1048
1234
  const state = {
1049
1235
  phase: "stopped",
@@ -1065,6 +1251,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1065
1251
  const scopedQueueCompletionAttempts = new Map();
1066
1252
  const unsettledExecutions = new Set();
1067
1253
  let owner;
1254
+ let livePreparation;
1068
1255
  let drainPromise;
1069
1256
  let pendingSignal = false;
1070
1257
  let blocked = false;
@@ -1091,6 +1278,14 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1091
1278
  recordRuntimeEvent(error, { phase: "state-observer" });
1092
1279
  }
1093
1280
  };
1281
+ /** Fresh live input this generation admitted itself: not startup history, no live release issued, not executing. */
1282
+ const isFreshLiveSource = (sourceOwner, updateId) => {
1283
+ const source = deferredSources.get(updateId);
1284
+ return (!sourceOwner.startupUpdateIds?.has(updateId) &&
1285
+ !source?.historical &&
1286
+ !source?.liveReleaseIssued &&
1287
+ !unsettledExecutionsByUpdateId.has(updateId));
1288
+ };
1094
1289
  const updateClaimCounts = () => {
1095
1290
  let deferredClaimCount = 0;
1096
1291
  let queuedClaimCount = 0;
@@ -1116,6 +1311,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1116
1311
  state.historicalClaimCount = historicalClaimCount;
1117
1312
  else
1118
1313
  delete state.historicalClaimCount;
1314
+ if (livePreparation && !livePreparation.released)
1315
+ state.preparedInputCount = livePreparation.ids.size;
1316
+ else
1317
+ delete state.preparedInputCount;
1119
1318
  };
1120
1319
  const releaseDeferredClaims = (updateIds) => {
1121
1320
  let changed = false;
@@ -1167,7 +1366,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1167
1366
  ? { journalBindingKey: deps.getJournalBindingKey() }
1168
1367
  : {}),
1169
1368
  });
1170
- const inspectQueueReceipt = deps.journal.isQueueReceiptCurrent?.bind(deps.journal);
1369
+ const queueAdmissionJournal = deps.journal;
1370
+ const queueAdmissionRead = queueAdmissionJournal.read, queueAdmissionCommit = queueAdmissionJournal.markQueued;
1371
+ const queueAdmissionInspect = deps.journal.isQueueReceiptCurrent;
1372
+ const inspectQueueReceipt = queueAdmissionInspect?.bind(deps.journal);
1171
1373
  const inspectQueuedSources = deps.journal.inspectQueuedReceipt?.bind(deps.journal);
1172
1374
  const prepareQueueReceipt = deps.beforeQueueReceiptPublished;
1173
1375
  const pendingQueuePublications = new Map();
@@ -1186,7 +1388,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1186
1388
  if (prepareQueueReceipt) {
1187
1389
  const pending = pendingQueuePublications.get(normalized.receiptId);
1188
1390
  if (pending) {
1189
- if (pending.owner !== expectedOwner || !areTelegramQueueAdmissionReceiptsEqual(pending.receipt, normalized) ||
1391
+ if (pending.owner !== expectedOwner ||
1392
+ !areTelegramQueueAdmissionReceiptsEqual(pending.receipt, normalized) ||
1190
1393
  !areTelegramUpdateJournalQueueOwnersEqual(pending.queueOwner, queueOwner)) {
1191
1394
  throw new TelegramUpdateAdmissionOutcomeError("Telegram queue publication has conflicting in-flight authority.");
1192
1395
  }
@@ -1194,13 +1397,23 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1194
1397
  return false;
1195
1398
  }
1196
1399
  const journalBindingKey = deps.getJournalBindingKey?.();
1197
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted && deps.hasAuthority(ctx) && deps.isContextCurrent?.(ctx) !== false &&
1198
- isQueueOwnerIdentityCurrent(expectedOwner) && deps.getJournalBindingKey?.() === journalBindingKey;
1199
- if (!journalBindingKey || !current() || !inspectQueueReceipt ||
1200
- inspectQueueReceipt(normalizeQueueReceipt(normalized), { ...queueOwner }) !== true || !current()) {
1400
+ const current = () => owner === expectedOwner &&
1401
+ !expectedOwner.controller.signal.aborted &&
1402
+ deps.hasAuthority(ctx) &&
1403
+ deps.isContextCurrent?.(ctx) !== false &&
1404
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
1405
+ deps.getJournalBindingKey?.() === journalBindingKey;
1406
+ if (!journalBindingKey ||
1407
+ !current() ||
1408
+ !inspectQueueReceipt ||
1409
+ inspectQueueReceipt(normalizeQueueReceipt(normalized), {
1410
+ ...queueOwner,
1411
+ }) !== true ||
1412
+ !current()) {
1201
1413
  throw new TelegramUpdateAdmissionOutcomeError("Telegram queue publication requires exact current receipt inspection.");
1202
1414
  }
1203
- const task = Promise.resolve().then(async () => {
1415
+ const task = Promise.resolve()
1416
+ .then(async () => {
1204
1417
  if (!current())
1205
1418
  throw new TelegramUpdateAdmissionOutcomeError("Telegram queue publication authority ended before acceptance.");
1206
1419
  const prepared = await prepareQueueReceipt(normalizeQueueReceipt(normalized), { ...queueOwner }, ctx, current);
@@ -1211,29 +1424,52 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1211
1424
  if (sourceCompletions.length !== normalized.sourceUpdateIds.length) {
1212
1425
  if (!current() || !inspectQueuedSources)
1213
1426
  throw new Error("Telegram partial queue scopes require strict current whole-receipt origin inspection.");
1214
- const expected = { queueKind: normalized.queueKind, receiptId: normalized.receiptId,
1215
- sourceUpdateIds: [...normalized.sourceUpdateIds], queueOwner: { ...queueOwner } };
1427
+ const expected = {
1428
+ queueKind: normalized.queueKind,
1429
+ receiptId: normalized.receiptId,
1430
+ sourceUpdateIds: [...normalized.sourceUpdateIds],
1431
+ queueOwner: { ...queueOwner },
1432
+ };
1216
1433
  const proof = inspectQueuedSources(structuredClone(expected));
1217
- if (!current() || !proof || !isDeepStrictEqual(proof.receipt, expected) ||
1218
- !Array.isArray(proof.sources) || proof.sources.length !== expected.sourceUpdateIds.length ||
1219
- proof.sources.some((source, index) => source.updateId !== expected.sourceUpdateIds[index] || typeof source.sourceSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(source.sourceSha256)) ||
1220
- sourceCompletions.some(scope => proof.sources.find(source => source.updateId === scope.updateId)?.sourceSha256 !== scope.sourceSha256)) {
1434
+ if (!current() ||
1435
+ !proof ||
1436
+ !isDeepStrictEqual(proof.receipt, expected) ||
1437
+ !Array.isArray(proof.sources) ||
1438
+ proof.sources.length !== expected.sourceUpdateIds.length ||
1439
+ proof.sources.some((source, index) => source.updateId !== expected.sourceUpdateIds[index] ||
1440
+ typeof source.sourceSha256 !== "string" ||
1441
+ !/^[a-f0-9]{64}$/u.test(source.sourceSha256)) ||
1442
+ sourceCompletions.some((scope) => proof.sources.find((source) => source.updateId === scope.updateId)?.sourceSha256 !== scope.sourceSha256)) {
1221
1443
  throw new Error("Telegram partial queue scope origin was not confirmed under whole-receipt authority.");
1222
1444
  }
1223
1445
  }
1224
1446
  for (const { journalBindingKey: _binding, ...completion } of sourceCompletions) {
1225
- if (!current() || inspectCompletion({ ...completion }) !== undefined || !current()) {
1447
+ if (!current() ||
1448
+ inspectCompletion({ ...completion }) !== undefined ||
1449
+ !current()) {
1226
1450
  throw new Error("Telegram queued source completion authority is contradictory.");
1227
1451
  }
1228
1452
  }
1229
1453
  }
1230
- if (!current() || inspectQueueReceipt(normalizeQueueReceipt(normalized), { ...queueOwner }) !== true || !current()) {
1454
+ if (!current() ||
1455
+ inspectQueueReceipt(normalizeQueueReceipt(normalized), {
1456
+ ...queueOwner,
1457
+ }) !== true ||
1458
+ !current()) {
1231
1459
  throw new TelegramUpdateAdmissionOutcomeError("Telegram queue publication authority changed after acceptance.");
1232
1460
  }
1233
1461
  publishReady();
1234
- }).finally(() => { if (pendingQueuePublications.get(normalized.receiptId)?.task === task)
1235
- pendingQueuePublications.delete(normalized.receiptId); });
1236
- pendingQueuePublications.set(normalized.receiptId, { receipt: normalizeQueueReceipt(normalized), queueOwner: { ...queueOwner }, owner: expectedOwner, task });
1462
+ })
1463
+ .finally(() => {
1464
+ if (pendingQueuePublications.get(normalized.receiptId)?.task === task)
1465
+ pendingQueuePublications.delete(normalized.receiptId);
1466
+ });
1467
+ pendingQueuePublications.set(normalized.receiptId, {
1468
+ receipt: normalizeQueueReceipt(normalized),
1469
+ queueOwner: { ...queueOwner },
1470
+ owner: expectedOwner,
1471
+ task,
1472
+ });
1237
1473
  await task;
1238
1474
  return true;
1239
1475
  }
@@ -1243,7 +1479,13 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1243
1479
  committedQueueReceipts.set(receipt.receiptId, {
1244
1480
  receipt: normalized,
1245
1481
  queueOwner: { ...queueOwner },
1246
- ...(sourceCompletions ? { sourceCompletions: sourceCompletions.map(value => ({ ...value })) } : {}),
1482
+ ...(sourceCompletions
1483
+ ? {
1484
+ sourceCompletions: sourceCompletions.map((value) => ({
1485
+ ...value,
1486
+ })),
1487
+ }
1488
+ : {}),
1247
1489
  });
1248
1490
  if (sourceCompletions)
1249
1491
  requiredScopedQueueReceipts.add(receipt.receiptId);
@@ -1262,11 +1504,14 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1262
1504
  if (scopedQueueCompletionAttempts.has(receipt.receiptId))
1263
1505
  return undefined;
1264
1506
  const committed = committedQueueReceipts.get(receipt.receiptId);
1265
- if (!committed || !areTelegramQueueAdmissionReceiptsEqual(committed.receipt, normalizeQueueReceipt(receipt)))
1507
+ if (!committed ||
1508
+ !areTelegramQueueAdmissionReceiptsEqual(committed.receipt, normalizeQueueReceipt(receipt)))
1266
1509
  return undefined;
1267
1510
  try {
1268
1511
  if (inspectQueueReceipt &&
1269
- inspectQueueReceipt(normalizeQueueReceipt(committed.receipt), { ...committed.queueOwner }) !== true) {
1512
+ inspectQueueReceipt(normalizeQueueReceipt(committed.receipt), {
1513
+ ...committed.queueOwner,
1514
+ }) !== true) {
1270
1515
  return undefined;
1271
1516
  }
1272
1517
  }
@@ -1316,7 +1561,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1316
1561
  snapshot.version !== TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION) ||
1317
1562
  (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION &&
1318
1563
  !deps.executeCustodiedUpdate) ||
1319
- snapshot.entries.some((entry) => ((snapshot.version === TELEGRAM_UPDATE_JOURNAL_EXCLUSION_VERSION || entry.preApprovalExcluded !== undefined) &&
1564
+ snapshot.entries.some((entry) => ((snapshot.version === TELEGRAM_UPDATE_JOURNAL_EXCLUSION_VERSION ||
1565
+ entry.preApprovalExcluded !== undefined) &&
1320
1566
  typeof entry.preApprovalExcluded !== "boolean") ||
1321
1567
  (entry.preApprovalExcluded === true && entry.state === "queued"))) {
1322
1568
  throw new TelegramUpdateAdmissionOutcomeError("Telegram journal snapshot has invalid pairing exclusion evidence.");
@@ -1338,7 +1584,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1338
1584
  oldestAdmittedAtMs === undefined
1339
1585
  ? entry.admittedAtMs
1340
1586
  : Math.min(oldestAdmittedAtMs, entry.admittedAtMs);
1341
- if (entry.state === "retry-wait" && entry.nextRetryAtMs !== undefined && !claims.has(entry.updateId)) {
1587
+ if (entry.state === "retry-wait" &&
1588
+ entry.nextRetryAtMs !== undefined &&
1589
+ !claims.has(entry.updateId)) {
1342
1590
  scheduledRetryAtMs =
1343
1591
  scheduledRetryAtMs === undefined
1344
1592
  ? entry.nextRetryAtMs
@@ -1406,10 +1654,15 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1406
1654
  }
1407
1655
  for (const updateId of deferredSources.keys()) {
1408
1656
  const claim = claims.get(updateId);
1409
- if (claim !== "deferred" && claim !== "abandoning" && claim !== "historical" && claim !== "retained")
1657
+ if (claim !== "deferred" &&
1658
+ claim !== "abandoning" &&
1659
+ claim !== "historical" &&
1660
+ claim !== "retained")
1410
1661
  deferredSources.delete(updateId);
1411
1662
  }
1412
1663
  for (const [receiptId, receipt] of queuedReceiptEntries) {
1664
+ if (receipt.sourceUpdateIds.some((id) => livePreparation?.ids.has(id)))
1665
+ continue;
1413
1666
  await publishCommittedQueueReceipt({
1414
1667
  queueKind: receipt.queueKind,
1415
1668
  receiptId,
@@ -1461,17 +1714,23 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1461
1714
  transition("blocked", currentUpdateId, "authority-lost");
1462
1715
  return "blocked";
1463
1716
  };
1464
- const executeWithinOwner = async (expectedOwner, update) => {
1717
+ const executeWithinOwner = async (expectedOwner, update, preparedExecute) => {
1465
1718
  if (expectedOwner.controller.signal.aborted) {
1466
1719
  return TELEGRAM_UPDATE_WORKER_EXECUTION_ABORTED;
1467
1720
  }
1468
1721
  const custodied = deps.executeCustodiedUpdate !== undefined;
1469
- const execution = Promise.resolve().then(async () => deps.executeCustodiedUpdate
1470
- ? deps.executeCustodiedUpdate(update, expectedOwner.ctx, expectedOwner.controller.signal)
1471
- : deps.executeUpdate(update, expectedOwner.ctx, expectedOwner.controller.signal));
1722
+ const execution = Promise.resolve().then(async () => preparedExecute
1723
+ ? preparedExecute(update, expectedOwner.ctx, expectedOwner.controller.signal)
1724
+ : deps.executeCustodiedUpdate
1725
+ ? deps.executeCustodiedUpdate(update, expectedOwner.ctx, expectedOwner.controller.signal)
1726
+ : deps.executeUpdate(update, expectedOwner.ctx, expectedOwner.controller.signal));
1472
1727
  const settlement = execution.then((outcome) => custodied
1473
- ? ({ ok: true, outcome: outcome, custodied: true })
1474
- : ({ ok: true, outcome: outcome }), (error) => ({ ok: false, error }));
1728
+ ? {
1729
+ ok: true,
1730
+ outcome: outcome,
1731
+ custodied: true,
1732
+ }
1733
+ : { ok: true, outcome: outcome }, (error) => ({ ok: false, error }));
1475
1734
  unsettledExecutions.add(settlement);
1476
1735
  const updateExecutions = unsettledExecutionsByUpdateId.get(update.update_id) ?? new Set();
1477
1736
  updateExecutions.add(settlement);
@@ -1555,7 +1814,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1555
1814
  for (const sourceUpdateId of normalized.sourceUpdateIds) {
1556
1815
  claims.set(sourceUpdateId, "queued");
1557
1816
  }
1558
- return publishCommittedQueueReceipt(normalized, queueOwner, expectedOwner).then(() => "committed", error => blockWithFailure("invalid-outcome", "queue-receipt-publish", error, currentUpdateId));
1817
+ return publishCommittedQueueReceipt(normalized, queueOwner, expectedOwner).then(() => "committed", (error) => blockWithFailure("invalid-outcome", "queue-receipt-publish", error, currentUpdateId));
1559
1818
  };
1560
1819
  const persistExecutionFailure = (expectedOwner, entry, error) => {
1561
1820
  const authorityResult = checkAuthority(expectedOwner, entry.updateId);
@@ -1614,8 +1873,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1614
1873
  transition(disposition, entry.updateId);
1615
1874
  return disposition;
1616
1875
  };
1617
- const commitCompletedBatch = (expectedOwner, completions) => {
1618
- const updateIds = completions.map(value => value.updateId);
1876
+ const commitCompletedBatch = (expectedOwner, completions, isCurrent) => {
1877
+ const updateIds = completions.map((value) => value.updateId);
1619
1878
  if (updateIds.length === 0)
1620
1879
  return undefined;
1621
1880
  const commitAuthority = checkAuthority(expectedOwner, updateIds[updateIds.length - 1]);
@@ -1623,28 +1882,43 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1623
1882
  return commitAuthority;
1624
1883
  const journalBindingKey = deps.getJournalBindingKey?.();
1625
1884
  try {
1626
- const expectedSources = completions.flatMap(value => value.expectedSource ? [value.expectedSource] : []);
1627
- if (expectedSources.some(value => value.journalBindingKey !== journalBindingKey) ||
1628
- (expectedSources.length > 0 && (deps.isContextCurrent?.(expectedOwner.ctx) === false || !isQueueOwnerIdentityCurrent(expectedOwner)))) {
1885
+ if (isCurrent?.() === false)
1886
+ throw new Error("Telegram exact completion authority changed.");
1887
+ const expectedSources = completions.flatMap((value) => value.expectedSource ? [value.expectedSource] : []);
1888
+ if (expectedSources.some((value) => value.journalBindingKey !== journalBindingKey) ||
1889
+ (expectedSources.length > 0 &&
1890
+ (deps.isContextCurrent?.(expectedOwner.ctx) === false ||
1891
+ !isQueueOwnerIdentityCurrent(expectedOwner)))) {
1629
1892
  throw new Error("Telegram guarded completion source authority changed.");
1630
1893
  }
1631
- const scoped = expectedSources.flatMap(({ updateId, sourceSha256, completionSha256 }) => completionSha256 !== undefined
1632
- ? [{ updateId, sourceSha256, completionSha256 }] : []).sort((a, b) => a.updateId - b.updateId);
1894
+ const scoped = expectedSources
1895
+ .flatMap(({ updateId, sourceSha256, completionSha256 }) => completionSha256 !== undefined
1896
+ ? [{ updateId, sourceSha256, completionSha256 }]
1897
+ : [])
1898
+ .sort((a, b) => a.updateId - b.updateId);
1633
1899
  if (scoped.length > 0 && !inspectCompletion)
1634
1900
  throw new Error("Telegram source completion inspection is unavailable.");
1635
1901
  let removed;
1636
1902
  if (expectedSources.length > 0) {
1637
1903
  if (!completeExact)
1638
1904
  throw new Error("Telegram exact source completion is unavailable.");
1639
- removed = completeExact(updateIds, expectedSources.map(({ updateId, sourceSha256 }) => ({ updateId, sourceSha256 })), scoped.length > 0 ? scoped.map(completion => ({ ...completion })) : undefined);
1905
+ removed = completeExact(updateIds, expectedSources.map(({ updateId, sourceSha256 }) => ({
1906
+ updateId,
1907
+ sourceSha256,
1908
+ })), scoped.length > 0
1909
+ ? scoped.map((completion) => ({ ...completion }))
1910
+ : undefined, isCurrent);
1640
1911
  }
1641
1912
  else
1642
1913
  removed = deps.journal.removeCompleted(updateIds);
1914
+ if (isCurrent?.() === false)
1915
+ throw new Error("Telegram exact completion ACK authority changed.");
1643
1916
  if (scoped.length > 0) {
1644
1917
  const afterCommit = checkAuthority(expectedOwner, updateIds.at(-1));
1645
1918
  if (afterCommit)
1646
1919
  return afterCommit;
1647
- if (deps.isContextCurrent?.(expectedOwner.ctx) === false || !isQueueOwnerIdentityCurrent(expectedOwner) ||
1920
+ if (deps.isContextCurrent?.(expectedOwner.ctx) === false ||
1921
+ !isQueueOwnerIdentityCurrent(expectedOwner) ||
1648
1922
  deps.getJournalBindingKey?.() !== journalBindingKey)
1649
1923
  throw new Error("Telegram source completion ACK authority changed.");
1650
1924
  if (!isDeepStrictEqual(removed.sourceCompletions, scoped))
@@ -1655,7 +1929,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1655
1929
  const afterRead = checkAuthority(expectedOwner, completion.updateId);
1656
1930
  if (afterRead)
1657
1931
  return afterRead;
1658
- if (deps.isContextCurrent?.(expectedOwner.ctx) === false || !isQueueOwnerIdentityCurrent(expectedOwner) ||
1932
+ if (deps.isContextCurrent?.(expectedOwner.ctx) === false ||
1933
+ !isQueueOwnerIdentityCurrent(expectedOwner) ||
1659
1934
  deps.getJournalBindingKey?.() !== journalBindingKey)
1660
1935
  throw new Error("Telegram source completion ACK authority changed.");
1661
1936
  }
@@ -1697,68 +1972,116 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1697
1972
  };
1698
1973
  const reconcileRoutingInputs = async (snapshot, expectedOwner) => {
1699
1974
  clearRoutingTimer();
1700
- if (!isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) || !expireRouting || !expireRoutingObserver)
1975
+ if (!isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) ||
1976
+ !expireRouting ||
1977
+ !expireRoutingObserver)
1701
1978
  return false;
1702
1979
  let nextAtMs, changed = false;
1703
- const binding = deps.getJournalBindingKey?.(), snapshotIds = new Set(snapshot.entries.map(entry => entry.updateId));
1704
- // Lost removal ACKs reconcile only the exact held original through the retained copy/tombstone.
1705
- const candidates = [...snapshot.entries, ...[...deferredSources.values()]
1706
- .filter(source => source.journalBindingKey === binding && !snapshotIds.has(source.entry.updateId))
1707
- .map(source => source.entry)];
1980
+ const binding = deps.getJournalBindingKey?.(), snapshotIds = new Set(snapshot.entries.map((entry) => entry.updateId));
1981
+ // Lost expiry ACKs reconcile the exact held original against its body-free discard tombstone.
1982
+ const candidates = [
1983
+ ...snapshot.entries,
1984
+ ...[...deferredSources.values()]
1985
+ .filter((source) => source.journalBindingKey === binding &&
1986
+ !snapshotIds.has(source.entry.updateId))
1987
+ .map((source) => source.entry),
1988
+ ];
1708
1989
  for (const entry of candidates) {
1709
1990
  const lifetime = entry.routingInput;
1710
- if (claims.get(entry.updateId) === "retained" || (expectedOwner.startupUpdateIds?.has(entry.updateId) &&
1711
- !claims.has(entry.updateId) && deps.shouldReviewHistoricalInput))
1991
+ if (!lifetime ||
1992
+ entry.state !== "pending" ||
1993
+ !binding ||
1994
+ claims.get(entry.updateId) === "queued" ||
1995
+ unsettledExecutionsByUpdateId.get(entry.updateId)?.size)
1712
1996
  continue;
1713
- if (!lifetime || lifetime.phase !== "waiting" || entry.state !== "pending" || !binding || unsettledExecutionsByUpdateId.get(entry.updateId)?.size)
1997
+ // Classify the first startup snapshot before constructing any historical expiry carrier.
1998
+ if (expectedOwner.startupUpdateIds?.has(entry.updateId) &&
1999
+ !claims.has(entry.updateId) &&
2000
+ deps.shouldReviewHistoricalInput) {
2001
+ nextAtMs = Math.min(nextAtMs ?? Infinity, Math.max(getNowMs() + 1, lifetime.expiresAtMs));
1714
2002
  continue;
2003
+ }
1715
2004
  if (getNowMs() < lifetime.expiresAtMs) {
1716
2005
  nextAtMs = Math.min(nextAtMs ?? Infinity, lifetime.expiresAtMs);
1717
2006
  continue;
1718
2007
  }
1719
2008
  const existing = deferredSources.get(entry.updateId);
1720
- if (existing && !isDeepStrictEqual(existing.entry, entry))
1721
- continue;
1722
- const source = existing ?? { entry: structuredClone(entry), journalBindingKey: binding, historical: true };
2009
+ if (existing && !isDeepStrictEqual(existing.entry, entry)) {
2010
+ // Lost clock/selection ACKs may leave only this immutable metadata behind its exact source; reconciliation grants expiry, never another dispatch.
2011
+ const previous = existing.entry.routingInput;
2012
+ if (!isDeepStrictEqual({ ...existing.entry, routingInput: lifetime }, entry) ||
2013
+ (previous &&
2014
+ (!isDeepStrictEqual({ ...previous, phase: lifetime.phase }, lifetime) ||
2015
+ (previous.phase === "selected" && lifetime.phase !== "selected"))))
2016
+ continue;
2017
+ existing.entry = structuredClone(entry);
2018
+ }
2019
+ const source = existing ?? {
2020
+ entry: structuredClone(entry),
2021
+ journalBindingKey: binding,
2022
+ historical: true,
2023
+ };
1723
2024
  deferredSources.set(entry.updateId, source);
1724
2025
  if (!claims.has(entry.updateId))
1725
2026
  claims.set(entry.updateId, "historical");
1726
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted &&
1727
- deps.isContextCurrent?.(expectedOwner.ctx) !== false && deps.getJournalBindingKey?.() === binding &&
1728
- deps.hasAuthority(expectedOwner.ctx) && isQueueOwnerIdentityCurrent(expectedOwner) && deferredSources.get(entry.updateId) === source &&
1729
- isDeepStrictEqual(source.entry, entry) && claims.get(entry.updateId) !== "queued";
2027
+ const current = () => owner === expectedOwner &&
2028
+ !expectedOwner.controller.signal.aborted &&
2029
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2030
+ deps.getJournalBindingKey?.() === binding &&
2031
+ deps.hasAuthority(expectedOwner.ctx) &&
2032
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
2033
+ deferredSources.get(entry.updateId) === source &&
2034
+ isDeepStrictEqual(source.entry, entry) &&
2035
+ claims.get(entry.updateId) !== "queued";
1730
2036
  let committed;
1731
2037
  try {
1732
2038
  if (current())
1733
- await expireRoutingObserver({ original: structuredClone(entry), journalBindingKey: binding, isCurrent: current,
2039
+ await expireRoutingObserver({
2040
+ original: structuredClone(entry),
2041
+ journalBindingKey: binding,
2042
+ isCurrent: current,
1734
2043
  expire() {
1735
2044
  if (!current())
1736
2045
  return undefined;
1737
2046
  if (committed)
1738
2047
  return { ...committed, duplicate: true };
1739
2048
  claims.set(entry.updateId, "abandoning");
1740
- committed = expireRouting({ entry: source.entry, journalBindingKey: binding, operatorUserId: lifetime.operatorUserId, isCurrent: current });
2049
+ committed = expireRouting({
2050
+ entry: source.entry,
2051
+ journalBindingKey: binding,
2052
+ operatorUserId: lifetime.operatorUserId,
2053
+ isCurrent: current,
2054
+ });
2055
+ // Source expiry is terminal before chooser/cleanup awaits; later failures must not retain a deferred/retry source.
1741
2056
  claims.delete(entry.updateId);
1742
2057
  deferredSources.delete(entry.updateId);
1743
2058
  state.journalEntryCount = committed.entryCount;
1744
2059
  state.journalSerializedBytes = committed.serializedBytes;
1745
2060
  updateClaimCounts();
1746
2061
  notifyStateChange();
1747
- changed = true;
2062
+ changed ||= !committed.duplicate;
1748
2063
  return committed;
1749
- } }, expectedOwner.ctx, expectedOwner.controller.signal);
2064
+ },
2065
+ }, expectedOwner.ctx, expectedOwner.controller.signal);
1750
2066
  }
1751
2067
  catch (error) {
1752
- recordRuntimeEvent(error, { phase: "routing-input-expiry", updateId: entry.updateId });
2068
+ recordRuntimeEvent(error, {
2069
+ phase: "routing-input-expiry",
2070
+ updateId: entry.updateId,
2071
+ });
1753
2072
  }
1754
2073
  if (!committed)
1755
2074
  nextAtMs = Math.min(nextAtMs ?? Infinity, getNowMs() + 60_000);
1756
2075
  }
1757
- if (nextAtMs !== undefined && owner === expectedOwner && !expectedOwner.controller.signal.aborted) {
2076
+ if (nextAtMs !== undefined &&
2077
+ owner === expectedOwner &&
2078
+ !expectedOwner.controller.signal.aborted) {
1758
2079
  const token = {};
1759
2080
  routingTimerToken = token;
1760
2081
  routingTimer = scheduleRetry(() => {
1761
- if (routingTimerToken !== token || owner !== expectedOwner || expectedOwner.controller.signal.aborted)
2082
+ if (routingTimerToken !== token ||
2083
+ owner !== expectedOwner ||
2084
+ expectedOwner.controller.signal.aborted)
1762
2085
  return;
1763
2086
  routingTimer = undefined;
1764
2087
  routingTimerToken = undefined;
@@ -1770,32 +2093,69 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1770
2093
  };
1771
2094
  const mutateRoutingSource = (input, select) => {
1772
2095
  const expectedOwner = owner, ids = [...input.sourceUpdateIds];
1773
- if (!ids.length || !ids.includes(input.updateId) || new Set(ids).size !== ids.length || ids.some(id => !Number.isSafeInteger(id) || id < 0))
2096
+ if (!ids.length ||
2097
+ !ids.includes(input.updateId) ||
2098
+ new Set(ids).size !== ids.length ||
2099
+ ids.some((id) => !Number.isSafeInteger(id) || id < 0))
1774
2100
  return undefined;
1775
- const sources = ids.map(id => deferredSources.get(id)), binding = sources[0]?.journalBindingKey;
1776
- const current = () => !!expectedOwner && owner === expectedOwner && expectedOwner.controller.signal === input.signal && !input.signal.aborted &&
1777
- !!binding && sources.every((source, index) => !!source && deferredSources.get(ids[index]) === source && !source.historical &&
1778
- source.journalBindingKey === binding && claims.get(ids[index]) !== "queued" && claims.get(ids[index]) !== "abandoning") &&
1779
- deps.getJournalBindingKey?.() === binding && deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
1780
- deps.hasAuthority(expectedOwner.ctx) && isQueueOwnerIdentityCurrent(expectedOwner);
2101
+ const sources = ids.map((id) => deferredSources.get(id)), binding = sources[0]?.journalBindingKey;
2102
+ const current = () => !!expectedOwner &&
2103
+ owner === expectedOwner &&
2104
+ expectedOwner.controller.signal === input.signal &&
2105
+ !input.signal.aborted &&
2106
+ !!binding &&
2107
+ sources.every((source, index) => !!source &&
2108
+ deferredSources.get(ids[index]) === source &&
2109
+ !source.historical &&
2110
+ source.journalBindingKey === binding &&
2111
+ claims.get(ids[index]) !== "queued" &&
2112
+ claims.get(ids[index]) !== "abandoning") &&
2113
+ deps.getJournalBindingKey?.() === binding &&
2114
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2115
+ deps.hasAuthority(expectedOwner.ctx) &&
2116
+ isQueueOwnerIdentityCurrent(expectedOwner);
1781
2117
  if (!current() || !armRouting || !selectRouting || !expireRoutingObserver)
1782
2118
  return undefined;
1783
- const authority = { journalBindingKey: binding, entries: sources.map(source => structuredClone(source.entry)), operatorUserId: input.operatorUserId, isCurrent: current };
1784
- const result = select ? selectRouting(authority) : { issued: false, entries: armRouting({ ...authority, publishedAtMs: getNowMs() }) };
1785
- if (result.entries.length !== ids.length || result.entries.some((entry, index) => entry.updateId !== ids[index] || !entry.routingInput) || !current()) {
2119
+ const authority = {
2120
+ journalBindingKey: binding,
2121
+ entries: sources.map((source) => structuredClone(source.entry)),
2122
+ operatorUserId: input.operatorUserId,
2123
+ isCurrent: current,
2124
+ };
2125
+ const result = select
2126
+ ? selectRouting(authority)
2127
+ : {
2128
+ issued: false,
2129
+ entries: armRouting({
2130
+ ...authority,
2131
+ publishedAtMs: getNowMs(),
2132
+ ...(input.chooser ? { chooser: input.chooser } : {}),
2133
+ }),
2134
+ };
2135
+ if (result.entries.length !== ids.length ||
2136
+ result.entries.some((entry, index) => entry.updateId !== ids[index] || !entry.routingInput) ||
2137
+ !current()) {
1786
2138
  throw new Error("Telegram routing input lifetime ACK was not confirmed.");
1787
2139
  }
1788
2140
  const retained = deps.journal.read().entries;
1789
- if (result.entries.some(entry => !isDeepStrictEqual(retained.find(value => value.updateId === entry.updateId), entry)) || !current()) {
2141
+ if (result.entries.some((entry) => !isDeepStrictEqual(retained.find((value) => value.updateId === entry.updateId), entry)) ||
2142
+ !current()) {
1790
2143
  throw new Error("Telegram routing input lifetime ACK was not retained.");
1791
2144
  }
1792
- result.entries.forEach((entry, index) => { sources[index].entry = structuredClone(entry); });
2145
+ // Retain the decoder's exact source representation, not the mutation result's property ordering.
2146
+ result.entries.forEach((entry, index) => {
2147
+ sources[index].entry = structuredClone(retained.find((value) => value.updateId === entry.updateId));
2148
+ });
1793
2149
  pendingSignal = true;
1794
2150
  launchDrain();
1795
- return { issued: result.issued, lifetime: { ...result.entries[0].routingInput } };
2151
+ return {
2152
+ issued: result.issued,
2153
+ lifetime: { ...result.entries[0].routingInput },
2154
+ };
1796
2155
  };
1797
2156
  const drain = async (expectedOwner) => {
1798
- while (owner === expectedOwner && !expectedOwner.controller.signal.aborted) {
2157
+ while (owner === expectedOwner &&
2158
+ !expectedOwner.controller.signal.aborted) {
1799
2159
  const authorityResult = checkAuthority(expectedOwner);
1800
2160
  if (authorityResult)
1801
2161
  return authorityResult;
@@ -1806,7 +2166,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1806
2166
  scheduledRetryAtMs = await refreshJournalState(snapshot, expectedOwner);
1807
2167
  // One complete validated baseline per generation, before batching or execution.
1808
2168
  // Replayed v1 entries do not prove that a prior process never forwarded them.
1809
- expectedOwner.startupUpdateIds ??= new Set(snapshot.entries.map(entry => entry.updateId));
2169
+ expectedOwner.startupUpdateIds ??= new Set(snapshot.entries.map((entry) => entry.updateId));
1810
2170
  }
1811
2171
  catch (error) {
1812
2172
  return blockWithFailure("journal-read", "journal-read", error);
@@ -1823,24 +2183,39 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1823
2183
  let blockedInputCustody;
1824
2184
  delete state.blockedInputCustody;
1825
2185
  for (const candidate of snapshot.entries) {
2186
+ if (livePreparation?.ids.has(candidate.updateId) &&
2187
+ (!livePreparation.released || livePreparation.status?.issued))
2188
+ continue;
1826
2189
  if (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION &&
1827
2190
  (candidate.state === "retry-wait" || candidate.state === "failed")) {
1828
2191
  hasUnavailableInputCustody = true;
1829
- blockedInputCustody ??= { updateId: candidate.updateId, kind: "legacy-retry-state" };
2192
+ blockedInputCustody ??= {
2193
+ updateId: candidate.updateId,
2194
+ kind: "legacy-retry-state",
2195
+ };
1830
2196
  continue;
1831
2197
  }
1832
- if (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION && candidate.inputClaim) {
2198
+ if (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION &&
2199
+ candidate.inputClaim) {
1833
2200
  if (candidate.inputClaim.phase === "running") {
1834
2201
  hasOutcomeUnknownInput = true;
1835
- if (!blockedInputCustody || blockedInputCustody.kind !== "running-outcome-unknown")
1836
- blockedInputCustody = { updateId: candidate.updateId,
1837
- kind: "running-outcome-unknown" };
2202
+ if (!blockedInputCustody ||
2203
+ blockedInputCustody.kind !== "running-outcome-unknown")
2204
+ blockedInputCustody = {
2205
+ updateId: candidate.updateId,
2206
+ kind: "running-outcome-unknown",
2207
+ };
1838
2208
  continue;
1839
2209
  }
1840
- if (candidate.inputClaim.handoff || !isTelegramUpdateJournalQueueOwnerProcess(candidate.inputClaim.owner, expectedOwner.queueOwnerIdentity)) {
2210
+ if (candidate.inputClaim.handoff ||
2211
+ !isTelegramUpdateJournalQueueOwnerProcess(candidate.inputClaim.owner, expectedOwner.queueOwnerIdentity)) {
1841
2212
  hasUnavailableInputCustody = true;
1842
- blockedInputCustody ??= { updateId: candidate.updateId,
1843
- kind: candidate.inputClaim.handoff ? "handoff-frozen" : "foreign-ready" };
2213
+ blockedInputCustody ??= {
2214
+ updateId: candidate.updateId,
2215
+ kind: candidate.inputClaim.handoff
2216
+ ? "handoff-frozen"
2217
+ : "foreign-ready",
2218
+ };
1844
2219
  continue;
1845
2220
  }
1846
2221
  }
@@ -1870,6 +2245,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1870
2245
  const completedUpdateIds = [];
1871
2246
  let snapshotInvalidated = false;
1872
2247
  for (const entry of entries) {
2248
+ if (livePreparation?.ids.has(entry.updateId) &&
2249
+ (!livePreparation.released || livePreparation.status?.issued))
2250
+ continue;
1873
2251
  const priorExecutions = unsettledExecutionsByUpdateId.get(entry.updateId);
1874
2252
  if (priorExecutions?.size) {
1875
2253
  const completionResult = commitCompletedBatch(expectedOwner, completedUpdateIds);
@@ -1888,7 +2266,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1888
2266
  completedUpdateIds.push({ updateId: entry.updateId });
1889
2267
  continue;
1890
2268
  }
1891
- if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) && entry.state === "pending" && deps.journal.inspectPendingRetention) {
2269
+ if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) &&
2270
+ entry.state === "pending" &&
2271
+ deps.journal.inspectPendingRetention) {
1892
2272
  const authorityResult = checkAuthority(expectedOwner, entry.updateId);
1893
2273
  if (authorityResult)
1894
2274
  return authorityResult;
@@ -1899,7 +2279,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1899
2279
  }
1900
2280
  catch (error) {
1901
2281
  unverifiable = true;
1902
- recordRuntimeEvent(error, { phase: "pending-retention-inspection", updateId: entry.updateId });
2282
+ recordRuntimeEvent(error, {
2283
+ phase: "pending-retention-inspection",
2284
+ updateId: entry.updateId,
2285
+ });
1903
2286
  }
1904
2287
  if (retained || unverifiable) {
1905
2288
  // A copy is protection evidence only. A fresh owner must explicitly retry
@@ -1912,19 +2295,28 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1912
2295
  continue;
1913
2296
  }
1914
2297
  }
1915
- if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) && (entry.state === "pending" || entry.state === "retry-wait")) {
2298
+ if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) &&
2299
+ (entry.state === "pending" || entry.state === "retry-wait")) {
1916
2300
  const historical = expectedOwner.startupUpdateIds.has(entry.updateId);
2301
+ let revived = false;
1917
2302
  const journalBindingKey = deps.getJournalBindingKey?.();
1918
2303
  // Classifiers and handler projections receive no mutable alias to CAS evidence.
1919
2304
  if (journalBindingKey)
1920
- deferredSources.set(entry.updateId, { entry: structuredClone(entry), journalBindingKey,
1921
- ...(historical ? { historical: true } : {}) });
1922
- const classifyPending = historical && entry.state === "pending" ? deps.shouldReviewHistoricalInput : deps.shouldHoldPendingInput;
2305
+ deferredSources.set(entry.updateId, {
2306
+ entry: structuredClone(entry),
2307
+ journalBindingKey,
2308
+ ...(historical ? { historical: true } : {}),
2309
+ });
2310
+ const classifyPending = historical && entry.state === "pending"
2311
+ ? deps.shouldReviewHistoricalInput
2312
+ : deps.shouldHoldPendingInput;
1923
2313
  if (classifyPending) {
1924
2314
  const checkHistoricalAuthority = () => {
1925
- if (owner !== expectedOwner || expectedOwner.controller.signal.aborted)
2315
+ if (owner !== expectedOwner ||
2316
+ expectedOwner.controller.signal.aborted)
1926
2317
  return "aborted";
1927
- if (deps.getJournalBindingKey?.() !== journalBindingKey || deps.isContextCurrent?.(expectedOwner.ctx) === false) {
2318
+ if (deps.getJournalBindingKey?.() !== journalBindingKey ||
2319
+ deps.isContextCurrent?.(expectedOwner.ctx) === false) {
1928
2320
  transition("blocked", entry.updateId, "authority-lost");
1929
2321
  return "blocked";
1930
2322
  }
@@ -1946,12 +2338,18 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1946
2338
  const authorityResult = checkHistoricalAuthority();
1947
2339
  if (authorityResult)
1948
2340
  return authorityResult;
1949
- if (typeof review !== "boolean" && !(historical && entry.state === "pending" && review === "retain"))
2341
+ if (typeof review !== "boolean" &&
2342
+ !(historical &&
2343
+ entry.state === "pending" &&
2344
+ (review === "retain" ||
2345
+ (review === "revive" &&
2346
+ isRevivableTelegramRoutingInput(entry, getNowMs())))))
1950
2347
  return blockWithFailure("execution", "historical-review-classification", new TelegramUpdateAdmissionOutcomeError("Historical classification must return a boolean or retain verdict."), entry.updateId);
1951
2348
  // Classification may await a domain read; accepted/changed source evidence wins.
1952
2349
  try {
1953
2350
  const current = deps.journal.read();
1954
- if (current.version !== snapshot.version || JSON.stringify(current.entries.find(candidate => candidate.updateId === entry.updateId)) !== JSON.stringify(entry)) {
2351
+ if (current.version !== snapshot.version ||
2352
+ JSON.stringify(current.entries.find((candidate) => candidate.updateId === entry.updateId)) !== JSON.stringify(entry)) {
1955
2353
  snapshotInvalidated = true;
1956
2354
  break;
1957
2355
  }
@@ -1959,38 +2357,96 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1959
2357
  catch (error) {
1960
2358
  return blockWithFailure("journal-read", "historical-source-recheck", error, entry.updateId);
1961
2359
  }
1962
- if (review === "retain") {
2360
+ if (review === "revive") {
2361
+ // A still-waiting saved clock proves no selection ran, so this generation owns the source as live input.
2362
+ revived = true;
2363
+ const source = deferredSources.get(entry.updateId);
2364
+ if (source)
2365
+ delete source.historical;
2366
+ expectedOwner.startupUpdateIds = new Set([...expectedOwner.startupUpdateIds].filter((id) => id !== entry.updateId));
2367
+ }
2368
+ else if (review === "retain") {
1963
2369
  claims.set(entry.updateId, "retained");
1964
2370
  transition("deferred", entry.updateId);
1965
2371
  continue;
1966
2372
  }
1967
- if (review && historical && spendHistorical && classifyPending === deps.shouldReviewHistoricalInput) {
1968
- completedUpdateIds.push({ updateId: entry.updateId, spent: true });
2373
+ if (review &&
2374
+ historical &&
2375
+ spendHistorical &&
2376
+ !entry.routingInput &&
2377
+ classifyPending === deps.shouldReviewHistoricalInput) {
2378
+ completedUpdateIds.push({
2379
+ updateId: entry.updateId,
2380
+ spent: true,
2381
+ });
1969
2382
  continue;
1970
2383
  }
1971
- if (review) {
2384
+ if (review === true) {
1972
2385
  claims.set(entry.updateId, "historical");
1973
2386
  transition("deferred", entry.updateId);
1974
2387
  continue;
1975
2388
  }
1976
2389
  }
1977
- if (entry.state === "pending" && entry.routingInput) {
1978
- if (historical && spendHistorical) {
1979
- completedUpdateIds.push({ updateId: entry.updateId, spent: true });
1980
- continue;
1981
- }
2390
+ if (!revived && entry.state === "pending" && entry.routingInput) {
1982
2391
  claims.set(entry.updateId, "historical");
1983
2392
  transition("deferred", entry.updateId);
1984
2393
  continue;
1985
2394
  }
1986
2395
  }
2396
+ if (livePreparation?.ids.has(entry.updateId) &&
2397
+ (!livePreparation.released || livePreparation.status?.issued))
2398
+ continue;
2399
+ const prepared = livePreparation?.released && livePreparation.ids.has(entry.updateId)
2400
+ ? livePreparation
2401
+ : undefined;
2402
+ const status = prepared?.status;
2403
+ if (status) {
2404
+ status.issued = true;
2405
+ try {
2406
+ if (!status.current() ||
2407
+ !isDeepStrictEqual(prepared.saved?.[0], entry))
2408
+ throw new Error("Prepared status original changed.");
2409
+ status.assertCurrent();
2410
+ if (!status.current())
2411
+ throw new Error("Prepared status owner changed after assertion.");
2412
+ }
2413
+ catch (error) {
2414
+ claims.set(entry.updateId, "retained");
2415
+ recordRuntimeEvent(error, {
2416
+ phase: "prepared-status-authority",
2417
+ updateId: entry.updateId,
2418
+ });
2419
+ continue;
2420
+ }
2421
+ }
1987
2422
  clearRetryTimer();
1988
2423
  transition("executing", entry.updateId);
1989
- const execution = await executeWithinOwner(expectedOwner, entry.update);
2424
+ const execution = await executeWithinOwner(expectedOwner, entry.update, status
2425
+ ? async (update, ctx, signal) => {
2426
+ const admitted = await status.admit(update, ctx, signal);
2427
+ status.carrier = admitted.carrier;
2428
+ if (!status.current() ||
2429
+ admitted.outcome.kind !== "deferred" ||
2430
+ !isDeepStrictEqual(deps.journal
2431
+ .read()
2432
+ .entries.find((value) => value.updateId === entry.updateId), entry) ||
2433
+ !status.current())
2434
+ throw new Error("Prepared status admission is unconfirmed.");
2435
+ return admitted.outcome;
2436
+ }
2437
+ : undefined);
1990
2438
  if (execution === TELEGRAM_UPDATE_WORKER_EXECUTION_ABORTED) {
1991
2439
  return "aborted";
1992
2440
  }
1993
2441
  if (!execution.ok) {
2442
+ if (status) {
2443
+ claims.set(entry.updateId, "retained");
2444
+ recordRuntimeEvent(execution.error, {
2445
+ phase: "prepared-status-admission",
2446
+ updateId: entry.updateId,
2447
+ });
2448
+ continue;
2449
+ }
1994
2450
  if (deps.executeCustodiedUpdate) {
1995
2451
  transition("blocked", entry.updateId, "execution");
1996
2452
  return "blocked";
@@ -2039,7 +2495,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2039
2495
  return postExecutionAuthority;
2040
2496
  const claimableUpdateIds = new Set([
2041
2497
  entry.updateId,
2042
- ...[...claims].filter(([, claim]) => claim !== "abandoning" && claim !== "historical" && claim !== "retained").map(([id]) => id),
2498
+ ...[...claims]
2499
+ .filter(([id, claim]) => !livePreparation?.ids.has(id) &&
2500
+ claim !== "abandoning" &&
2501
+ claim !== "historical" &&
2502
+ claim !== "retained")
2503
+ .map(([id]) => id),
2043
2504
  ]);
2044
2505
  let outcome;
2045
2506
  try {
@@ -2049,11 +2510,37 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2049
2510
  return blockWithFailure("invalid-outcome", "invalid-outcome", error, entry.updateId);
2050
2511
  }
2051
2512
  if (outcome.kind === "deferred") {
2052
- if (outcome.routingReview && !deferredSources.get(entry.updateId)?.historical) {
2513
+ if (outcome.routingReview &&
2514
+ !deferredSources.get(entry.updateId)?.historical) {
2053
2515
  return blockWithFailure("invalid-outcome", "historical-review-source", new TelegramUpdateAdmissionOutcomeError("Historical review needs an exact startup source."), entry.updateId);
2054
2516
  }
2055
2517
  claims.set(entry.updateId, outcome.routingReview ? "historical" : "deferred");
2056
2518
  transition("deferred", entry.updateId);
2519
+ if (status) {
2520
+ // Native warm deferral precedes consumption; late reports retain the existing ACK owner.
2521
+ if (livePreparation === prepared)
2522
+ livePreparation = undefined;
2523
+ updateClaimCounts();
2524
+ try {
2525
+ if (!status.current())
2526
+ throw new Error("Prepared status consumer owner changed.");
2527
+ status.assertCurrent();
2528
+ if (!status.bindCarrier(status.carrier) || !status.current())
2529
+ throw new Error("Prepared status carrier binding refused.");
2530
+ status.assertCurrent();
2531
+ if (!status.current())
2532
+ throw new Error("Prepared status owner changed before consumption.");
2533
+ await status.execute();
2534
+ if (!status.current())
2535
+ throw new Error("Prepared status consumption result is unconfirmed.");
2536
+ }
2537
+ catch (error) {
2538
+ recordRuntimeEvent(error, {
2539
+ phase: "prepared-status-consume",
2540
+ updateId: entry.updateId,
2541
+ });
2542
+ }
2543
+ }
2057
2544
  continue;
2058
2545
  }
2059
2546
  if (outcome.kind === "queued") {
@@ -2068,7 +2555,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2068
2555
  snapshotInvalidated = true;
2069
2556
  break;
2070
2557
  }
2071
- completedUpdateIds.push({ updateId: entry.updateId, ...(outcome.expectedSource ? { expectedSource: outcome.expectedSource } : {}) });
2558
+ completedUpdateIds.push({
2559
+ updateId: entry.updateId,
2560
+ ...(outcome.expectedSource
2561
+ ? { expectedSource: outcome.expectedSource }
2562
+ : {}),
2563
+ });
2072
2564
  }
2073
2565
  const completionResult = commitCompletedBatch(expectedOwner, completedUpdateIds);
2074
2566
  if (completionResult)
@@ -2076,8 +2568,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2076
2568
  if (!snapshotInvalidated && !hasMoreEntries)
2077
2569
  continue;
2078
2570
  await yieldToEventLoop();
2079
- if (owner !== expectedOwner ||
2080
- expectedOwner.controller.signal.aborted) {
2571
+ if (owner !== expectedOwner || expectedOwner.controller.signal.aborted) {
2081
2572
  return "aborted";
2082
2573
  }
2083
2574
  }
@@ -2092,7 +2583,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2092
2583
  try {
2093
2584
  startupKey = deps.getJournalBindingKey?.();
2094
2585
  }
2095
- catch { /* Missing startup identity grants no notification. */ }
2586
+ catch {
2587
+ /* Missing startup identity grants no notification. */
2588
+ }
2096
2589
  }
2097
2590
  let lastResult;
2098
2591
  const run = async () => {
@@ -2113,28 +2606,55 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2113
2606
  if (pendingSignal && !expectedOwner.controller.signal.aborted) {
2114
2607
  launchDrain();
2115
2608
  }
2116
- else if (lastResult === "idle" && startupKey && onHeldSourcesPrepared && !expectedOwner.heldSourcesPreparedIssued) {
2609
+ else if (lastResult === "idle" &&
2610
+ startupKey &&
2611
+ onHeldSourcesPrepared &&
2612
+ !expectedOwner.heldSourcesPreparedIssued) {
2117
2613
  try {
2118
2614
  const ctx = expectedOwner.ctx, key = startupKey;
2119
- const isCurrent = () => owner === expectedOwner && !blocked && !expectedOwner.controller.signal.aborted &&
2120
- deps.getJournalBindingKey?.() === key && deps.isContextCurrent?.(ctx) !== false && deps.hasAuthority(ctx);
2615
+ const isCurrent = () => owner === expectedOwner &&
2616
+ !blocked &&
2617
+ !expectedOwner.controller.signal.aborted &&
2618
+ deps.getJournalBindingKey?.() === key &&
2619
+ deps.isContextCurrent?.(ctx) !== false &&
2620
+ deps.hasAuthority(ctx);
2121
2621
  if (!isCurrent())
2122
2622
  return;
2123
2623
  expectedOwner.heldSourcesPreparedIssued = true;
2124
2624
  // Do not couple lifecycle/stop or the worker drain to a held controller/API reply.
2125
- void Promise.resolve().then(() => isCurrent() ? onHeldSourcesPrepared({ ctx,
2126
- journalBindingKey: key, signal: expectedOwner.controller.signal, isCurrent }) : undefined).catch(error => {
2625
+ void Promise.resolve()
2626
+ .then(() => isCurrent()
2627
+ ? onHeldSourcesPrepared({
2628
+ ctx,
2629
+ journalBindingKey: key,
2630
+ signal: expectedOwner.controller.signal,
2631
+ isCurrent,
2632
+ routingSourceIds: [...deferredSources]
2633
+ .filter(([, source]) => source.journalBindingKey === key &&
2634
+ !!source.entry.routingInput)
2635
+ .map(([id]) => id),
2636
+ })
2637
+ : undefined)
2638
+ .catch((error) => {
2127
2639
  try {
2128
- deps.recordRuntimeEvent?.("inbound-worker", error, { phase: "held-source-preparation" });
2640
+ deps.recordRuntimeEvent?.("inbound-worker", error, {
2641
+ phase: "held-source-preparation",
2642
+ });
2643
+ }
2644
+ catch {
2645
+ /* Diagnostic only. */
2129
2646
  }
2130
- catch { /* Diagnostic only. */ }
2131
2647
  });
2132
2648
  }
2133
2649
  catch (error) {
2134
2650
  try {
2135
- deps.recordRuntimeEvent?.("inbound-worker", error, { phase: "held-source-preparation" });
2651
+ deps.recordRuntimeEvent?.("inbound-worker", error, {
2652
+ phase: "held-source-preparation",
2653
+ });
2654
+ }
2655
+ catch {
2656
+ /* Diagnostic only. */
2136
2657
  }
2137
- catch { /* Diagnostic only. */ }
2138
2658
  }
2139
2659
  }
2140
2660
  }
@@ -2153,22 +2673,34 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2153
2673
  const source = deferredSources.get(input.updateId);
2154
2674
  const abandonPending = deps.journal.abandonPending;
2155
2675
  const isCurrent = () => {
2156
- if (!expectedOwner || owner !== expectedOwner || input.signal.aborted ||
2157
- expectedOwner.controller.signal !== input.signal || !source ||
2158
- source.entry.routingInput?.phase === "selected" || (source.historical && historicalSource !== source) ||
2159
- (claims.get(input.updateId) !== "deferred" && claims.get(input.updateId) !== "abandoning" &&
2160
- !(claims.get(input.updateId) === "historical" && historicalSource === source)))
2676
+ if (!expectedOwner ||
2677
+ owner !== expectedOwner ||
2678
+ input.signal.aborted ||
2679
+ expectedOwner.controller.signal !== input.signal ||
2680
+ !source ||
2681
+ source.entry.routingInput?.phase === "selected" ||
2682
+ (source.historical && historicalSource !== source) ||
2683
+ (claims.get(input.updateId) !== "deferred" &&
2684
+ claims.get(input.updateId) !== "abandoning" &&
2685
+ !(claims.get(input.updateId) === "historical" &&
2686
+ historicalSource === source)))
2161
2687
  return false;
2162
- return input.isCurrent() && deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2163
- deps.getJournalBindingKey?.() === source.journalBindingKey && deps.hasAuthority(expectedOwner.ctx);
2688
+ return (input.isCurrent() &&
2689
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2690
+ deps.getJournalBindingKey?.() === source.journalBindingKey &&
2691
+ deps.hasAuthority(expectedOwner.ctx));
2164
2692
  };
2165
2693
  if (!source || !abandonPending || !isCurrent())
2166
2694
  return undefined;
2167
2695
  // Retain this claim on failure, including unknown commit: never reopen dispatch.
2168
2696
  claims.set(input.updateId, "abandoning");
2169
2697
  try {
2170
- const result = abandonPending({ ...input, entry: source.entry,
2171
- journalBindingKey: source.journalBindingKey, isCurrent });
2698
+ const result = abandonPending({
2699
+ ...input,
2700
+ entry: source.entry,
2701
+ journalBindingKey: source.journalBindingKey,
2702
+ isCurrent,
2703
+ });
2172
2704
  claims.delete(input.updateId);
2173
2705
  deferredSources.delete(input.updateId);
2174
2706
  state.journalEntryCount = result.entryCount;
@@ -2179,7 +2711,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2179
2711
  return result;
2180
2712
  }
2181
2713
  catch (error) {
2182
- recordRuntimeEvent(error, { phase: "pending-abandonment", updateId: input.updateId });
2714
+ recordRuntimeEvent(error, {
2715
+ phase: "pending-abandonment",
2716
+ updateId: input.updateId,
2717
+ });
2183
2718
  updateClaimCounts();
2184
2719
  notifyStateChange();
2185
2720
  throw error;
@@ -2189,34 +2724,93 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2189
2724
  const expectedOwner = owner;
2190
2725
  if (!expectedOwner)
2191
2726
  return undefined;
2192
- const { signal, journalBindingKey, isCurrent: callerIsCurrent, afterUpdateId } = input;
2193
- const isOwnerCurrent = () => owner === expectedOwner && expectedOwner.controller.signal === signal && !signal.aborted;
2194
- const isCurrent = () => isOwnerCurrent() && callerIsCurrent() && deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2195
- deps.getJournalBindingKey?.() === journalBindingKey && deps.hasAuthority(expectedOwner.ctx);
2196
- if (!isCurrent() || !deps.journal.abandonPending || !deps.journal.inspectPendingRetention ||
2197
- (afterUpdateId !== undefined && (!Number.isSafeInteger(afterUpdateId) || afterUpdateId < 0)))
2727
+ const { signal, journalBindingKey, isCurrent: callerIsCurrent, afterUpdateId, } = input;
2728
+ const isOwnerCurrent = () => owner === expectedOwner &&
2729
+ expectedOwner.controller.signal === signal &&
2730
+ !signal.aborted;
2731
+ const isCurrent = () => isOwnerCurrent() &&
2732
+ callerIsCurrent() &&
2733
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2734
+ deps.getJournalBindingKey?.() === journalBindingKey &&
2735
+ deps.hasAuthority(expectedOwner.ctx);
2736
+ if (!isCurrent() ||
2737
+ !deps.journal.abandonPending ||
2738
+ !deps.journal.inspectPendingRetention ||
2739
+ (afterUpdateId !== undefined &&
2740
+ (!Number.isSafeInteger(afterUpdateId) || afterUpdateId < 0)))
2198
2741
  return undefined;
2199
- const matches = [...deferredSources].filter(([id, source]) => claims.get(id) === kind &&
2200
- source.journalBindingKey === journalBindingKey && (afterUpdateId === undefined || id > afterUpdateId))
2742
+ const matches = [...deferredSources]
2743
+ .filter(([id, source]) => claims.get(id) === kind &&
2744
+ source.journalBindingKey === journalBindingKey &&
2745
+ (afterUpdateId === undefined || id > afterUpdateId))
2201
2746
  .sort(([a], [b]) => a - b);
2202
- const sources = matches.slice(0, TELEGRAM_HELD_SOURCE_INSPECTION_LIMIT).map(([updateId, source]) => {
2747
+ const sources = matches
2748
+ .slice(0, TELEGRAM_HELD_SOURCE_INSPECTION_LIMIT)
2749
+ .map(([updateId, source]) => {
2203
2750
  let committed;
2204
- return { original: structuredClone(source.entry), retry(authority) {
2751
+ return {
2752
+ original: structuredClone(source.entry),
2753
+ retry(authority) {
2205
2754
  const authorized = () => isOwnerCurrent() && authority.isCurrent() && isCurrent();
2206
2755
  if (!authorized())
2207
2756
  return undefined;
2208
2757
  if (committed)
2209
2758
  return { ...committed, duplicate: true };
2210
- const stillProtected = () => authorized() && (claims.get(updateId) === kind || claims.get(updateId) === "abandoning") &&
2759
+ const stillProtected = () => authorized() &&
2760
+ (claims.get(updateId) === kind ||
2761
+ claims.get(updateId) === "abandoning") &&
2211
2762
  deferredSources.get(updateId) === source;
2212
2763
  if (!stillProtected())
2213
2764
  return undefined;
2214
2765
  committed = abandonSource({ ...authority, updateId, signal, isCurrent: stillProtected }, source);
2215
2766
  return committed;
2216
- } };
2767
+ },
2768
+ };
2217
2769
  });
2218
- return isCurrent() ? { sources,
2219
- ...(matches.length > TELEGRAM_HELD_SOURCE_INSPECTION_LIMIT ? { nextAfterUpdateId: matches[TELEGRAM_HELD_SOURCE_INSPECTION_LIMIT - 1][0] } : {}) } : undefined;
2770
+ return isCurrent()
2771
+ ? {
2772
+ sources,
2773
+ ...(matches.length > TELEGRAM_HELD_SOURCE_INSPECTION_LIMIT
2774
+ ? {
2775
+ nextAfterUpdateId: matches[TELEGRAM_HELD_SOURCE_INSPECTION_LIMIT - 1][0],
2776
+ }
2777
+ : {}),
2778
+ }
2779
+ : undefined;
2780
+ };
2781
+ const inspectDeferredOriginal = (input) => {
2782
+ const expectedOwner = owner;
2783
+ const source = deferredSources.get(input.updateId);
2784
+ const current = () => {
2785
+ if (!expectedOwner ||
2786
+ owner !== expectedOwner ||
2787
+ input.signal.aborted ||
2788
+ expectedOwner.controller.signal !== input.signal ||
2789
+ !source ||
2790
+ deferredSources.get(input.updateId) !== source ||
2791
+ claims.get(input.updateId) !== "deferred" ||
2792
+ deps.getJournalBindingKey?.() !== source.journalBindingKey ||
2793
+ deps.isContextCurrent?.(expectedOwner.ctx) === false ||
2794
+ !deps.hasAuthority(expectedOwner.ctx))
2795
+ return false;
2796
+ return isQueueOwnerIdentityCurrent(expectedOwner);
2797
+ };
2798
+ if (!current())
2799
+ return undefined;
2800
+ const snapshot = deps.journal.read();
2801
+ const entry = snapshot.entries.find((value) => value.updateId === input.updateId);
2802
+ if (!isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) ||
2803
+ !entry ||
2804
+ JSON.stringify(entry) !== JSON.stringify(source.entry) ||
2805
+ !current())
2806
+ return undefined;
2807
+ return {
2808
+ source: {
2809
+ journalBindingKey: source.journalBindingKey,
2810
+ ...createTelegramUpdateJournalEntryDigest(entry),
2811
+ },
2812
+ entry,
2813
+ };
2220
2814
  };
2221
2815
  return {
2222
2816
  start(ctx) {
@@ -2238,6 +2832,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2238
2832
  controller: createAbortController(),
2239
2833
  queueOwnerIdentity,
2240
2834
  };
2835
+ livePreparation = undefined;
2836
+ delete state.preparedInputCount;
2241
2837
  claims.clear();
2242
2838
  deferredSources.clear();
2243
2839
  committedQueueReceipts.clear();
@@ -2251,25 +2847,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2251
2847
  state.journalEntryCount = 0;
2252
2848
  state.journalSerializedBytes = 0;
2253
2849
  state.oldestAdmittedAtMs = undefined;
2254
- state.deferredClaimCount = 0;
2255
- delete state.abandoningClaimCount;
2256
- delete state.historicalClaimCount;
2257
- state.queuedClaimCount = 0;
2258
- state.foreignQueuedCount = 0;
2259
- delete state.foreignQueuedOwner;
2260
- state.retryWaitCount = 0;
2261
- state.failedCount = 0;
2262
- state.nextRetryUpdateId = undefined;
2263
- state.nextRetryAtMs = undefined;
2264
- state.nextRetryAttemptCount = undefined;
2265
- state.nextRetryFailureClass = undefined;
2266
- state.failedUpdateId = undefined;
2267
- state.failedFailureId = undefined;
2268
- state.failedAttemptCount = undefined;
2269
- state.failedClass = undefined;
2270
- state.failedSummary = undefined;
2271
- state.terminalFailureAtMs = undefined;
2272
- state.unsettledExecutionCount = unsettledExecutions.size;
2850
+ clearTelegramUpdateWorkerClaimState(state, unsettledExecutions.size);
2273
2851
  state.lastCompletedUpdateId = undefined;
2274
2852
  state.lastCompletedAtMs = undefined;
2275
2853
  state.lastFailureAtMs = undefined;
@@ -2277,6 +2855,401 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2277
2855
  notifyStateChange();
2278
2856
  launchDrain();
2279
2857
  },
2858
+ prepareDeferredLiveInput(input) {
2859
+ const expectedOwner = owner, binding = deps.getJournalBindingKey?.(), ids = [...input.sourceUpdateIds];
2860
+ const sources = ids.map((id) => deferredSources.get(id));
2861
+ const ownerCurrent = () => !!expectedOwner &&
2862
+ owner === expectedOwner &&
2863
+ expectedOwner.controller.signal === input.signal &&
2864
+ !input.signal.aborted &&
2865
+ !!binding &&
2866
+ deps.getJournalBindingKey?.() === binding &&
2867
+ deps.hasAuthority(expectedOwner.ctx) &&
2868
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2869
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
2870
+ input.isCurrent();
2871
+ const sourcesCurrent = () => ownerCurrent() &&
2872
+ sources.every((source, index) => !!source &&
2873
+ deferredSources.get(ids[index]) === source &&
2874
+ source.journalBindingKey === binding &&
2875
+ claims.get(ids[index]) === "deferred" &&
2876
+ isFreshLiveSource(expectedOwner, ids[index]));
2877
+ if (!ownerCurrent() ||
2878
+ !ids.length ||
2879
+ !ids.includes(input.updateId) ||
2880
+ new Set(ids).size !== ids.length ||
2881
+ ids.some((id) => !Number.isSafeInteger(id) || id < 0) ||
2882
+ livePreparation ||
2883
+ deps.executeCustodiedUpdate ||
2884
+ !sourcesCurrent())
2885
+ return undefined;
2886
+ const confirm = () => {
2887
+ if (!sourcesCurrent())
2888
+ return false;
2889
+ try {
2890
+ const snapshot = deps.journal.read();
2891
+ return (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) &&
2892
+ sourcesCurrent() &&
2893
+ sources.every((source, index) => snapshot.entries.filter((entry) => entry.updateId === ids[index]).length === 1 &&
2894
+ source?.entry.state === "pending" &&
2895
+ !source.entry.queueOwner &&
2896
+ !source.entry.queueReceiptId &&
2897
+ !source.entry.preApprovalExcluded &&
2898
+ isDeepStrictEqual(snapshot.entries.find((entry) => entry.updateId === ids[index]), source.entry)));
2899
+ }
2900
+ catch (error) {
2901
+ recordRuntimeEvent(error, { phase: "live-deferred-observe" });
2902
+ return false;
2903
+ }
2904
+ };
2905
+ if (!confirm())
2906
+ return undefined;
2907
+ const held = {
2908
+ version: 1,
2909
+ ids: new Set(ids),
2910
+ };
2911
+ livePreparation = held;
2912
+ const current = () => livePreparation === held && sourcesCurrent();
2913
+ const available = () => current() && !held.discardIssued;
2914
+ const finish = () => {
2915
+ livePreparation = undefined;
2916
+ updateClaimCounts();
2917
+ notifyStateChange();
2918
+ };
2919
+ updateClaimCounts();
2920
+ notifyStateChange();
2921
+ return {
2922
+ sourceUpdateIds: Object.freeze(ids),
2923
+ isCurrent: available,
2924
+ isOwnerCurrent: ownerCurrent,
2925
+ confirmSaved: () => available() && confirm(),
2926
+ beginRelease(canRelease) {
2927
+ if (!available() ||
2928
+ !canRelease() ||
2929
+ !confirm() ||
2930
+ !canRelease() ||
2931
+ !available())
2932
+ return false;
2933
+ for (const source of sources)
2934
+ source.liveReleaseIssued = true;
2935
+ finish();
2936
+ return true;
2937
+ },
2938
+ settleTransferred(canSettle) {
2939
+ if (!current() || !canSettle() || !completeExact)
2940
+ return "protected";
2941
+ if (held.discardIssued)
2942
+ return "unknown";
2943
+ if (!confirm() || !canSettle() || !available())
2944
+ return "protected";
2945
+ const expected = sources.map((source) => createTelegramUpdateJournalEntryDigest(source.entry));
2946
+ held.discardIssued = true;
2947
+ try {
2948
+ const result = completeExact(ids, expected, undefined, () => current() && canSettle());
2949
+ if (!current() ||
2950
+ !canSettle() ||
2951
+ result.removedUpdateIds.length !== ids.length ||
2952
+ !ids.every((id) => result.removedUpdateIds.includes(id)))
2953
+ return "unknown";
2954
+ }
2955
+ catch (error) {
2956
+ recordRuntimeEvent(error, {
2957
+ phase: "live-deferred-transfer-settlement",
2958
+ });
2959
+ return "unknown";
2960
+ }
2961
+ for (const id of ids) {
2962
+ claims.delete(id);
2963
+ deferredSources.delete(id);
2964
+ }
2965
+ finish();
2966
+ for (const id of ids) {
2967
+ if (!ownerCurrent() || !canSettle())
2968
+ return "unknown";
2969
+ state.lastCompletedUpdateId = id;
2970
+ state.lastCompletedAtMs = getNowMs();
2971
+ try {
2972
+ deps.onUpdateCompleted?.(id, expectedOwner.ctx, binding);
2973
+ }
2974
+ catch (error) {
2975
+ recordRuntimeEvent(error, {
2976
+ phase: "update-completion-observer",
2977
+ updateId: id,
2978
+ });
2979
+ }
2980
+ }
2981
+ return ownerCurrent() && canSettle() ? "settled" : "unknown";
2982
+ },
2983
+ cancel() {
2984
+ if (!available())
2985
+ return false;
2986
+ finish();
2987
+ return true;
2988
+ },
2989
+ };
2990
+ },
2991
+ prepareLiveInput(ctx, sourceUpdateIds, isCurrent) {
2992
+ const expectedOwner = owner, binding = deps.getJournalBindingKey?.();
2993
+ const ids = [...sourceUpdateIds];
2994
+ const currentOwner = () => !!expectedOwner &&
2995
+ owner === expectedOwner &&
2996
+ expectedOwner.ctx === ctx &&
2997
+ !expectedOwner.controller.signal.aborted &&
2998
+ !!binding &&
2999
+ deps.getJournalBindingKey?.() === binding &&
3000
+ deps.isContextCurrent?.(ctx) !== false &&
3001
+ deps.hasAuthority(ctx) &&
3002
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
3003
+ isCurrent?.() !== false;
3004
+ if (!currentOwner() ||
3005
+ !expectedOwner?.startupUpdateIds ||
3006
+ livePreparation ||
3007
+ deps.executeCustodiedUpdate ||
3008
+ !completeExact ||
3009
+ !ids.length ||
3010
+ new Set(ids).size !== ids.length ||
3011
+ ids.some((id) => !Number.isSafeInteger(id) ||
3012
+ id < 0 ||
3013
+ claims.has(id) ||
3014
+ unsettledExecutionsByUpdateId.has(id) ||
3015
+ (state.phase === "executing" && state.currentUpdateId === id)))
3016
+ return undefined;
3017
+ let before;
3018
+ try {
3019
+ before = deps.journal.read();
3020
+ }
3021
+ catch (error) {
3022
+ recordRuntimeEvent(error, { phase: "live-input-prepare" });
3023
+ return undefined;
3024
+ }
3025
+ if (!currentOwner() ||
3026
+ !isTelegramUpdateJournalLegacyFamilyVersion(before.version) ||
3027
+ before.entries.some((entry) => ids.includes(entry.updateId)))
3028
+ return undefined;
3029
+ const held = {
3030
+ version: before.version,
3031
+ ids: new Set(ids),
3032
+ };
3033
+ let released = false, completionReadiness;
3034
+ livePreparation = held;
3035
+ const current = () => livePreparation === held && !held.released && currentOwner();
3036
+ const readPending = (full = true) => {
3037
+ if (!current())
3038
+ return undefined;
3039
+ try {
3040
+ const snapshot = deps.journal.read();
3041
+ const entries = snapshot.entries.filter((entry) => held.ids.has(entry.updateId));
3042
+ if (!current() ||
3043
+ snapshot.version !== held.version ||
3044
+ (full && entries.length !== ids.length) ||
3045
+ entries.some((entry) => entry.state !== "pending" ||
3046
+ entry.preApprovalExcluded === true ||
3047
+ entry.inputClaim ||
3048
+ entry.routingInput ||
3049
+ entry.failure ||
3050
+ entry.queueOwner ||
3051
+ entry.queueReceiptId ||
3052
+ entry.queueKind ||
3053
+ entry.queueHandoff ||
3054
+ "inputProvenance" in entry ||
3055
+ claims.has(entry.updateId) ||
3056
+ unsettledExecutionsByUpdateId.has(entry.updateId)))
3057
+ return undefined;
3058
+ return entries;
3059
+ }
3060
+ catch (error) {
3061
+ recordRuntimeEvent(error, { phase: "live-input-observe" });
3062
+ return undefined;
3063
+ }
3064
+ };
3065
+ const finish = () => {
3066
+ if (!held.released || !held.status)
3067
+ livePreparation = undefined;
3068
+ updateClaimCounts();
3069
+ notifyStateChange();
3070
+ blocked = false;
3071
+ pendingSignal = true;
3072
+ launchDrain();
3073
+ };
3074
+ updateClaimCounts();
3075
+ notifyStateChange();
3076
+ return {
3077
+ sourceUpdateIds: Object.freeze([...ids]),
3078
+ isCurrent: () => current() && !held.discardIssued,
3079
+ isOwnerCurrent: currentOwner,
3080
+ confirmSaved() {
3081
+ const entries = readPending();
3082
+ if (held.discardIssued ||
3083
+ !entries ||
3084
+ (held.saved && !isDeepStrictEqual(held.saved, entries)))
3085
+ return false;
3086
+ held.saved ??= structuredClone(entries);
3087
+ return true;
3088
+ },
3089
+ prepareSourceCompletion() {
3090
+ if (!current() ||
3091
+ held.discardIssued ||
3092
+ !held.saved ||
3093
+ ids.length !== 1 ||
3094
+ !deps.getQueueOwnerIdentity)
3095
+ return undefined;
3096
+ if (completionReadiness)
3097
+ return completionReadiness.isCurrent()
3098
+ ? completionReadiness
3099
+ : undefined;
3100
+ const entries = readPending(), entry = entries?.[0], journal = deps.journal, read = journal.read, remove = journal.removeCompletedExact;
3101
+ if (!entry ||
3102
+ !isDeepStrictEqual(entries, held.saved) ||
3103
+ typeof remove !== "function" ||
3104
+ !completeExact ||
3105
+ !current())
3106
+ return undefined;
3107
+ const snapshot = {
3108
+ source: {
3109
+ journalBindingKey: binding,
3110
+ ...createTelegramUpdateJournalEntryDigest(entry),
3111
+ },
3112
+ update: structuredClone(entry.update),
3113
+ };
3114
+ const ownerCurrent = () => !held.discardIssued &&
3115
+ currentOwner() &&
3116
+ deps.journal === journal &&
3117
+ journal.read === read &&
3118
+ journal.removeCompletedExact === remove;
3119
+ let retained;
3120
+ completionReadiness = {
3121
+ get snapshot() {
3122
+ return structuredClone(snapshot);
3123
+ },
3124
+ isCurrent: ownerCurrent,
3125
+ bindCarrier(value) {
3126
+ if (!released ||
3127
+ !ownerCurrent() ||
3128
+ getTelegramUpdateExecutionFence(value)?.signal !==
3129
+ expectedOwner.controller.signal)
3130
+ return undefined;
3131
+ if (retained)
3132
+ return value === retained.carrier &&
3133
+ retained.completion.isCurrent()
3134
+ ? retained.completion
3135
+ : undefined;
3136
+ const completion = prepareTelegramDeferredSourceCompletion(value);
3137
+ if (!completion ||
3138
+ !isDeepStrictEqual(completion.source, snapshot.source) ||
3139
+ !ownerCurrent() ||
3140
+ !completion.isCurrent())
3141
+ return undefined;
3142
+ retained = { carrier: value, completion };
3143
+ return completion;
3144
+ },
3145
+ };
3146
+ return ownerCurrent() ? completionReadiness : undefined;
3147
+ },
3148
+ canPrepareStatusConsumption() {
3149
+ return (current() &&
3150
+ !held.discardIssued &&
3151
+ typeof deps.admitPreparedLiveInput === "function" &&
3152
+ !!deps.getQueueOwnerIdentity);
3153
+ },
3154
+ prepareStatusConsumption(input) {
3155
+ const admit = deps.admitPreparedLiveInput, ports = {
3156
+ assertCurrent: input.assertCurrent,
3157
+ bindCarrier: input.bindCarrier,
3158
+ execute: input.execute,
3159
+ };
3160
+ if (!current() ||
3161
+ held.status ||
3162
+ typeof admit !== "function" ||
3163
+ !completionReadiness ||
3164
+ !completionReadiness.isCurrent() ||
3165
+ Object.values(ports).some((port) => typeof port !== "function") ||
3166
+ !isDeepStrictEqual(input.source, completionReadiness.snapshot.source))
3167
+ return false;
3168
+ const readiness = completionReadiness, readyCurrent = readiness.isCurrent;
3169
+ held.status = {
3170
+ current: () => currentOwner() &&
3171
+ !held.discardIssued &&
3172
+ readiness.isCurrent === readyCurrent &&
3173
+ readyCurrent.call(readiness) &&
3174
+ deps.admitPreparedLiveInput === admit &&
3175
+ Object.entries(ports).every(([key, value]) => Reflect.get(input, key) === value),
3176
+ assertCurrent: ports.assertCurrent.bind(input),
3177
+ bindCarrier: ports.bindCarrier.bind(input),
3178
+ execute: ports.execute.bind(input),
3179
+ admit,
3180
+ };
3181
+ return held.status.current();
3182
+ },
3183
+ release(canRelease) {
3184
+ if (!current() ||
3185
+ held.discardIssued ||
3186
+ !held.saved ||
3187
+ !canRelease() ||
3188
+ held.status?.current() === false)
3189
+ return false;
3190
+ const entries = readPending();
3191
+ if (!entries ||
3192
+ !isDeepStrictEqual(entries, held.saved) ||
3193
+ !canRelease() ||
3194
+ !current())
3195
+ return false;
3196
+ released = true;
3197
+ held.released = true;
3198
+ finish();
3199
+ return true;
3200
+ },
3201
+ cancelEmpty() {
3202
+ if (!current() || held.discardIssued || held.saved)
3203
+ return false;
3204
+ try {
3205
+ const snapshot = deps.journal.read();
3206
+ if (!current() ||
3207
+ snapshot.version !== held.version ||
3208
+ snapshot.entries.some((entry) => held.ids.has(entry.updateId)))
3209
+ return false;
3210
+ }
3211
+ catch (error) {
3212
+ recordRuntimeEvent(error, { phase: "live-input-cancel-empty" });
3213
+ return false;
3214
+ }
3215
+ finish();
3216
+ return true;
3217
+ },
3218
+ discardSaved(canDiscard) {
3219
+ if (!current() || !canDiscard() || !completeExact)
3220
+ return "protected";
3221
+ if (held.discardIssued)
3222
+ return "unknown";
3223
+ const entries = readPending(false);
3224
+ if (!entries ||
3225
+ (held.saved && !isDeepStrictEqual(held.saved, entries)) ||
3226
+ !current() ||
3227
+ !canDiscard())
3228
+ return "protected";
3229
+ if (!entries.length) {
3230
+ finish();
3231
+ return "discarded";
3232
+ }
3233
+ const updateIds = entries.map((entry) => entry.updateId);
3234
+ const expected = entries.map((entry) => createTelegramUpdateJournalEntryDigest(entry));
3235
+ held.discardIssued = true;
3236
+ try {
3237
+ const result = completeExact(updateIds, expected, undefined, () => current() && canDiscard());
3238
+ if (!current() ||
3239
+ !canDiscard() ||
3240
+ result.removedUpdateIds.length !== updateIds.length ||
3241
+ !updateIds.every((id) => result.removedUpdateIds.includes(id)))
3242
+ return "unknown";
3243
+ }
3244
+ catch (error) {
3245
+ recordRuntimeEvent(error, { phase: "live-input-discard" });
3246
+ return "unknown";
3247
+ }
3248
+ finish();
3249
+ return "discarded";
3250
+ },
3251
+ };
3252
+ },
2280
3253
  signal() {
2281
3254
  if (!owner || owner.controller.signal.aborted)
2282
3255
  return;
@@ -2284,51 +3257,294 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2284
3257
  pendingSignal = true;
2285
3258
  launchDrain();
2286
3259
  },
2287
- armRoutingInput(input) { return mutateRoutingSource(input, false)?.lifetime; },
2288
- selectRoutingInput(input) { return mutateRoutingSource(input, true)?.issued === true; },
2289
- inspectDeferredSource(input) {
2290
- const expectedOwner = owner;
2291
- const source = deferredSources.get(input.updateId);
2292
- const current = () => {
2293
- if (!expectedOwner || owner !== expectedOwner || input.signal.aborted || expectedOwner.controller.signal !== input.signal ||
2294
- !source || deferredSources.get(input.updateId) !== source || claims.get(input.updateId) !== "deferred" ||
2295
- deps.getJournalBindingKey?.() !== source.journalBindingKey || deps.isContextCurrent?.(expectedOwner.ctx) === false ||
2296
- !deps.hasAuthority(expectedOwner.ctx))
2297
- return false;
2298
- return isQueueOwnerIdentityCurrent(expectedOwner);
3260
+ armRoutingInput(input) {
3261
+ return mutateRoutingSource(input, false)?.lifetime;
3262
+ },
3263
+ selectRoutingInput(input) {
3264
+ return mutateRoutingSource(input, true)?.issued === true;
3265
+ },
3266
+ isRoutingInputCurrent(input) {
3267
+ return (owner?.controller.signal === input.signal &&
3268
+ !input.signal.aborted &&
3269
+ deferredSources.has(input.updateId) &&
3270
+ claims.get(input.updateId) !== "abandoning");
3271
+ },
3272
+ prepareDeferredSourceCompletion(input) {
3273
+ const expectedOwner = owner, binding = deps.getJournalBindingKey?.(), journal = deps.journal, read = journal.read, remove = journal.removeCompletedExact;
3274
+ const current = () => !!expectedOwner &&
3275
+ owner === expectedOwner &&
3276
+ expectedOwner.controller.signal === input.signal &&
3277
+ !input.signal.aborted &&
3278
+ !!binding &&
3279
+ deps.getJournalBindingKey?.() === binding &&
3280
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
3281
+ deps.hasAuthority(expectedOwner.ctx) &&
3282
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
3283
+ deps.journal === journal &&
3284
+ journal.read === read &&
3285
+ journal.removeCompletedExact === remove;
3286
+ if (!deps.getQueueOwnerIdentity ||
3287
+ !expectedOwner?.startupUpdateIds ||
3288
+ typeof remove !== "function" ||
3289
+ !completeExact ||
3290
+ !current() ||
3291
+ deps.executeCustodiedUpdate)
3292
+ return undefined;
3293
+ let original;
3294
+ try {
3295
+ original = inspectDeferredOriginal(input);
3296
+ }
3297
+ catch (error) {
3298
+ recordRuntimeEvent(error, {
3299
+ phase: "source-completion-prepare",
3300
+ updateId: input.updateId,
3301
+ });
3302
+ return undefined;
3303
+ }
3304
+ if (!original ||
3305
+ !current() ||
3306
+ !isFreshLiveSource(expectedOwner, input.updateId) ||
3307
+ original.entry.state !== "pending" ||
3308
+ original.entry.preApprovalExcluded ||
3309
+ original.entry.inputClaim ||
3310
+ original.entry.queueOwner ||
3311
+ original.entry.queueKind ||
3312
+ original.entry.queueReceiptId ||
3313
+ original.entry.queueHandoff ||
3314
+ original.entry.failure ||
3315
+ "inputProvenance" in original.entry)
3316
+ return undefined;
3317
+ const source = { ...original.source };
3318
+ return {
3319
+ get source() {
3320
+ return { ...source };
3321
+ },
3322
+ isCurrent: current,
2299
3323
  };
2300
- if (!current())
3324
+ },
3325
+ prepareDeferredQueueAdmission(input) {
3326
+ const expectedOwner = owner, binding = deps.getJournalBindingKey?.();
3327
+ const read = queueAdmissionRead, commit = queueAdmissionCommit, inspect = queueAdmissionInspect;
3328
+ const current = () => !!expectedOwner &&
3329
+ owner === expectedOwner &&
3330
+ expectedOwner.controller.signal === input.signal &&
3331
+ !input.signal.aborted &&
3332
+ !!binding &&
3333
+ deps.getJournalBindingKey?.() === binding &&
3334
+ deps.hasAuthority(expectedOwner.ctx) &&
3335
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
3336
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
3337
+ deps.journal === queueAdmissionJournal &&
3338
+ deps.journal.read === read &&
3339
+ deps.journal.markQueued === commit &&
3340
+ deps.journal.isQueueReceiptCurrent === inspect;
3341
+ if (!deps.getQueueOwnerIdentity ||
3342
+ typeof commit !== "function" ||
3343
+ !inspect ||
3344
+ !current())
2301
3345
  return undefined;
2302
- const snapshot = deps.journal.read();
2303
- const entry = snapshot.entries.find(value => value.updateId === input.updateId);
2304
- if (!isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) || !entry ||
2305
- JSON.stringify(entry) !== JSON.stringify(source.entry) || !current())
3346
+ let original;
3347
+ try {
3348
+ original = inspectDeferredOriginal(input);
3349
+ }
3350
+ catch (error) {
3351
+ recordRuntimeEvent(error, {
3352
+ phase: "queue-admission-prepare",
3353
+ updateId: input.updateId,
3354
+ });
2306
3355
  return undefined;
2307
- return { journalBindingKey: source.journalBindingKey, ...createTelegramUpdateJournalEntryDigest(entry) };
3356
+ }
3357
+ if (!original ||
3358
+ !current() ||
3359
+ !isFreshLiveSource(expectedOwner, input.updateId) ||
3360
+ original.entry.state !== "pending" ||
3361
+ original.entry.queueOwner ||
3362
+ original.entry.queueReceiptId ||
3363
+ original.entry.preApprovalExcluded)
3364
+ return undefined;
3365
+ const source = { ...original.source }, update = structuredClone(original.entry.update);
3366
+ const complete = queueAdmissionJournal.completeQueued, completeScoped = queueAdmissionJournal.completeQueuedExact;
3367
+ const readCompletion = queueAdmissionJournal.inspectSourceCompletion;
3368
+ const completionCurrent = () => current() &&
3369
+ queueAdmissionJournal.completeQueued === complete &&
3370
+ completeScoped === queueCompletionPort &&
3371
+ queueAdmissionJournal.completeQueuedExact === completeScoped &&
3372
+ readCompletion === completionReaderPort &&
3373
+ queueAdmissionJournal.inspectSourceCompletion === readCompletion;
3374
+ const inspectedReceipts = new WeakMap();
3375
+ return {
3376
+ isCurrent: current,
3377
+ inspectReceipt(receipt) {
3378
+ if (!current() ||
3379
+ receipt.journalBindingKey !== binding ||
3380
+ receipt.queueKind !== "prompt" ||
3381
+ !isDeepStrictEqual(receipt.sourceUpdateIds, [input.updateId]))
3382
+ return undefined;
3383
+ try {
3384
+ // The warm owner requires its actual publication ACK, not merely queued bytes or the report hint.
3385
+ const committed = getCurrentQueueReceipt(receipt);
3386
+ if (!committed ||
3387
+ !current() ||
3388
+ !isTelegramUpdateJournalQueueOwnerProcess(committed.queueOwner, expectedOwner.queueOwnerIdentity))
3389
+ return undefined;
3390
+ const entry = findExactQueuedPromptEntry(read.call(queueAdmissionJournal), receipt.receiptId, input.updateId, committed.queueOwner, update);
3391
+ if (!entry ||
3392
+ !current() ||
3393
+ !getCurrentQueueReceipt(receipt) ||
3394
+ !current())
3395
+ return undefined;
3396
+ const scope = committed.sourceCompletions?.find((scope) => scope.updateId === input.updateId);
3397
+ if (scope?.sourceSha256 ===
3398
+ createTelegramUpdateJournalEntryDigest(entry).sourceSha256) {
3399
+ inspectedReceipts.set(receipt, {
3400
+ receipt: normalizeQueueReceipt(committed.receipt),
3401
+ completion: {
3402
+ updateId: scope.updateId,
3403
+ sourceSha256: scope.sourceSha256,
3404
+ completionSha256: scope.completionSha256,
3405
+ },
3406
+ });
3407
+ }
3408
+ return {
3409
+ source: { ...source },
3410
+ receipt: {
3411
+ queueKind: "prompt",
3412
+ receiptId: committed.receipt.receiptId,
3413
+ sourceUpdateIds: [...committed.receipt.sourceUpdateIds],
3414
+ queueOwner: { ...committed.queueOwner },
3415
+ },
3416
+ };
3417
+ }
3418
+ catch (error) {
3419
+ recordRuntimeEvent(error, {
3420
+ phase: "queue-admission-observe",
3421
+ updateId: input.updateId,
3422
+ });
3423
+ return undefined;
3424
+ }
3425
+ },
3426
+ prepareCompletionScope(receipt, queueOwner, completionSha256) {
3427
+ if (!completionCurrent() ||
3428
+ !queueCompletionPort ||
3429
+ !completionReaderPort ||
3430
+ !/^[a-f0-9]{64}$/u.test(completionSha256) ||
3431
+ receipt.journalBindingKey !== binding ||
3432
+ receipt.queueKind !== "prompt" ||
3433
+ !isDeepStrictEqual(receipt.sourceUpdateIds, [input.updateId]) ||
3434
+ !isTelegramUpdateJournalQueueOwnerProcess(queueOwner, expectedOwner.queueOwnerIdentity))
3435
+ return undefined;
3436
+ try {
3437
+ if (!current())
3438
+ return undefined;
3439
+ const entry = findExactQueuedPromptEntry(read.call(queueAdmissionJournal), receipt.receiptId, input.updateId, queueOwner, update);
3440
+ if (!entry ||
3441
+ inspect.call(queueAdmissionJournal, normalizeQueueReceipt(receipt), { ...queueOwner }) !== true ||
3442
+ !completionCurrent())
3443
+ return undefined;
3444
+ const completion = {
3445
+ ...createTelegramUpdateJournalEntryDigest(entry),
3446
+ completionSha256,
3447
+ };
3448
+ inspectedReceipts.set(receipt, {
3449
+ receipt: normalizeQueueReceipt(receipt),
3450
+ completion: { ...completion },
3451
+ });
3452
+ return { ...completion, journalBindingKey: binding };
3453
+ }
3454
+ catch (error) {
3455
+ recordRuntimeEvent(error, {
3456
+ phase: "queue-completion-scope",
3457
+ updateId: input.updateId,
3458
+ });
3459
+ return undefined;
3460
+ }
3461
+ },
3462
+ inspectCompletion(receipt) {
3463
+ const inspected = inspectedReceipts.get(receipt);
3464
+ // The native ACK belongs to this exact object and whole receipt, not an equivalent reconstructed query.
3465
+ if (!completionCurrent() ||
3466
+ !readCompletion ||
3467
+ !inspected ||
3468
+ !areTelegramQueueAdmissionReceiptsEqual(inspected.receipt, receipt) ||
3469
+ !isQueueReceiptCompletionAcknowledged(receipt) ||
3470
+ !completionCurrent())
3471
+ return undefined;
3472
+ try {
3473
+ const acknowledged = readCompletion.call(queueAdmissionJournal, {
3474
+ ...inspected.completion,
3475
+ });
3476
+ return completionCurrent() &&
3477
+ isDeepStrictEqual(acknowledged, inspected.completion)
3478
+ ? { ...source }
3479
+ : undefined;
3480
+ }
3481
+ catch (error) {
3482
+ recordRuntimeEvent(error, {
3483
+ phase: "queue-completion-observe",
3484
+ updateId: input.updateId,
3485
+ });
3486
+ return undefined;
3487
+ }
3488
+ },
3489
+ };
3490
+ },
3491
+ inspectDeferredSource(input) {
3492
+ return inspectDeferredOriginal(input)?.source;
3493
+ },
3494
+ inspectDeferredSourceSnapshot(input) {
3495
+ const original = inspectDeferredOriginal(input);
3496
+ return (original && {
3497
+ source: original.source,
3498
+ update: structuredClone(original.entry.update),
3499
+ });
2308
3500
  },
2309
3501
  isHistoricalSource(input) {
2310
3502
  const source = deferredSources.get(input.updateId);
2311
- const isCurrent = () => owner?.controller.signal === input.signal && !input.signal.aborted &&
2312
- source?.historical === true && deferredSources.get(input.updateId) === source;
3503
+ const isCurrent = () => owner?.controller.signal === input.signal &&
3504
+ !input.signal.aborted &&
3505
+ source?.historical === true &&
3506
+ deferredSources.get(input.updateId) === source;
2313
3507
  if (!isCurrent())
2314
3508
  return false;
2315
3509
  // Domain eligibility observes original evidence, never mutable handler projections.
2316
- return (!input.matchesOriginal || input.matchesOriginal(structuredClone(source.entry)) === true) && isCurrent();
3510
+ return ((!input.matchesOriginal ||
3511
+ input.matchesOriginal(structuredClone(source.entry)) === true) &&
3512
+ isCurrent());
3513
+ },
3514
+ getRoutingInput(input) {
3515
+ const lifetime = deferredSources.get(input.updateId)?.entry.routingInput;
3516
+ return owner?.controller.signal === input.signal &&
3517
+ !input.signal.aborted &&
3518
+ lifetime
3519
+ ? structuredClone(lifetime)
3520
+ : undefined;
2317
3521
  },
2318
3522
  supportsDeferredAbandonment(input) {
2319
3523
  const expectedOwner = owner;
2320
3524
  const source = deferredSources.get(input.updateId);
2321
- if (!expectedOwner || input.signal.aborted || expectedOwner.controller.signal !== input.signal ||
2322
- !source || source.historical || input.journalBindingKey !== source.journalBindingKey ||
2323
- !deps.journal.abandonPending || !deps.journal.inspectPendingRetention ||
3525
+ if (!expectedOwner ||
3526
+ input.signal.aborted ||
3527
+ expectedOwner.controller.signal !== input.signal ||
3528
+ !source ||
3529
+ source.historical ||
3530
+ input.journalBindingKey !== source.journalBindingKey ||
3531
+ !deps.journal.abandonPending ||
3532
+ !deps.journal.inspectPendingRetention ||
2324
3533
  claims.get(input.updateId) === "queued")
2325
3534
  return false;
2326
- return deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2327
- deps.getJournalBindingKey?.() === source.journalBindingKey && deps.hasAuthority(expectedOwner.ctx);
3535
+ return (deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
3536
+ deps.getJournalBindingKey?.() === source.journalBindingKey &&
3537
+ deps.hasAuthority(expectedOwner.ctx));
3538
+ },
3539
+ abandonDeferred(input) {
3540
+ return abandonSource(input);
3541
+ },
3542
+ inspectAbandoning(input) {
3543
+ return inspectRecovery(input, "abandoning");
3544
+ },
3545
+ inspectHistorical(input) {
3546
+ return inspectRecovery(input, "historical");
2328
3547
  },
2329
- abandonDeferred(input) { return abandonSource(input); },
2330
- inspectAbandoning(input) { return inspectRecovery(input, "abandoning"); },
2331
- inspectHistorical(input) { return inspectRecovery(input, "historical"); },
2332
3548
  async settleDeferred(input) {
2333
3549
  const expectedOwner = owner;
2334
3550
  if (!expectedOwner ||
@@ -2346,7 +3562,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2346
3562
  return;
2347
3563
  }
2348
3564
  const claim = claims.get(input.updateId);
2349
- if (claim === "abandoning" || claim === "historical" || claim === "retained")
3565
+ if (livePreparation?.ids.has(input.updateId))
3566
+ return;
3567
+ if (claim === "abandoning" ||
3568
+ claim === "historical" ||
3569
+ claim === "retained" ||
3570
+ (!claim && routingJournal?.inspectExpiry(input.updateId)))
2350
3571
  return;
2351
3572
  if (input.outcome.kind === "complete") {
2352
3573
  // Expiry may retire only a still-deferred source, never accepted queue work.
@@ -2362,10 +3583,27 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2362
3583
  }
2363
3584
  if (completion.kind !== "complete")
2364
3585
  return;
2365
- const result = commitCompletedBatch(expectedOwner, [{ updateId: input.updateId,
2366
- ...(completion.expectedSource ? { expectedSource: completion.expectedSource } : {}) }]);
2367
- if (!result)
3586
+ const source = completion.expectedSource && {
3587
+ ...completion.expectedSource,
3588
+ };
3589
+ const journalBindingKey = deps.getJournalBindingKey?.();
3590
+ const isCurrent = () => owner === expectedOwner &&
3591
+ !input.signal.aborted &&
3592
+ deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
3593
+ deps.hasAuthority(expectedOwner.ctx) &&
3594
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
3595
+ deps.getJournalBindingKey?.() === journalBindingKey;
3596
+ const result = commitCompletedBatch(expectedOwner, [
3597
+ {
3598
+ updateId: input.updateId,
3599
+ ...(source ? { expectedSource: source } : {}),
3600
+ },
3601
+ ], source ? isCurrent : undefined);
3602
+ if (!result) {
2368
3603
  transition("idle", input.updateId);
3604
+ if (source && isCurrent())
3605
+ return { source, isCurrent };
3606
+ }
2369
3607
  return;
2370
3608
  }
2371
3609
  if (input.outcome.kind === "deferred") {
@@ -2380,7 +3618,14 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2380
3618
  }
2381
3619
  let outcome;
2382
3620
  try {
2383
- outcome = validateTelegramUpdateAdmissionOutcome(input.outcome, input.updateId, new Set([input.updateId, ...[...claims].filter(([, claim]) => claim !== "abandoning" && claim !== "historical" && claim !== "retained").map(([id]) => id)]));
3621
+ outcome = validateTelegramUpdateAdmissionOutcome(input.outcome, input.updateId, new Set([
3622
+ input.updateId,
3623
+ ...[...claims]
3624
+ .filter(([, claim]) => claim !== "abandoning" &&
3625
+ claim !== "historical" &&
3626
+ claim !== "retained")
3627
+ .map(([id]) => id),
3628
+ ]));
2384
3629
  }
2385
3630
  catch (error) {
2386
3631
  blockWithFailure("invalid-outcome", "late-invalid-outcome", error, input.updateId);
@@ -2408,7 +3653,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2408
3653
  },
2409
3654
  async settleCustodied(input) {
2410
3655
  const expectedOwner = owner;
2411
- if (!expectedOwner || expectedOwner.controller.signal !== input.signal || input.signal.aborted)
3656
+ if (!expectedOwner ||
3657
+ expectedOwner.controller.signal !== input.signal ||
3658
+ input.signal.aborted)
2412
3659
  return;
2413
3660
  if (checkAuthority(expectedOwner, input.updateId))
2414
3661
  return;
@@ -2432,7 +3679,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2432
3679
  }
2433
3680
  const receipt = normalizeQueueReceipt(input.result.queueReceipt);
2434
3681
  const existing = committedQueueReceipts.get(receipt.receiptId);
2435
- if (existing && areTelegramQueueAdmissionReceiptsEqual(existing.receipt, receipt) &&
3682
+ if (existing &&
3683
+ areTelegramQueueAdmissionReceiptsEqual(existing.receipt, receipt) &&
2436
3684
  areTelegramUpdateJournalQueueOwnersEqual(existing.queueOwner, input.result.queueReceipt.queueOwner))
2437
3685
  return;
2438
3686
  if (existing) {
@@ -2440,7 +3688,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2440
3688
  return;
2441
3689
  }
2442
3690
  if (!receipt.sourceUpdateIds.includes(input.updateId) ||
2443
- receipt.sourceUpdateIds.some(updateId => claims.get(updateId) !== "deferred")) {
3691
+ receipt.sourceUpdateIds.some((updateId) => claims.get(updateId) !== "deferred")) {
2444
3692
  blockWithFailure("invalid-outcome", "late-custody-unclaimed", new TelegramUpdateAdmissionOutcomeError(`Telegram update ${input.updateId} reported custodied queue without exact deferred claims.`), input.updateId);
2445
3693
  return;
2446
3694
  }
@@ -2459,21 +3707,29 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2459
3707
  }
2460
3708
  transition("queued", input.updateId);
2461
3709
  },
2462
- isQueueReceiptCommitted: receipt => getCurrentQueueReceipt(receipt) !== undefined,
3710
+ isQueueReceiptCommitted: (receipt) => getCurrentQueueReceipt(receipt) !== undefined,
2463
3711
  getQueueReceiptOwner(receipt) {
2464
3712
  const committed = getCurrentQueueReceipt(receipt);
2465
3713
  return committed ? { ...committed.queueOwner } : undefined;
2466
3714
  },
2467
3715
  getQueueReceiptSettlementOwner(receipt, ctx, reason) {
2468
3716
  const expectedOwner = owner, committed = committedQueueReceipts.get(receipt.receiptId);
2469
- if (!expectedOwner || expectedOwner.controller.signal.aborted ||
2470
- !(deps.isContextCurrent?.(ctx) ?? expectedOwner.ctx === ctx) || !isQueueOwnerIdentityCurrent(expectedOwner) ||
2471
- !committed || !areTelegramQueueAdmissionReceiptsEqual(committed.receipt, normalizeQueueReceipt(receipt)) ||
2472
- (deps.getJournalBindingKey && committed.receipt.journalBindingKey !== deps.getJournalBindingKey()) ||
3717
+ if (!expectedOwner ||
3718
+ expectedOwner.controller.signal.aborted ||
3719
+ !(deps.isContextCurrent?.(ctx) ?? expectedOwner.ctx === ctx) ||
3720
+ !isQueueOwnerIdentityCurrent(expectedOwner) ||
3721
+ !committed ||
3722
+ !areTelegramQueueAdmissionReceiptsEqual(committed.receipt, normalizeQueueReceipt(receipt)) ||
3723
+ (deps.getJournalBindingKey &&
3724
+ committed.receipt.journalBindingKey !==
3725
+ deps.getJournalBindingKey()) ||
2473
3726
  !isTelegramUpdateJournalQueueOwnerProcess(committed.queueOwner, expectedOwner.queueOwnerIdentity))
2474
3727
  return undefined;
2475
3728
  const attempt = scopedQueueCompletionAttempts.get(receipt.receiptId);
2476
- if (attempt ? attempt.owner !== expectedOwner || attempt.reason !== reason || attempt.journalBindingKey !== deps.getJournalBindingKey?.()
3729
+ if (attempt
3730
+ ? attempt.owner !== expectedOwner ||
3731
+ attempt.reason !== reason ||
3732
+ attempt.journalBindingKey !== deps.getJournalBindingKey?.()
2477
3733
  : !getCurrentQueueReceipt(receipt))
2478
3734
  return undefined;
2479
3735
  return { ...committed.queueOwner };
@@ -2486,7 +3742,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2486
3742
  return false;
2487
3743
  }
2488
3744
  // An ACK permits only local completion reconciliation, never execution readiness.
2489
- const receipts = input.sourceCompletions === undefined ? input.receipts.filter(receipt => !isQueueReceiptCompletionAcknowledged(receipt)) : input.receipts;
3745
+ const receipts = input.sourceCompletions === undefined
3746
+ ? input.receipts.filter((receipt) => !isQueueReceiptCompletionAcknowledged(receipt))
3747
+ : input.receipts;
2490
3748
  if (receipts.length === 0)
2491
3749
  return input.sourceCompletions === undefined;
2492
3750
  const normalizedReceipts = receipts.map(normalizeQueueReceipt);
@@ -2514,83 +3772,138 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2514
3772
  }
2515
3773
  }
2516
3774
  const completionBinding = deps.getJournalBindingKey?.();
2517
- const ordinaryObserverBound = normalizedReceipts.every(receipt => committedQueueReceipts.get(receipt.receiptId).receipt.journalBindingKey === completionBinding);
2518
- const ordinaryCompletionCurrent = () => ordinaryObserverBound && owner === expectedOwner &&
2519
- !expectedOwner.controller.signal.aborted && (deps.isContextCurrent?.(input.ctx) ?? expectedOwner.ctx === input.ctx) &&
2520
- isQueueOwnerIdentityCurrent(expectedOwner) && deps.getJournalBindingKey?.() === completionBinding;
3775
+ const ordinaryObserverBound = normalizedReceipts.every((receipt) => committedQueueReceipts.get(receipt.receiptId).receipt
3776
+ .journalBindingKey === completionBinding);
3777
+ const ordinaryCompletionCurrent = () => ordinaryObserverBound &&
3778
+ owner === expectedOwner &&
3779
+ !expectedOwner.controller.signal.aborted &&
3780
+ (deps.isContextCurrent?.(input.ctx) ??
3781
+ expectedOwner.ctx === input.ctx) &&
3782
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
3783
+ deps.getJournalBindingKey?.() === completionBinding;
2521
3784
  let removedUpdateIds;
2522
3785
  let scopedCompletionCurrent;
2523
3786
  try {
2524
- const prepared = normalizedReceipts.flatMap(receipt => committedQueueReceipts.get(receipt.receiptId).sourceCompletions ?? []);
2525
- const sourceCompletions = input.sourceCompletions === undefined ? (prepared.length ? prepared : undefined) : input.sourceCompletions;
2526
- if (input.sourceCompletions !== undefined && prepared.some(expected => !input.sourceCompletions.some(value => isDeepStrictEqual(value, expected)))) {
3787
+ const prepared = normalizedReceipts.flatMap((receipt) => committedQueueReceipts.get(receipt.receiptId).sourceCompletions ??
3788
+ []);
3789
+ const sourceCompletions = input.sourceCompletions === undefined
3790
+ ? prepared.length
3791
+ ? prepared
3792
+ : undefined
3793
+ : input.sourceCompletions;
3794
+ if (input.sourceCompletions !== undefined &&
3795
+ prepared.some((expected) => !input.sourceCompletions.some((value) => isDeepStrictEqual(value, expected)))) {
2527
3796
  throw new Error("Telegram queued completion conflicts with its prepared source scopes.");
2528
3797
  }
2529
- if (input.sourceCompletions === undefined && prepared.length && receipts.some(receipt => !requiredScopedQueueReceipts.has(receipt.receiptId))) {
2530
- const scoped = receipts.filter(receipt => requiredScopedQueueReceipts.has(receipt.receiptId));
2531
- const ordinary = receipts.filter(receipt => !requiredScopedQueueReceipts.has(receipt.receiptId));
3798
+ if (input.sourceCompletions === undefined &&
3799
+ prepared.length &&
3800
+ receipts.some((receipt) => !requiredScopedQueueReceipts.has(receipt.receiptId))) {
3801
+ const scoped = receipts.filter((receipt) => requiredScopedQueueReceipts.has(receipt.receiptId));
3802
+ const ordinary = receipts.filter((receipt) => !requiredScopedQueueReceipts.has(receipt.receiptId));
2532
3803
  if (!scoped.length || !ordinary.length)
2533
3804
  throw new Error("Telegram mixed queue completion has incomplete scoped authority.");
2534
3805
  const binding = deps.getJournalBindingKey?.();
2535
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted &&
2536
- (deps.isContextCurrent?.(input.ctx) ?? expectedOwner.ctx === input.ctx) && isQueueOwnerIdentityCurrent(expectedOwner) &&
2537
- !!binding && deps.getJournalBindingKey?.() === binding;
3806
+ const current = () => owner === expectedOwner &&
3807
+ !expectedOwner.controller.signal.aborted &&
3808
+ (deps.isContextCurrent?.(input.ctx) ??
3809
+ expectedOwner.ctx === input.ctx) &&
3810
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
3811
+ !!binding &&
3812
+ deps.getJournalBindingKey?.() === binding;
2538
3813
  // Independent whole receipts need independent ACKs. Ordinary siblings must not acquire invented scopes.
2539
- if (!current() || !completeQueueReceipts({ receipts: scoped, ctx: input.ctx, reason: input.reason }) || !current())
3814
+ if (!current() ||
3815
+ !completeQueueReceipts({
3816
+ receipts: scoped,
3817
+ ctx: input.ctx,
3818
+ reason: input.reason,
3819
+ }) ||
3820
+ !current())
2540
3821
  return false;
2541
- return completeQueueReceipts({ receipts: ordinary, ctx: input.ctx, reason: input.reason });
3822
+ return completeQueueReceipts({
3823
+ receipts: ordinary,
3824
+ ctx: input.ctx,
3825
+ reason: input.reason,
3826
+ });
2542
3827
  }
2543
3828
  if (sourceCompletions === undefined) {
2544
- if (queuedCompletions.some(receipt => requiredScopedQueueReceipts.has(receipt.receiptId))) {
3829
+ if (queuedCompletions.some((receipt) => requiredScopedQueueReceipts.has(receipt.receiptId))) {
2545
3830
  throw new Error("Telegram scoped queue completion cannot downgrade required source scopes.");
2546
3831
  }
2547
- removedUpdateIds = deps.journal.completeQueued(queuedCompletions).removedUpdateIds;
3832
+ removedUpdateIds =
3833
+ deps.journal.completeQueued(queuedCompletions).removedUpdateIds;
2548
3834
  }
2549
3835
  else {
2550
3836
  for (const receipt of queuedCompletions)
2551
3837
  requiredScopedQueueReceipts.add(receipt.receiptId);
2552
3838
  queuedCompletions.sort((a, b) => a.receiptId < b.receiptId ? -1 : a.receiptId > b.receiptId ? 1 : 0);
2553
3839
  const journalBindingKey = deps.getJournalBindingKey?.();
2554
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted &&
2555
- (deps.isContextCurrent?.(input.ctx) ?? expectedOwner.ctx === input.ctx) && isQueueOwnerIdentityCurrent(expectedOwner) &&
3840
+ const current = () => owner === expectedOwner &&
3841
+ !expectedOwner.controller.signal.aborted &&
3842
+ (deps.isContextCurrent?.(input.ctx) ??
3843
+ expectedOwner.ctx === input.ctx) &&
3844
+ isQueueOwnerIdentityCurrent(expectedOwner) &&
2556
3845
  deps.getJournalBindingKey?.() === journalBindingKey;
2557
- if (!journalBindingKey || !current() || !completeQueuedExact || !inspectCompletion ||
2558
- !Array.isArray(sourceCompletions) || input.sourceCompletions !== undefined && sourceCompletions.length !== sourceUpdateIds.size) {
3846
+ if (!journalBindingKey ||
3847
+ !current() ||
3848
+ !completeQueuedExact ||
3849
+ !inspectCompletion ||
3850
+ !Array.isArray(sourceCompletions) ||
3851
+ (input.sourceCompletions !== undefined &&
3852
+ sourceCompletions.length !== sourceUpdateIds.size)) {
2559
3853
  throw new Error("Telegram scoped queue completion requires full current source authority.");
2560
3854
  }
2561
3855
  scopedCompletionCurrent = current;
2562
- const completions = normalizeTelegramQueueSourceCompletions(sourceCompletions, sourceUpdateIds, journalBindingKey, input.sourceCompletions !== undefined)
2563
- .map(({ journalBindingKey: _binding, ...completion }) => completion);
2564
- if (queuedCompletions.some(receipt => !completions.some(scope => receipt.sourceUpdateIds.includes(scope.updateId)))) {
3856
+ const completions = normalizeTelegramQueueSourceCompletions(sourceCompletions, sourceUpdateIds, journalBindingKey, input.sourceCompletions !== undefined).map(({ journalBindingKey: _binding, ...completion }) => completion);
3857
+ if (queuedCompletions.some((receipt) => !completions.some((scope) => receipt.sourceUpdateIds.includes(scope.updateId)))) {
2565
3858
  throw new Error("Telegram scoped queue completion requires a proven origin for every whole receipt.");
2566
3859
  }
2567
- const previous = queuedCompletions.map(receipt => scopedQueueCompletionAttempts.get(receipt.receiptId));
2568
- const retained = previous.find(attempt => attempt !== undefined);
2569
- if (retained && (previous.some(attempt => attempt !== retained) || retained.owner !== expectedOwner ||
2570
- retained.journalBindingKey !== journalBindingKey || retained.reason !== input.reason ||
2571
- !isDeepStrictEqual(retained.receipts, queuedCompletions) || !isDeepStrictEqual(retained.completions, completions))) {
3860
+ const previous = queuedCompletions.map((receipt) => scopedQueueCompletionAttempts.get(receipt.receiptId));
3861
+ const retained = previous.find((attempt) => attempt !== undefined);
3862
+ if (retained &&
3863
+ (previous.some((attempt) => attempt !== retained) ||
3864
+ retained.owner !== expectedOwner ||
3865
+ retained.journalBindingKey !== journalBindingKey ||
3866
+ retained.reason !== input.reason ||
3867
+ !isDeepStrictEqual(retained.receipts, queuedCompletions) ||
3868
+ !isDeepStrictEqual(retained.completions, completions))) {
2572
3869
  throw new Error("Telegram scoped queue completion has conflicting issued authority.");
2573
3870
  }
2574
3871
  if (!retained) {
2575
- const attempt = { owner: expectedOwner, journalBindingKey, reason: input.reason,
2576
- receipts: queuedCompletions.map(receipt => ({ ...receipt, sourceUpdateIds: [...receipt.sourceUpdateIds], queueOwner: { ...receipt.queueOwner } })),
2577
- completions: completions.map(completion => ({ ...completion })) };
3872
+ const attempt = {
3873
+ owner: expectedOwner,
3874
+ journalBindingKey,
3875
+ reason: input.reason,
3876
+ receipts: queuedCompletions.map((receipt) => ({
3877
+ ...receipt,
3878
+ sourceUpdateIds: [...receipt.sourceUpdateIds],
3879
+ queueOwner: { ...receipt.queueOwner },
3880
+ })),
3881
+ completions: completions.map((completion) => ({ ...completion })),
3882
+ };
2578
3883
  if (!current())
2579
3884
  throw new Error("Telegram scoped queue completion authority changed before disposal.");
2580
3885
  // Mark before issuance: an exception may follow a committed rename, and cannot license another disposal.
2581
3886
  for (const receipt of queuedCompletions)
2582
3887
  scopedQueueCompletionAttempts.set(receipt.receiptId, attempt);
2583
- const result = completeQueuedExact(queuedCompletions.map(receipt => ({ ...receipt, sourceUpdateIds: [...receipt.sourceUpdateIds], queueOwner: { ...receipt.queueOwner } })), completions.map(completion => ({ ...completion })));
2584
- if (!current() || !isDeepStrictEqual(result.sourceCompletions, completions)) {
3888
+ const result = completeQueuedExact(queuedCompletions.map((receipt) => ({
3889
+ ...receipt,
3890
+ sourceUpdateIds: [...receipt.sourceUpdateIds],
3891
+ queueOwner: { ...receipt.queueOwner },
3892
+ })), completions.map((completion) => ({ ...completion })));
3893
+ if (!current() ||
3894
+ !isDeepStrictEqual(result.sourceCompletions, completions)) {
2585
3895
  throw new Error("Telegram scoped queue completion ACK was not confirmed.");
2586
3896
  }
2587
3897
  const removed = new Set(result.removedUpdateIds);
2588
- if (removed.size !== sourceUpdateIds.size || [...sourceUpdateIds].some(id => !removed.has(id))) {
3898
+ if (removed.size !== sourceUpdateIds.size ||
3899
+ [...sourceUpdateIds].some((id) => !removed.has(id))) {
2589
3900
  throw new Error("Telegram scoped queue completion did not remove every source.");
2590
3901
  }
2591
3902
  }
2592
3903
  for (const completion of completions) {
2593
- if (!current() || !isDeepStrictEqual(inspectCompletion({ ...completion }), completion) || !current()) {
3904
+ if (!current() ||
3905
+ !isDeepStrictEqual(inspectCompletion({ ...completion }), completion) ||
3906
+ !current()) {
2594
3907
  throw new Error("Telegram scoped queue completion ACK was not retained under current authority.");
2595
3908
  }
2596
3909
  }
@@ -2633,7 +3946,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2633
3946
  observeQueuedCompletion?.(normalizeQueueReceipt(receipt), input.ctx);
2634
3947
  }
2635
3948
  catch (error) {
2636
- recordRuntimeEvent(error, { phase: "queue-receipt-completion-observer", receiptId: receipt.receiptId });
3949
+ recordRuntimeEvent(error, {
3950
+ phase: "queue-receipt-completion-observer",
3951
+ receiptId: receipt.receiptId,
3952
+ });
2637
3953
  }
2638
3954
  }
2639
3955
  // Ordinary queued disposal is source completion too; notify only after a whole positive ACK and fresh origin authority.
@@ -2645,7 +3961,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2645
3961
  deps.onUpdateCompleted?.(updateId, input.ctx, completionBinding);
2646
3962
  }
2647
3963
  catch (error) {
2648
- recordRuntimeEvent(error, { phase: "queue-source-completion-observer", updateId });
3964
+ recordRuntimeEvent(error, {
3965
+ phase: "queue-source-completion-observer",
3966
+ updateId,
3967
+ });
2649
3968
  }
2650
3969
  }
2651
3970
  if (!scopedCompletionCurrent || scopedCompletionCurrent()) {
@@ -2666,6 +3985,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2666
3985
  if (owner !== expectedOwner)
2667
3986
  return;
2668
3987
  owner = undefined;
3988
+ livePreparation = undefined;
3989
+ delete state.preparedInputCount;
2669
3990
  drainPromise = undefined;
2670
3991
  claims.clear();
2671
3992
  deferredSources.clear();
@@ -2675,30 +3996,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2675
3996
  state.phaseStartedAtMs = getNowMs();
2676
3997
  state.currentUpdateId = undefined;
2677
3998
  state.blockedReason = undefined;
2678
- state.deferredClaimCount = 0;
2679
- delete state.abandoningClaimCount;
2680
- delete state.historicalClaimCount;
2681
- state.queuedClaimCount = 0;
2682
- state.foreignQueuedCount = 0;
2683
- delete state.foreignQueuedOwner;
2684
- state.retryWaitCount = 0;
2685
- state.failedCount = 0;
2686
- state.nextRetryUpdateId = undefined;
2687
- state.nextRetryAtMs = undefined;
2688
- state.nextRetryAttemptCount = undefined;
2689
- state.nextRetryFailureClass = undefined;
2690
- state.failedUpdateId = undefined;
2691
- state.failedFailureId = undefined;
2692
- state.failedAttemptCount = undefined;
2693
- state.failedClass = undefined;
2694
- state.failedSummary = undefined;
2695
- state.terminalFailureAtMs = undefined;
2696
- state.unsettledExecutionCount = unsettledExecutions.size;
3999
+ clearTelegramUpdateWorkerClaimState(state, unsettledExecutions.size);
2697
4000
  notifyStateChange();
2698
4001
  },
2699
4002
  async waitForDrain() {
2700
4003
  await (drainPromise ?? Promise.resolve());
2701
- await Promise.allSettled([...pendingQueuePublications.values()].map(value => value.task));
4004
+ await Promise.allSettled([...pendingQueuePublications.values()].map((value) => value.task));
2702
4005
  },
2703
4006
  getState() {
2704
4007
  return getTelegramUpdateWorkerStateSnapshot(state);
@@ -2791,19 +4094,6 @@ function getOrCreateUpdateHandlerRegistry() {
2791
4094
  export function getTelegramUpdateHandlerRegistry() {
2792
4095
  return getOrCreateUpdateHandlerRegistry();
2793
4096
  }
2794
- /**
2795
- * Wrap a default polling `handleUpdate` with the public update handler registry.
2796
- */
2797
- export function createTelegramUpdateHandle(deps) {
2798
- const registry = deps.registry ?? getOrCreateUpdateHandlerRegistry();
2799
- const { defaultHandle } = deps;
2800
- return async (update, ctx) => {
2801
- const verdict = await registry.dispatch(update);
2802
- if (verdict === "consume")
2803
- return;
2804
- await defaultHandle(update, ctx);
2805
- };
2806
- }
2807
4097
  function mergeTelegramReportedAdmissionOutcome(current, next, updateId) {
2808
4098
  if (!current || current.kind === "deferred")
2809
4099
  return next;
@@ -2815,16 +4105,20 @@ function mergeTelegramReportedAdmissionOutcome(current, next, updateId) {
2815
4105
  if (!next.expectedSource)
2816
4106
  return current;
2817
4107
  if (current.expectedSource.updateId === next.expectedSource.updateId &&
2818
- current.expectedSource.journalBindingKey === next.expectedSource.journalBindingKey &&
4108
+ current.expectedSource.journalBindingKey ===
4109
+ next.expectedSource.journalBindingKey &&
2819
4110
  current.expectedSource.sourceSha256 === next.expectedSource.sourceSha256) {
2820
4111
  if (!current.expectedSource.completionSha256)
2821
4112
  return next;
2822
- if (!next.expectedSource.completionSha256 || current.expectedSource.completionSha256 === next.expectedSource.completionSha256)
4113
+ if (!next.expectedSource.completionSha256 ||
4114
+ current.expectedSource.completionSha256 ===
4115
+ next.expectedSource.completionSha256)
2823
4116
  return current;
2824
4117
  }
2825
4118
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${updateId} reported conflicting completion sources.`);
2826
4119
  }
2827
- if (current.kind === "queued" && next.kind === "queued" &&
4120
+ if (current.kind === "queued" &&
4121
+ next.kind === "queued" &&
2828
4122
  areTelegramQueueAdmissionReceiptsEqual(current, next))
2829
4123
  return current;
2830
4124
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${updateId} reported conflicting queue outcomes.`);
@@ -2836,17 +4130,23 @@ export function createTelegramInputCustodyWorkerJournalPort(store) {
2836
4130
  return {
2837
4131
  read: () => store.read(),
2838
4132
  isQueueReceiptCurrent(receipt, owner) {
2839
- const entries = store.read().entries.filter(entry => entry.queueReceiptId === receipt.receiptId);
2840
- return receipt.sourceUpdateIds.length > 0 && entries.length === receipt.sourceUpdateIds.length &&
4133
+ const entries = store
4134
+ .read()
4135
+ .entries.filter((entry) => entry.queueReceiptId === receipt.receiptId);
4136
+ return (receipt.sourceUpdateIds.length > 0 &&
4137
+ entries.length === receipt.sourceUpdateIds.length &&
2841
4138
  entries.every((entry, index) => entry.updateId === receipt.sourceUpdateIds[index] &&
2842
- entry.state === "queued" && entry.queueKind === receipt.queueKind && !entry.queueHandoff &&
2843
- entry.queueOwner !== undefined && areTelegramUpdateJournalQueueOwnersEqual(entry.queueOwner, owner));
4139
+ entry.state === "queued" &&
4140
+ entry.queueKind === receipt.queueKind &&
4141
+ !entry.queueHandoff &&
4142
+ entry.queueOwner !== undefined &&
4143
+ areTelegramUpdateJournalQueueOwnersEqual(entry.queueOwner, owner)));
2844
4144
  },
2845
4145
  markQueued: legacyMutation,
2846
4146
  markExecutionFailure: legacyMutation,
2847
4147
  // The common drain batches excluded IDs here; the store rejects all non-vetoed input atomically.
2848
- removeCompleted: updateIds => store.removeExcluded(updateIds),
2849
- completeQueued: receipts => store.completeQueued(receipts),
4148
+ removeCompleted: (updateIds) => store.removeExcluded(updateIds),
4149
+ completeQueued: (receipts) => store.completeQueued(receipts),
2850
4150
  inputCustody: {
2851
4151
  acquireInput: store.acquireInput,
2852
4152
  startInput: store.startInput,
@@ -2859,7 +4159,7 @@ export function createTelegramInputCustodyLegacyDispositionRuntime(deps) {
2859
4159
  const withBinding = (recoveryKey, operation) => {
2860
4160
  if (!recoveryKey)
2861
4161
  throw new TelegramUpdateAdmissionOutcomeError("Telegram legacy custody disposition binding is unavailable.");
2862
- return deps.withBindingReference(recoveryKey, binding => {
4162
+ return deps.withBindingReference(recoveryKey, (binding) => {
2863
4163
  if (binding.recoveryKey !== recoveryKey)
2864
4164
  throw new TelegramUpdateAdmissionOutcomeError("Telegram legacy custody disposition binding is unavailable.");
2865
4165
  return operation(binding.journal);
@@ -2867,10 +4167,10 @@ export function createTelegramInputCustodyLegacyDispositionRuntime(deps) {
2867
4167
  };
2868
4168
  return {
2869
4169
  list(recoveryKey) {
2870
- return withBinding(recoveryKey, journal => journal.listLegacyCustodyCandidates());
4170
+ return withBinding(recoveryKey, (journal) => journal.listLegacyCustodyCandidates());
2871
4171
  },
2872
4172
  apply(recoveryKey, authority) {
2873
- return withBinding(recoveryKey, journal => journal.applyLegacyCustodyDisposition(authority));
4173
+ return withBinding(recoveryKey, (journal) => journal.applyLegacyCustodyDisposition(authority));
2874
4174
  },
2875
4175
  };
2876
4176
  }
@@ -2879,44 +4179,62 @@ export function createTelegramInputCustodyHandoffClient(deps) {
2879
4179
  async transfer(input) {
2880
4180
  const reconciled = deps.resolveAcceptedReference?.({
2881
4181
  sourceUpdateId: input.receipt.updateId,
2882
- recipientBindingKey: input.recipientBindingKey
4182
+ recipientBindingKey: input.recipientBindingKey,
2883
4183
  });
2884
4184
  if (reconciled)
2885
4185
  return { ...reconciled, duplicate: true };
2886
- const offered = deps.journal.offerInputHandoff({ receipt: input.receipt,
2887
- recipientOwner: input.recipientOwner, handoffToken: input.handoffToken });
4186
+ const offered = deps.journal.offerInputHandoff({
4187
+ receipt: input.receipt,
4188
+ recipientOwner: input.recipientOwner,
4189
+ handoffToken: input.handoffToken,
4190
+ });
2888
4191
  const envelope = {
2889
- kind: "leader.offerInputCustodyHandoff", requestId: input.requestId,
4192
+ kind: "leader.offerInputCustodyHandoff",
4193
+ requestId: input.requestId,
2890
4194
  recipientInstanceId: input.recipientInstanceId,
2891
4195
  recipientRegistrationGeneration: input.recipientRegistrationGeneration,
2892
4196
  recipientBindingKey: input.recipientBindingKey,
2893
4197
  sourceRecoveryKey: offered.source.journalBindingKey,
2894
- source: offered.source, handoffId: offered.handoff.handoffId,
2895
- sentAtMs: input.sentAtMs, ...(input.auth ? { auth: input.auth } : {}),
4198
+ source: offered.source,
4199
+ handoffId: offered.handoff.handoffId,
4200
+ sentAtMs: input.sentAtMs,
4201
+ ...(input.auth ? { auth: input.auth } : {}),
2896
4202
  };
2897
4203
  const response = await deps.sendEnvelope(envelope);
2898
- if (response?.kind !== "bus.ack" || response.requestId !== input.requestId ||
2899
- !response.ok || !response.result || typeof response.result !== "object" ||
4204
+ if (response?.kind !== "bus.ack" ||
4205
+ response.requestId !== input.requestId ||
4206
+ !response.ok ||
4207
+ !response.result ||
4208
+ typeof response.result !== "object" ||
2900
4209
  Array.isArray(response.result))
2901
4210
  throw new Error("Telegram input custody handoff acknowledgement is missing or rejected.");
2902
4211
  const result = response.result;
2903
4212
  const source = result.source;
2904
4213
  if (result.sourceRecoveryKey !== offered.source.journalBindingKey ||
2905
- !source || typeof source !== "object" || Array.isArray(source) ||
2906
- source.updateId !== offered.source.updateId ||
4214
+ !source ||
4215
+ typeof source !== "object" ||
4216
+ Array.isArray(source) ||
4217
+ source.updateId !==
4218
+ offered.source.updateId ||
2907
4219
  !source.owner ||
2908
4220
  typeof source.owner !== "object" ||
2909
4221
  Array.isArray(source.owner) ||
2910
4222
  typeof source.owner.acquisitionId !== "string" ||
2911
- source.owner.handoffId !== offered.handoff.handoffId ||
4223
+ source.owner
4224
+ .handoffId !== offered.handoff.handoffId ||
2912
4225
  typeof result.duplicate !== "boolean")
2913
4226
  throw new Error("Telegram input custody handoff acknowledgement returned mismatched authority.");
2914
- return { sourceRecoveryKey: result.sourceRecoveryKey,
2915
- source: { updateId: source.updateId, owner: {
4227
+ return {
4228
+ sourceRecoveryKey: result.sourceRecoveryKey,
4229
+ source: {
4230
+ updateId: source.updateId,
4231
+ owner: {
2916
4232
  acquisitionId: source.owner.acquisitionId,
2917
- handoffId: source.owner.handoffId
2918
- } },
2919
- duplicate: result.duplicate };
4233
+ handoffId: source.owner.handoffId,
4234
+ },
4235
+ },
4236
+ duplicate: result.duplicate,
4237
+ };
2920
4238
  },
2921
4239
  };
2922
4240
  }
@@ -2924,56 +4242,88 @@ export function createTelegramInputCustodyHandoffAcceptanceRuntime(deps) {
2924
4242
  return {
2925
4243
  accept(input, ctx) {
2926
4244
  const binding = deps.resolveBinding(input.sourceRecoveryKey);
2927
- if (!binding || binding.recoveryKey !== input.sourceRecoveryKey ||
4245
+ if (!binding ||
4246
+ binding.recoveryKey !== input.sourceRecoveryKey ||
2928
4247
  binding.recipientBindingKey !== input.recipientBindingKey ||
2929
4248
  input.source.journalBindingKey !== input.sourceRecoveryKey)
2930
4249
  throw new Error("Telegram input custody handoff binding is unavailable or changed.");
2931
- const accepted = binding.journal.acceptInputHandoff({ source: input.source,
2932
- recipientOwner: binding.recipientOwner, handoffId: input.handoffId });
4250
+ const accepted = binding.journal.acceptInputHandoff({
4251
+ source: input.source,
4252
+ recipientOwner: binding.recipientOwner,
4253
+ handoffId: input.handoffId,
4254
+ });
2933
4255
  const acceptedHandoffId = accepted.receipt.owner.handoffId;
2934
4256
  if (!acceptedHandoffId || acceptedHandoffId !== input.handoffId)
2935
4257
  throw new Error("Telegram input custody handoff acceptance returned mismatched authority.");
2936
4258
  binding.signalWorker(ctx);
2937
- return { sourceRecoveryKey: binding.recoveryKey,
2938
- source: { updateId: accepted.receipt.updateId,
2939
- owner: { acquisitionId: accepted.receipt.owner.acquisitionId,
2940
- handoffId: acceptedHandoffId } }, duplicate: accepted.duplicate };
4259
+ return {
4260
+ sourceRecoveryKey: binding.recoveryKey,
4261
+ source: {
4262
+ updateId: accepted.receipt.updateId,
4263
+ owner: {
4264
+ acquisitionId: accepted.receipt.owner.acquisitionId,
4265
+ handoffId: acceptedHandoffId,
4266
+ },
4267
+ },
4268
+ duplicate: accepted.duplicate,
4269
+ };
2941
4270
  },
2942
4271
  };
2943
4272
  }
2944
4273
  export function createTelegramInputCustodyForwardReferenceResolver(deps) {
2945
- return input => {
4274
+ return (input) => {
2946
4275
  if (input.recipientBindingKey !== deps.recipientBindingKey)
2947
4276
  return undefined;
2948
- const entry = deps.journal.read().entries.find(candidate => candidate.updateId === input.sourceUpdateId);
4277
+ const entry = deps.journal
4278
+ .read()
4279
+ .entries.find((candidate) => candidate.updateId === input.sourceUpdateId);
2949
4280
  const claim = entry?.inputClaim;
2950
- if (!entry || entry.state !== "pending" || entry.preApprovalExcluded !== false ||
2951
- !claim || claim.phase !== "ready" || claim.handoff ||
4281
+ if (!entry ||
4282
+ entry.state !== "pending" ||
4283
+ entry.preApprovalExcluded !== false ||
4284
+ !claim ||
4285
+ claim.phase !== "ready" ||
4286
+ claim.handoff ||
2952
4287
  claim.recipientBindingKey !== deps.recipientBindingKey ||
2953
4288
  !claim.owner.handoffId ||
2954
4289
  claim.owner.sessionGeneration !== deps.recipientOwner.sessionGeneration ||
2955
4290
  !isTelegramUpdateJournalQueueOwnerProcess(claim.owner, deps.recipientOwner))
2956
4291
  return undefined;
2957
- return { sourceRecoveryKey: deps.recoveryKey, source: { updateId: entry.updateId,
2958
- owner: { acquisitionId: claim.owner.acquisitionId,
2959
- handoffId: claim.owner.handoffId } } };
4292
+ return {
4293
+ sourceRecoveryKey: deps.recoveryKey,
4294
+ source: {
4295
+ updateId: entry.updateId,
4296
+ owner: {
4297
+ acquisitionId: claim.owner.acquisitionId,
4298
+ handoffId: claim.owner.handoffId,
4299
+ },
4300
+ },
4301
+ };
2960
4302
  };
2961
4303
  }
2962
4304
  export function createTelegramInputCustodySourceReferenceWakeRuntime(deps) {
2963
4305
  return {
2964
4306
  wakeSource(input, ctx) {
2965
4307
  const binding = deps.resolveBinding(input.sourceRecoveryKey);
2966
- if (!binding || binding.recoveryKey !== input.sourceRecoveryKey ||
4308
+ if (!binding ||
4309
+ binding.recoveryKey !== input.sourceRecoveryKey ||
2967
4310
  binding.recipientBindingKey !== input.recipientBindingKey)
2968
4311
  throw new Error("Telegram follower source-reference binding is unavailable or changed.");
2969
- const entry = binding.journal.read().entries.find(candidate => candidate.updateId === input.sourceUpdateId);
4312
+ const entry = binding.journal
4313
+ .read()
4314
+ .entries.find((candidate) => candidate.updateId === input.sourceUpdateId);
2970
4315
  const claim = entry?.inputClaim;
2971
- if (!entry || entry.state !== "pending" || entry.preApprovalExcluded !== false ||
2972
- !claim || claim.phase !== "ready" || claim.handoff ||
4316
+ if (!entry ||
4317
+ entry.state !== "pending" ||
4318
+ entry.preApprovalExcluded !== false ||
4319
+ !claim ||
4320
+ claim.phase !== "ready" ||
4321
+ claim.handoff ||
2973
4322
  claim.recipientBindingKey !== input.recipientBindingKey ||
2974
4323
  claim.owner.acquisitionId !== input.sourceClaim.acquisitionId ||
2975
4324
  claim.owner.handoffId !== input.sourceClaim.handoffId ||
2976
- claim.owner.sessionGeneration !== binding.recipientOwner.sessionGeneration ||
4325
+ claim.owner.sessionGeneration !==
4326
+ binding.recipientOwner.sessionGeneration ||
2977
4327
  !isTelegramUpdateJournalQueueOwnerProcess(claim.owner, binding.recipientOwner))
2978
4328
  throw new Error("Telegram follower source-reference claim is unavailable or changed.");
2979
4329
  binding.signalWorker(ctx);
@@ -2982,10 +4332,10 @@ export function createTelegramInputCustodySourceReferenceWakeRuntime(deps) {
2982
4332
  }
2983
4333
  export function createTelegramInputCustodyBusBindingRuntime(deps) {
2984
4334
  const acceptance = createTelegramInputCustodyHandoffAcceptanceRuntime({
2985
- resolveBinding: deps.resolveBinding
4335
+ resolveBinding: deps.resolveBinding,
2986
4336
  });
2987
4337
  const wake = createTelegramInputCustodySourceReferenceWakeRuntime({
2988
- resolveBinding: deps.resolveBinding
4338
+ resolveBinding: deps.resolveBinding,
2989
4339
  });
2990
4340
  return {
2991
4341
  acceptHandoff: acceptance.accept,
@@ -3015,7 +4365,7 @@ export function evaluateTelegramInputCustodyActivationReadiness(input) {
3015
4365
  return { enabled: false, blocker: "migration-incomplete" };
3016
4366
  if (input.sourceStatus !== "absent" && input.sourceStatus !== "v3")
3017
4367
  return { enabled: false, blocker: "source-unready" };
3018
- if (input.peerReadiness.some(readiness => readiness !== "ready"))
4368
+ if (input.peerReadiness.some((readiness) => readiness !== "ready"))
3019
4369
  return { enabled: false, blocker: "peer-capability-mismatch" };
3020
4370
  return { enabled: true };
3021
4371
  }
@@ -3035,9 +4385,17 @@ export function createTelegramInputCustodyReadinessEvidenceStore(deps) {
3035
4385
  if (!value || typeof value !== "object" || Array.isArray(value))
3036
4386
  throw new Error("Telegram custody readiness evidence is malformed.");
3037
4387
  const snapshot = value;
3038
- if (snapshot.version !== 1 || !Number.isSafeInteger(snapshot.revision) ||
3039
- snapshot.revision < 0 || Object.keys(snapshot).some(key => !["version", "revision", "writerExclusion", "migration", "startupExclusion",
3040
- "migrationCompletion"].includes(key)))
4388
+ if (snapshot.version !== 1 ||
4389
+ !Number.isSafeInteger(snapshot.revision) ||
4390
+ snapshot.revision < 0 ||
4391
+ Object.keys(snapshot).some((key) => ![
4392
+ "version",
4393
+ "revision",
4394
+ "writerExclusion",
4395
+ "migration",
4396
+ "startupExclusion",
4397
+ "migrationCompletion",
4398
+ ].includes(key)))
3041
4399
  throw new Error("Telegram custody readiness evidence is malformed.");
3042
4400
  const validate = (evidence, kind) => {
3043
4401
  if (evidence === undefined)
@@ -3045,61 +4403,104 @@ export function createTelegramInputCustodyReadinessEvidenceStore(deps) {
3045
4403
  if (!evidence || typeof evidence !== "object" || Array.isArray(evidence))
3046
4404
  throw new Error("Telegram custody readiness evidence is malformed.");
3047
4405
  const record = evidence;
3048
- const statuses = kind === "writer" ? ["excluded", "present", "unknown"]
4406
+ const statuses = kind === "writer"
4407
+ ? ["excluded", "present", "unknown"]
3049
4408
  : ["complete", "incomplete", "unknown"];
3050
- const writerKeys = ["startupAuthorityId", "closureOperationId", "writerInventorySha256"];
3051
- const migrationKeys = ["migrationAuthorityId", "startupAuthorityId", "closureOperationId",
3052
- "migrationInventorySha256", "resultingSourceFamily"];
4409
+ const writerKeys = [
4410
+ "startupAuthorityId",
4411
+ "closureOperationId",
4412
+ "writerInventorySha256",
4413
+ ];
4414
+ const migrationKeys = [
4415
+ "migrationAuthorityId",
4416
+ "startupAuthorityId",
4417
+ "closureOperationId",
4418
+ "migrationInventorySha256",
4419
+ "resultingSourceFamily",
4420
+ ];
3053
4421
  const linkedKeys = kind === "writer" ? writerKeys : migrationKeys;
3054
- const suppliedLinkedKeys = linkedKeys.filter(key => record[key] !== undefined);
3055
- const linkedMalformed = suppliedLinkedKeys.length === linkedKeys.length && (typeof record.startupAuthorityId !== "string" || !record.startupAuthorityId ||
3056
- typeof record.closureOperationId !== "string" || !record.closureOperationId ||
3057
- (kind === "writer"
3058
- ? typeof record.writerInventorySha256 !== "string" ||
3059
- !/^[a-f0-9]{64}$/u.test(record.writerInventorySha256)
3060
- : typeof record.migrationAuthorityId !== "string" || !record.migrationAuthorityId ||
3061
- typeof record.migrationInventorySha256 !== "string" ||
3062
- !/^[a-f0-9]{64}$/u.test(record.migrationInventorySha256) ||
3063
- (record.resultingSourceFamily !== "absent" && record.resultingSourceFamily !== "v3")));
3064
- if (record.version !== 1 || typeof record.profileKey !== "string" || !record.profileKey ||
3065
- typeof record.recoveryKey !== "string" || !record.recoveryKey ||
3066
- !statuses.includes(record.status) || Object.keys(record).some(key => !["version", "profileKey", "recoveryKey", "status", ...linkedKeys].includes(key)) ||
3067
- (suppliedLinkedKeys.length !== 0 && suppliedLinkedKeys.length !== linkedKeys.length) ||
4422
+ const suppliedLinkedKeys = linkedKeys.filter((key) => record[key] !== undefined);
4423
+ const linkedMalformed = suppliedLinkedKeys.length === linkedKeys.length &&
4424
+ (typeof record.startupAuthorityId !== "string" ||
4425
+ !record.startupAuthorityId ||
4426
+ typeof record.closureOperationId !== "string" ||
4427
+ !record.closureOperationId ||
4428
+ (kind === "writer"
4429
+ ? typeof record.writerInventorySha256 !== "string" ||
4430
+ !/^[a-f0-9]{64}$/u.test(record.writerInventorySha256)
4431
+ : typeof record.migrationAuthorityId !== "string" ||
4432
+ !record.migrationAuthorityId ||
4433
+ typeof record.migrationInventorySha256 !== "string" ||
4434
+ !/^[a-f0-9]{64}$/u.test(record.migrationInventorySha256) ||
4435
+ (record.resultingSourceFamily !== "absent" &&
4436
+ record.resultingSourceFamily !== "v3")));
4437
+ if (record.version !== 1 ||
4438
+ typeof record.profileKey !== "string" ||
4439
+ !record.profileKey ||
4440
+ typeof record.recoveryKey !== "string" ||
4441
+ !record.recoveryKey ||
4442
+ !statuses.includes(record.status) ||
4443
+ Object.keys(record).some((key) => ![
4444
+ "version",
4445
+ "profileKey",
4446
+ "recoveryKey",
4447
+ "status",
4448
+ ...linkedKeys,
4449
+ ].includes(key)) ||
4450
+ (suppliedLinkedKeys.length !== 0 &&
4451
+ suppliedLinkedKeys.length !== linkedKeys.length) ||
3068
4452
  linkedMalformed)
3069
4453
  throw new Error("Telegram custody readiness evidence is malformed.");
3070
- return { version: 1, profileKey: record.profileKey,
3071
- recoveryKey: record.recoveryKey, status: record.status,
3072
- ...(suppliedLinkedKeys.length === linkedKeys.length ? kind === "writer" ? {
3073
- startupAuthorityId: record.startupAuthorityId,
3074
- closureOperationId: record.closureOperationId,
3075
- writerInventorySha256: record.writerInventorySha256,
3076
- } : {
3077
- migrationAuthorityId: record.migrationAuthorityId,
3078
- startupAuthorityId: record.startupAuthorityId,
3079
- closureOperationId: record.closureOperationId,
3080
- migrationInventorySha256: record.migrationInventorySha256,
3081
- resultingSourceFamily: record.resultingSourceFamily,
3082
- } : {}) };
4454
+ return {
4455
+ version: 1,
4456
+ profileKey: record.profileKey,
4457
+ recoveryKey: record.recoveryKey,
4458
+ status: record.status,
4459
+ ...(suppliedLinkedKeys.length === linkedKeys.length
4460
+ ? kind === "writer"
4461
+ ? {
4462
+ startupAuthorityId: record.startupAuthorityId,
4463
+ closureOperationId: record.closureOperationId,
4464
+ writerInventorySha256: record.writerInventorySha256,
4465
+ }
4466
+ : {
4467
+ migrationAuthorityId: record.migrationAuthorityId,
4468
+ startupAuthorityId: record.startupAuthorityId,
4469
+ closureOperationId: record.closureOperationId,
4470
+ migrationInventorySha256: record.migrationInventorySha256,
4471
+ resultingSourceFamily: record.resultingSourceFamily,
4472
+ }
4473
+ : {}),
4474
+ };
3083
4475
  };
3084
4476
  const writerExclusion = validate(snapshot.writerExclusion, "writer");
3085
4477
  const migration = validate(snapshot.migration, "migration");
3086
4478
  let startupExclusion;
3087
4479
  if (snapshot.startupExclusion !== undefined) {
3088
4480
  const candidate = snapshot.startupExclusion;
3089
- if (!candidate || typeof candidate.profileKey !== "string" ||
4481
+ if (!candidate ||
4482
+ typeof candidate.profileKey !== "string" ||
3090
4483
  typeof candidate.recoveryKey !== "string")
3091
4484
  throw new Error("Telegram custody readiness evidence is malformed.");
3092
- startupExclusion = normalizeTelegramInputCustodyStartupExclusionAuthority(candidate, { profileKey: candidate.profileKey, recoveryKey: candidate.recoveryKey });
4485
+ startupExclusion = normalizeTelegramInputCustodyStartupExclusionAuthority(candidate, {
4486
+ profileKey: candidate.profileKey,
4487
+ recoveryKey: candidate.recoveryKey,
4488
+ });
3093
4489
  if (!startupExclusion)
3094
4490
  throw new Error("Telegram custody readiness evidence is malformed.");
3095
4491
  }
3096
4492
  let migrationCompletion;
3097
4493
  if (snapshot.migrationCompletion !== undefined) {
3098
4494
  const candidate = snapshot.migrationCompletion;
3099
- if (!candidate || typeof candidate.profileKey !== "string" ||
4495
+ if (!candidate ||
4496
+ typeof candidate.profileKey !== "string" ||
3100
4497
  typeof candidate.recoveryKey !== "string")
3101
4498
  throw new Error("Telegram custody readiness evidence is malformed.");
3102
- migrationCompletion = normalizeTelegramInputCustodyMigrationCompletionAuthority(candidate, { profileKey: candidate.profileKey, recoveryKey: candidate.recoveryKey });
4499
+ migrationCompletion =
4500
+ normalizeTelegramInputCustodyMigrationCompletionAuthority(candidate, {
4501
+ profileKey: candidate.profileKey,
4502
+ recoveryKey: candidate.recoveryKey,
4503
+ });
3103
4504
  if (!migrationCompletion)
3104
4505
  throw new Error("Telegram custody readiness evidence is malformed.");
3105
4506
  }
@@ -3112,48 +4513,72 @@ export function createTelegramInputCustodyReadinessEvidenceStore(deps) {
3112
4513
  identities.push(startupExclusion);
3113
4514
  if (migrationCompletion)
3114
4515
  identities.push(migrationCompletion);
3115
- if (identities.some(evidence => evidence.profileKey !== identities[0]?.profileKey ||
4516
+ if (identities.some((evidence) => evidence.profileKey !== identities[0]?.profileKey ||
3116
4517
  evidence.recoveryKey !== identities[0]?.recoveryKey))
3117
4518
  throw new Error("Telegram custody readiness evidence has conflicting identities.");
3118
- if (writerExclusion?.startupAuthorityId && startupExclusion &&
4519
+ if (writerExclusion?.startupAuthorityId &&
4520
+ startupExclusion &&
3119
4521
  (writerExclusion.startupAuthorityId !== startupExclusion.authorityId ||
3120
- writerExclusion.closureOperationId !== startupExclusion.closureOperationId ||
3121
- writerExclusion.writerInventorySha256 !== startupExclusion.writerInventorySha256))
4522
+ writerExclusion.closureOperationId !==
4523
+ startupExclusion.closureOperationId ||
4524
+ writerExclusion.writerInventorySha256 !==
4525
+ startupExclusion.writerInventorySha256))
3122
4526
  throw new Error("Telegram custody readiness evidence has conflicting authorities.");
3123
- if (migration?.migrationAuthorityId && migrationCompletion &&
4527
+ if (migration?.migrationAuthorityId &&
4528
+ migrationCompletion &&
3124
4529
  (migration.migrationAuthorityId !== migrationCompletion.authorityId ||
3125
- migration.startupAuthorityId !== migrationCompletion.startupAuthorityId ||
3126
- migration.closureOperationId !== migrationCompletion.closureOperationId ||
3127
- migration.migrationInventorySha256 !== migrationCompletion.migrationInventorySha256 ||
3128
- migration.resultingSourceFamily !== migrationCompletion.resultingSourceFamily))
4530
+ migration.startupAuthorityId !==
4531
+ migrationCompletion.startupAuthorityId ||
4532
+ migration.closureOperationId !==
4533
+ migrationCompletion.closureOperationId ||
4534
+ migration.migrationInventorySha256 !==
4535
+ migrationCompletion.migrationInventorySha256 ||
4536
+ migration.resultingSourceFamily !==
4537
+ migrationCompletion.resultingSourceFamily))
3129
4538
  throw new Error("Telegram custody readiness evidence has conflicting migration authorities.");
3130
- if (migrationCompletion && startupExclusion &&
3131
- (migrationCompletion.startupAuthorityId !== startupExclusion.authorityId ||
3132
- migrationCompletion.closureOperationId !== startupExclusion.closureOperationId))
4539
+ if (migrationCompletion &&
4540
+ startupExclusion &&
4541
+ (migrationCompletion.startupAuthorityId !==
4542
+ startupExclusion.authorityId ||
4543
+ migrationCompletion.closureOperationId !==
4544
+ startupExclusion.closureOperationId))
3133
4545
  throw new Error("Telegram custody readiness evidence has conflicting cutover authorities.");
3134
- return { version: 1, revision: snapshot.revision,
4546
+ return {
4547
+ version: 1,
4548
+ revision: snapshot.revision,
3135
4549
  ...(writerExclusion ? { writerExclusion } : {}),
3136
4550
  ...(migration ? { migration } : {}),
3137
4551
  ...(startupExclusion ? { startupExclusion } : {}),
3138
- ...(migrationCompletion ? { migrationCompletion } : {}) };
4552
+ ...(migrationCompletion ? { migrationCompletion } : {}),
4553
+ };
3139
4554
  };
3140
4555
  const read = () => decode(deps.readRetained());
3141
4556
  const publish = (input) => deps.withSerialization(() => {
3142
4557
  const current = read();
3143
4558
  if (current.revision !== input.expectedRevision)
3144
4559
  throw new Error("Telegram custody readiness evidence revision changed.");
3145
- const next = { ...current, revision: current.revision + 1,
3146
- ...(input.kind === "writer-exclusion" ? { writerExclusion: input.evidence } :
3147
- input.kind === "migration" ? { migration: input.evidence } :
3148
- input.kind === "startup-exclusion" ? { startupExclusion: input.evidence } :
3149
- { migrationCompletion: input.evidence }) };
4560
+ const next = {
4561
+ ...current,
4562
+ revision: current.revision + 1,
4563
+ ...(input.kind === "writer-exclusion"
4564
+ ? { writerExclusion: input.evidence }
4565
+ : input.kind === "migration"
4566
+ ? { migration: input.evidence }
4567
+ : input.kind === "startup-exclusion"
4568
+ ? { startupExclusion: input.evidence }
4569
+ : { migrationCompletion: input.evidence }),
4570
+ };
3150
4571
  const serialized = `${JSON.stringify(next)}\n`;
3151
4572
  const validated = decode(serialized);
3152
4573
  const normalizedEvidence = input.kind === "writer-exclusion"
3153
- ? validated.writerExclusion : input.kind === "migration"
3154
- ? validated.migration : input.kind === "startup-exclusion"
3155
- ? validated.startupExclusion : validated.migrationCompletion;
3156
- if (!normalizedEvidence || !deps.authorizePublication(input.kind, normalizedEvidence))
4574
+ ? validated.writerExclusion
4575
+ : input.kind === "migration"
4576
+ ? validated.migration
4577
+ : input.kind === "startup-exclusion"
4578
+ ? validated.startupExclusion
4579
+ : validated.migrationCompletion;
4580
+ if (!normalizedEvidence ||
4581
+ !deps.authorizePublication(input.kind, normalizedEvidence))
3157
4582
  throw new Error("Telegram custody readiness evidence publication is unauthorized.");
3158
4583
  deps.publishRetained(serialized);
3159
4584
  return validated;
@@ -3164,23 +4589,45 @@ export function normalizeTelegramInputCustodyStartupExclusionAuthority(value, ex
3164
4589
  if (!value || typeof value !== "object" || Array.isArray(value))
3165
4590
  return undefined;
3166
4591
  const record = value;
3167
- const keys = ["version", "status", "authorityId", "closureOperationId", "profileKey",
3168
- "recoveryKey", "writerInventorySha256", "allowedWriterProtocol", "authorizedAtMs"];
3169
- if (Object.keys(record).some(key => !keys.includes(key)) || record.version !== 1 ||
4592
+ const keys = [
4593
+ "version",
4594
+ "status",
4595
+ "authorityId",
4596
+ "closureOperationId",
4597
+ "profileKey",
4598
+ "recoveryKey",
4599
+ "writerInventorySha256",
4600
+ "allowedWriterProtocol",
4601
+ "authorizedAtMs",
4602
+ ];
4603
+ if (Object.keys(record).some((key) => !keys.includes(key)) ||
4604
+ record.version !== 1 ||
3170
4605
  (record.status !== "enforced" && record.status !== "revoked") ||
3171
- record.profileKey !== expected.profileKey || record.recoveryKey !== expected.recoveryKey ||
3172
- typeof record.authorityId !== "string" || !record.authorityId || record.authorityId.length > 512 ||
3173
- typeof record.closureOperationId !== "string" || !record.closureOperationId ||
4606
+ record.profileKey !== expected.profileKey ||
4607
+ record.recoveryKey !== expected.recoveryKey ||
4608
+ typeof record.authorityId !== "string" ||
4609
+ !record.authorityId ||
4610
+ record.authorityId.length > 512 ||
4611
+ typeof record.closureOperationId !== "string" ||
4612
+ !record.closureOperationId ||
3174
4613
  record.closureOperationId.length > 512 ||
3175
4614
  typeof record.writerInventorySha256 !== "string" ||
3176
4615
  !/^[a-f0-9]{64}$/u.test(record.writerInventorySha256) ||
3177
4616
  record.allowedWriterProtocol !== "custody-v3" ||
3178
- !Number.isSafeInteger(record.authorizedAtMs) || record.authorizedAtMs < 0)
4617
+ !Number.isSafeInteger(record.authorizedAtMs) ||
4618
+ record.authorizedAtMs < 0)
3179
4619
  return undefined;
3180
- return { version: 1, status: record.status, authorityId: record.authorityId,
3181
- closureOperationId: record.closureOperationId, profileKey: expected.profileKey,
3182
- recoveryKey: expected.recoveryKey, writerInventorySha256: record.writerInventorySha256,
3183
- allowedWriterProtocol: "custody-v3", authorizedAtMs: record.authorizedAtMs };
4620
+ return {
4621
+ version: 1,
4622
+ status: record.status,
4623
+ authorityId: record.authorityId,
4624
+ closureOperationId: record.closureOperationId,
4625
+ profileKey: expected.profileKey,
4626
+ recoveryKey: expected.recoveryKey,
4627
+ writerInventorySha256: record.writerInventorySha256,
4628
+ allowedWriterProtocol: "custody-v3",
4629
+ authorizedAtMs: record.authorizedAtMs,
4630
+ };
3184
4631
  }
3185
4632
  export function evaluateTelegramInputCustodyWriterExclusionEvidence(input) {
3186
4633
  const identity = { ...input.expected };
@@ -3189,14 +4636,17 @@ export function evaluateTelegramInputCustodyWriterExclusionEvidence(input) {
3189
4636
  input.inventory.profileKey !== input.expected.profileKey ||
3190
4637
  input.inventory.recoveryKey !== input.expected.recoveryKey ||
3191
4638
  !/^[a-f0-9]{64}$/u.test(input.inventory.writerInventorySha256) ||
3192
- !authority || authority.status !== "enforced" ||
4639
+ !authority ||
4640
+ authority.status !== "enforced" ||
3193
4641
  authority.profileKey !== input.expected.profileKey ||
3194
4642
  authority.recoveryKey !== input.expected.recoveryKey ||
3195
4643
  authority.writerInventorySha256 !== input.inventory.writerInventorySha256)
3196
4644
  return { version: 1, status: "unknown", ...identity };
3197
- const authorityBinding = { startupAuthorityId: authority.authorityId,
4645
+ const authorityBinding = {
4646
+ startupAuthorityId: authority.authorityId,
3198
4647
  closureOperationId: authority.closureOperationId,
3199
- writerInventorySha256: authority.writerInventorySha256 };
4648
+ writerInventorySha256: authority.writerInventorySha256,
4649
+ };
3200
4650
  let unknown = false;
3201
4651
  for (const writer of input.inventory.writers) {
3202
4652
  let liveness;
@@ -3207,49 +4657,82 @@ export function evaluateTelegramInputCustodyWriterExclusionEvidence(input) {
3207
4657
  liveness = "unverifiable";
3208
4658
  }
3209
4659
  if (liveness === "alive")
3210
- return { version: 1, status: "present", ...identity,
3211
- ...authorityBinding };
4660
+ return {
4661
+ version: 1,
4662
+ status: "present",
4663
+ ...identity,
4664
+ ...authorityBinding,
4665
+ };
3212
4666
  if (liveness !== "dead")
3213
4667
  unknown = true;
3214
4668
  }
3215
- return { version: 1, status: unknown ? "unknown" : "excluded", ...identity,
3216
- ...authorityBinding };
4669
+ return {
4670
+ version: 1,
4671
+ status: unknown ? "unknown" : "excluded",
4672
+ ...identity,
4673
+ ...authorityBinding,
4674
+ };
3217
4675
  }
3218
4676
  export function normalizeTelegramInputCustodyMigrationCompletionAuthority(value, expected) {
3219
4677
  if (!value || typeof value !== "object" || Array.isArray(value))
3220
4678
  return undefined;
3221
4679
  const record = value;
3222
- const keys = ["version", "status", "authorityId", "startupAuthorityId",
3223
- "closureOperationId", "profileKey", "recoveryKey", "migrationInventorySha256",
3224
- "resultingSourceFamily", "authorizedAtMs"];
3225
- if (Object.keys(record).some(key => !keys.includes(key)) || record.version !== 1 ||
4680
+ const keys = [
4681
+ "version",
4682
+ "status",
4683
+ "authorityId",
4684
+ "startupAuthorityId",
4685
+ "closureOperationId",
4686
+ "profileKey",
4687
+ "recoveryKey",
4688
+ "migrationInventorySha256",
4689
+ "resultingSourceFamily",
4690
+ "authorizedAtMs",
4691
+ ];
4692
+ if (Object.keys(record).some((key) => !keys.includes(key)) ||
4693
+ record.version !== 1 ||
3226
4694
  (record.status !== "authorized" && record.status !== "revoked") ||
3227
- record.profileKey !== expected.profileKey || record.recoveryKey !== expected.recoveryKey ||
3228
- typeof record.authorityId !== "string" || !record.authorityId || record.authorityId.length > 512 ||
3229
- typeof record.startupAuthorityId !== "string" || !record.startupAuthorityId ||
4695
+ record.profileKey !== expected.profileKey ||
4696
+ record.recoveryKey !== expected.recoveryKey ||
4697
+ typeof record.authorityId !== "string" ||
4698
+ !record.authorityId ||
4699
+ record.authorityId.length > 512 ||
4700
+ typeof record.startupAuthorityId !== "string" ||
4701
+ !record.startupAuthorityId ||
3230
4702
  record.startupAuthorityId.length > 512 ||
3231
- typeof record.closureOperationId !== "string" || !record.closureOperationId ||
4703
+ typeof record.closureOperationId !== "string" ||
4704
+ !record.closureOperationId ||
3232
4705
  record.closureOperationId.length > 512 ||
3233
4706
  typeof record.migrationInventorySha256 !== "string" ||
3234
4707
  !/^[a-f0-9]{64}$/u.test(record.migrationInventorySha256) ||
3235
- (record.resultingSourceFamily !== "absent" && record.resultingSourceFamily !== "v3") ||
3236
- !Number.isSafeInteger(record.authorizedAtMs) || record.authorizedAtMs < 0)
4708
+ (record.resultingSourceFamily !== "absent" &&
4709
+ record.resultingSourceFamily !== "v3") ||
4710
+ !Number.isSafeInteger(record.authorizedAtMs) ||
4711
+ record.authorizedAtMs < 0)
3237
4712
  return undefined;
3238
- return { version: 1, status: record.status, authorityId: record.authorityId,
4713
+ return {
4714
+ version: 1,
4715
+ status: record.status,
4716
+ authorityId: record.authorityId,
3239
4717
  startupAuthorityId: record.startupAuthorityId,
3240
- closureOperationId: record.closureOperationId, profileKey: expected.profileKey,
4718
+ closureOperationId: record.closureOperationId,
4719
+ profileKey: expected.profileKey,
3241
4720
  recoveryKey: expected.recoveryKey,
3242
4721
  migrationInventorySha256: record.migrationInventorySha256,
3243
4722
  resultingSourceFamily: record.resultingSourceFamily,
3244
- authorizedAtMs: record.authorizedAtMs };
4723
+ authorizedAtMs: record.authorizedAtMs,
4724
+ };
3245
4725
  }
3246
4726
  export function executeTelegramInputCustodyWriterCutover(input) {
3247
- const matchesAuthority = (candidate) => Boolean(candidate && candidate.status === "enforced" && candidate.version === 1 &&
4727
+ const matchesAuthority = (candidate) => Boolean(candidate &&
4728
+ candidate.status === "enforced" &&
4729
+ candidate.version === 1 &&
3248
4730
  candidate.profileKey === input.expected.profileKey &&
3249
4731
  candidate.recoveryKey === input.expected.recoveryKey &&
3250
4732
  candidate.authorityId === input.startupAuthority.authorityId &&
3251
4733
  candidate.closureOperationId === input.closure.operationId &&
3252
- candidate.writerInventorySha256 === input.startupAuthority.writerInventorySha256 &&
4734
+ candidate.writerInventorySha256 ===
4735
+ input.startupAuthority.writerInventorySha256 &&
3253
4736
  candidate.allowedWriterProtocol === "custody-v3" &&
3254
4737
  candidate.authorizedAtMs === input.startupAuthority.authorizedAtMs);
3255
4738
  if (input.closure.profileKey !== input.expected.profileKey ||
@@ -3257,8 +4740,10 @@ export function executeTelegramInputCustodyWriterCutover(input) {
3257
4740
  !matchesAuthority(input.evidenceStore.read().startupExclusion))
3258
4741
  return { kind: "blocked", blocker: "startup-authority" };
3259
4742
  const evidence = evaluateTelegramInputCustodyWriterExclusionEvidence({
3260
- expected: input.expected, inventory: input.inventory,
3261
- startupAuthority: input.startupAuthority, getProcessLiveness: input.getProcessLiveness
4743
+ expected: input.expected,
4744
+ inventory: input.inventory,
4745
+ startupAuthority: input.startupAuthority,
4746
+ getProcessLiveness: input.getProcessLiveness,
3262
4747
  });
3263
4748
  if (evidence.status !== "excluded")
3264
4749
  return { kind: "blocked", blocker: "writer-inventory", evidence };
@@ -3277,30 +4762,43 @@ export function executeTelegramInputCustodyWriterCutover(input) {
3277
4762
  if (!matchesAuthority(current.startupExclusion))
3278
4763
  return { kind: "blocked", blocker: "startup-authority", evidence };
3279
4764
  const existing = current.writerExclusion;
3280
- if (existing?.status === "excluded" && existing.profileKey === evidence.profileKey &&
4765
+ if (existing?.status === "excluded" &&
4766
+ existing.profileKey === evidence.profileKey &&
3281
4767
  existing.recoveryKey === evidence.recoveryKey &&
3282
4768
  existing.startupAuthorityId === evidence.startupAuthorityId &&
3283
4769
  existing.closureOperationId === evidence.closureOperationId &&
3284
4770
  existing.writerInventorySha256 === evidence.writerInventorySha256)
3285
- return { kind: "completed", mode: installed.mode, evidence: existing,
3286
- resumed: installed.resumed };
3287
- input.evidenceStore.publish({ expectedRevision: current.revision,
3288
- kind: "writer-exclusion", evidence });
4771
+ return {
4772
+ kind: "completed",
4773
+ mode: installed.mode,
4774
+ evidence: existing,
4775
+ resumed: installed.resumed,
4776
+ };
4777
+ input.evidenceStore.publish({
4778
+ expectedRevision: current.revision,
4779
+ kind: "writer-exclusion",
4780
+ evidence,
4781
+ });
3289
4782
  return { kind: "completed", mode: installed.mode, evidence, resumed: false };
3290
4783
  }
3291
4784
  function matchesTelegramInputCustodyActivationEvidence(evidence, expected) {
3292
- return Boolean(evidence && evidence.profileKey === expected.profileKey &&
4785
+ return Boolean(evidence &&
4786
+ evidence.profileKey === expected.profileKey &&
3293
4787
  evidence.recoveryKey === expected.recoveryKey);
3294
4788
  }
3295
4789
  export function executeTelegramInputCustodyMigrationCompletion(input) {
3296
- const matchesAuthority = (candidate) => Boolean(candidate && candidate.version === 1 && candidate.status === "authorized" &&
4790
+ const matchesAuthority = (candidate) => Boolean(candidate &&
4791
+ candidate.version === 1 &&
4792
+ candidate.status === "authorized" &&
3297
4793
  candidate.profileKey === input.expected.profileKey &&
3298
4794
  candidate.recoveryKey === input.expected.recoveryKey &&
3299
4795
  candidate.authorityId === input.authority.authorityId &&
3300
4796
  candidate.startupAuthorityId === input.authority.startupAuthorityId &&
3301
4797
  candidate.closureOperationId === input.authority.closureOperationId &&
3302
- candidate.migrationInventorySha256 === input.authority.migrationInventorySha256 &&
3303
- candidate.resultingSourceFamily === input.authority.resultingSourceFamily &&
4798
+ candidate.migrationInventorySha256 ===
4799
+ input.authority.migrationInventorySha256 &&
4800
+ candidate.resultingSourceFamily ===
4801
+ input.authority.resultingSourceFamily &&
3304
4802
  candidate.authorizedAtMs === input.authority.authorizedAtMs);
3305
4803
  const current = input.evidenceStore.read();
3306
4804
  if (!matchesAuthority(current.migrationCompletion))
@@ -3316,15 +4814,19 @@ export function executeTelegramInputCustodyMigrationCompletion(input) {
3316
4814
  }
3317
4815
  if (source !== input.authority.resultingSourceFamily)
3318
4816
  return { kind: "blocked", blocker: "source-drift" };
3319
- const evidence = { version: 1,
3320
- status: "complete", ...input.expected,
4817
+ const evidence = {
4818
+ version: 1,
4819
+ status: "complete",
4820
+ ...input.expected,
3321
4821
  migrationAuthorityId: input.authority.authorityId,
3322
4822
  startupAuthorityId: input.authority.startupAuthorityId,
3323
4823
  closureOperationId: input.authority.closureOperationId,
3324
4824
  migrationInventorySha256: input.authority.migrationInventorySha256,
3325
- resultingSourceFamily: input.authority.resultingSourceFamily };
4825
+ resultingSourceFamily: input.authority.resultingSourceFamily,
4826
+ };
3326
4827
  const existing = current.migration;
3327
- if (existing?.status === "complete" && existing.profileKey === evidence.profileKey &&
4828
+ if (existing?.status === "complete" &&
4829
+ existing.profileKey === evidence.profileKey &&
3328
4830
  existing.recoveryKey === evidence.recoveryKey &&
3329
4831
  existing.migrationAuthorityId === evidence.migrationAuthorityId &&
3330
4832
  existing.startupAuthorityId === evidence.startupAuthorityId &&
@@ -3332,7 +4834,11 @@ export function executeTelegramInputCustodyMigrationCompletion(input) {
3332
4834
  existing.migrationInventorySha256 === evidence.migrationInventorySha256 &&
3333
4835
  existing.resultingSourceFamily === evidence.resultingSourceFamily)
3334
4836
  return { kind: "completed", evidence: existing, resumed: true };
3335
- input.evidenceStore.publish({ expectedRevision: current.revision, kind: "migration", evidence });
4837
+ input.evidenceStore.publish({
4838
+ expectedRevision: current.revision,
4839
+ kind: "migration",
4840
+ evidence,
4841
+ });
3336
4842
  return { kind: "completed", evidence, resumed: false };
3337
4843
  }
3338
4844
  export function createTelegramInputCustodyProvenReadinessResolver(deps) {
@@ -3349,26 +4855,30 @@ export function createTelegramInputCustodyProvenReadinessResolver(deps) {
3349
4855
  const expected = deps.expectedIdentity();
3350
4856
  const authority = deps.readMigrationCompletionAuthority();
3351
4857
  return matchesTelegramInputCustodyActivationEvidence(authority, expected) &&
3352
- authority?.status === "authorized" && authority.resultingSourceFamily === source
3353
- ? source : "ambiguous";
4858
+ authority?.status === "authorized" &&
4859
+ authority.resultingSourceFamily === source
4860
+ ? source
4861
+ : "ambiguous";
3354
4862
  },
3355
4863
  areLegacyWritersExcluded() {
3356
4864
  const expected = deps.expectedIdentity();
3357
4865
  const evidence = deps.readWriterExclusionEvidence();
3358
4866
  const authority = deps.readStartupExclusionAuthority();
3359
4867
  const mode = deps.readWriterProtocolMode();
3360
- return matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
4868
+ return (matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
3361
4869
  matchesTelegramInputCustodyActivationEvidence(authority, expected) &&
3362
4870
  matchesTelegramInputCustodyActivationEvidence(mode, expected) &&
3363
- evidence?.version === 1 && evidence.status === "excluded" &&
3364
- authority?.version === 1 && authority.status === "enforced" &&
4871
+ evidence?.version === 1 &&
4872
+ evidence.status === "excluded" &&
4873
+ authority?.version === 1 &&
4874
+ authority.status === "enforced" &&
3365
4875
  mode?.protocol === "custody-v3" &&
3366
4876
  evidence.startupAuthorityId === authority.authorityId &&
3367
4877
  evidence.startupAuthorityId === mode.startupAuthorityId &&
3368
4878
  evidence.closureOperationId === authority.closureOperationId &&
3369
4879
  evidence.closureOperationId === mode.closureOperationId &&
3370
4880
  evidence.writerInventorySha256 === authority.writerInventorySha256 &&
3371
- evidence.writerInventorySha256 === mode.writerInventorySha256;
4881
+ evidence.writerInventorySha256 === mode.writerInventorySha256);
3372
4882
  },
3373
4883
  isHistoricalMigrationComplete() {
3374
4884
  const expected = deps.expectedIdentity();
@@ -3383,10 +4893,12 @@ export function createTelegramInputCustodyProvenReadinessResolver(deps) {
3383
4893
  catch {
3384
4894
  source = "ambiguous";
3385
4895
  }
3386
- return matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
4896
+ return (matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
3387
4897
  matchesTelegramInputCustodyActivationEvidence(authority, expected) &&
3388
- evidence?.version === 1 && evidence.status === "complete" &&
3389
- authority?.version === 1 && authority.status === "authorized" &&
4898
+ evidence?.version === 1 &&
4899
+ evidence.status === "complete" &&
4900
+ authority?.version === 1 &&
4901
+ authority.status === "authorized" &&
3390
4902
  authority.resultingSourceFamily === source &&
3391
4903
  evidence.migrationAuthorityId === authority.authorityId &&
3392
4904
  evidence.startupAuthorityId === authority.startupAuthorityId &&
@@ -3394,8 +4906,9 @@ export function createTelegramInputCustodyProvenReadinessResolver(deps) {
3394
4906
  evidence.startupAuthorityId === mode?.startupAuthorityId &&
3395
4907
  evidence.closureOperationId === authority.closureOperationId &&
3396
4908
  evidence.closureOperationId === mode?.closureOperationId &&
3397
- evidence.migrationInventorySha256 === authority.migrationInventorySha256 &&
3398
- evidence.resultingSourceFamily === authority.resultingSourceFamily;
4909
+ evidence.migrationInventorySha256 ===
4910
+ authority.migrationInventorySha256 &&
4911
+ evidence.resultingSourceFamily === authority.resultingSourceFamily);
3399
4912
  },
3400
4913
  listPeerReadiness: deps.listPeerReadiness,
3401
4914
  });
@@ -3409,14 +4922,18 @@ export function createTelegramInputCustodyActivationReadinessResolver(deps) {
3409
4922
  try {
3410
4923
  legacyWritersExcluded = deps.areLegacyWritersExcluded();
3411
4924
  }
3412
- catch { /* blocked */ }
4925
+ catch {
4926
+ /* blocked */
4927
+ }
3413
4928
  if (!legacyWritersExcluded)
3414
4929
  return { enabled: false, blocker: "legacy-writers-present" };
3415
4930
  let historicalMigrationComplete = false;
3416
4931
  try {
3417
4932
  historicalMigrationComplete = deps.isHistoricalMigrationComplete();
3418
4933
  }
3419
- catch { /* blocked */ }
4934
+ catch {
4935
+ /* blocked */
4936
+ }
3420
4937
  if (!historicalMigrationComplete)
3421
4938
  return { enabled: false, blocker: "migration-incomplete" };
3422
4939
  let sourceStatus;
@@ -3435,8 +4952,13 @@ export function createTelegramInputCustodyActivationReadinessResolver(deps) {
3435
4952
  catch {
3436
4953
  peerReadiness = ["unknown"];
3437
4954
  }
3438
- return evaluateTelegramInputCustodyActivationReadiness({ requested,
3439
- sourceStatus, legacyWritersExcluded, historicalMigrationComplete, peerReadiness });
4955
+ return evaluateTelegramInputCustodyActivationReadiness({
4956
+ requested,
4957
+ sourceStatus,
4958
+ legacyWritersExcluded,
4959
+ historicalMigrationComplete,
4960
+ peerReadiness,
4961
+ });
3440
4962
  };
3441
4963
  }
3442
4964
  export function createTelegramInputCustodyLifecycleBindingResolver(deps) {
@@ -3447,21 +4969,33 @@ export function createTelegramInputCustodyLifecycleBindingResolver(deps) {
3447
4969
  return undefined;
3448
4970
  const source = deps.resolveInputJournal();
3449
4971
  const recipientBindingKey = deps.getRecipientBindingKey()?.trim();
3450
- if (!source || !source.runtimeKey || !source.recoveryKey || !recipientBindingKey)
4972
+ if (!source ||
4973
+ !source.runtimeKey ||
4974
+ !source.recoveryKey ||
4975
+ !recipientBindingKey)
3451
4976
  return undefined;
3452
4977
  let port = ports.get(source.journal);
3453
4978
  if (!port) {
3454
4979
  port = createTelegramInputCustodyWorkerJournalPort(source.journal);
3455
4980
  ports.set(source.journal, port);
3456
4981
  }
3457
- return { runtimeKey: JSON.stringify({ source: source.runtimeKey, recipientBindingKey }),
4982
+ return {
4983
+ runtimeKey: JSON.stringify({
4984
+ source: source.runtimeKey,
4985
+ recipientBindingKey,
4986
+ }),
3458
4987
  recoveryKey: source.recoveryKey,
3459
- recipientBindingKey, journal: { ...port, appendBatch: source.journal.appendBatch,
4988
+ recipientBindingKey,
4989
+ journal: {
4990
+ ...port,
4991
+ appendBatch: source.journal.appendBatch,
3460
4992
  discardQueued: source.journal.discardQueued,
3461
4993
  recoverDeadQueueOwner: source.journal.recoverDeadQueueOwner,
3462
4994
  offerQueuedHandoff: source.journal.offerQueuedHandoff,
3463
4995
  acceptQueuedHandoff: source.journal.acceptQueuedHandoff,
3464
- cancelQueuedHandoff: source.journal.cancelQueuedHandoff } };
4996
+ cancelQueuedHandoff: source.journal.cancelQueuedHandoff,
4997
+ },
4998
+ };
3465
4999
  };
3466
5000
  }
3467
5001
  export function createTelegramCustodiedExecutionSession(input) {
@@ -3471,10 +5005,12 @@ export function createTelegramCustodiedExecutionSession(input) {
3471
5005
  const receipt = receipts.get(updateId);
3472
5006
  if (!receipt) {
3473
5007
  const duplicate = settledQueues.get(updateId);
3474
- if (duplicate && outcome.kind === "queued" &&
5008
+ if (duplicate &&
5009
+ outcome.kind === "queued" &&
3475
5010
  duplicate.queueReceipt.queueKind === outcome.queueKind &&
3476
5011
  duplicate.queueReceipt.receiptId === outcome.receiptId &&
3477
- duplicate.queueReceipt.sourceUpdateIds.length === outcome.sourceUpdateIds.length &&
5012
+ duplicate.queueReceipt.sourceUpdateIds.length ===
5013
+ outcome.sourceUpdateIds.length &&
3478
5014
  duplicate.queueReceipt.sourceUpdateIds.every((id, index) => id === outcome.sourceUpdateIds[index])) {
3479
5015
  settledQueues.delete(updateId);
3480
5016
  return duplicate;
@@ -3486,12 +5022,18 @@ export function createTelegramCustodiedExecutionSession(input) {
3486
5022
  if (outcome.kind === "queued") {
3487
5023
  if (!outcome.sourceUpdateIds.includes(updateId))
3488
5024
  throw new TelegramUpdateAdmissionOutcomeError("Telegram grouped queue custody omitted the settling input.");
3489
- const grouped = outcome.sourceUpdateIds.map(sourceUpdateId => receipts.get(sourceUpdateId));
3490
- if (grouped.some(candidate => !candidate))
5025
+ const grouped = outcome.sourceUpdateIds.map((sourceUpdateId) => receipts.get(sourceUpdateId));
5026
+ if (grouped.some((candidate) => !candidate))
3491
5027
  throw new TelegramUpdateAdmissionOutcomeError("Telegram grouped queue custody omitted an exact running source receipt.");
3492
- const queued = input.journal.queueInputs({ queueKind: outcome.queueKind,
3493
- receiptId: outcome.receiptId, receipts: grouped });
3494
- const result = { status: "queued", queueReceipt: queued.queueReceipt };
5028
+ const queued = input.journal.queueInputs({
5029
+ queueKind: outcome.queueKind,
5030
+ receiptId: outcome.receiptId,
5031
+ receipts: grouped,
5032
+ });
5033
+ const result = {
5034
+ status: "queued",
5035
+ queueReceipt: queued.queueReceipt,
5036
+ };
3495
5037
  for (const sourceUpdateId of outcome.sourceUpdateIds) {
3496
5038
  receipts.delete(sourceUpdateId);
3497
5039
  if (sourceUpdateId !== updateId)
@@ -3506,8 +5048,11 @@ export function createTelegramCustodiedExecutionSession(input) {
3506
5048
  return {
3507
5049
  settle,
3508
5050
  async execute(update, handler) {
3509
- const acquired = input.journal.acquireInput({ updateId: update.update_id,
3510
- recipientBindingKey: input.recipientBindingKey, executionUpdate: update });
5051
+ const acquired = input.journal.acquireInput({
5052
+ updateId: update.update_id,
5053
+ recipientBindingKey: input.recipientBindingKey,
5054
+ executionUpdate: update,
5055
+ });
3511
5056
  const started = input.journal.startInput(acquired.receipt);
3512
5057
  if (!started.started)
3513
5058
  return { status: "outcome-unknown", receipt: acquired.receipt };
@@ -3535,6 +5080,10 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3535
5080
  if (deps.onLateOutcome && !deps.onLateOutcomeError) {
3536
5081
  throw new Error("Telegram late admission outcomes require a diagnostic error sink.");
3537
5082
  }
5083
+ return createTelegramUpdateSourceAdmissionHandle(deps);
5084
+ }
5085
+ /** Same native source/fence/late-ACK owner, with an internal binding-only callback instead of registry/ordinary dispatch. */
5086
+ function createTelegramUpdateSourceAdmissionHandle(deps, capture) {
3538
5087
  const registry = deps.registry ?? getOrCreateUpdateHandlerRegistry();
3539
5088
  let nextExecutionGeneration = 0;
3540
5089
  return async (update, ctx, signal) => {
@@ -3551,26 +5100,41 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3551
5100
  generation,
3552
5101
  updateId: update.update_id,
3553
5102
  signal,
3554
- isCurrent: () => !signal.aborted && !suspended,
5103
+ isCurrent: () => !signal.aborted &&
5104
+ !suspended &&
5105
+ ((!routingLifetime && !routingClockUnknown) ||
5106
+ immediate ||
5107
+ settlementReported ||
5108
+ deps.isRoutingInputCurrent?.({
5109
+ updateId: update.update_id,
5110
+ signal,
5111
+ }) !== false),
3555
5112
  assertCurrent() {
3556
- if (signal.aborted || suspended) {
5113
+ if (!execution.isCurrent()) {
3557
5114
  throw signal.reason ?? new DOMException("Aborted", "AbortError");
3558
5115
  }
3559
5116
  },
3560
5117
  };
3561
5118
  execution.assertCurrent();
3562
- const verdict = await registry.dispatch(update, execution);
3563
- execution.assertCurrent();
3564
- if (verdict === "consume")
3565
- return { kind: "complete" };
5119
+ if (!capture) {
5120
+ const verdict = await registry.dispatch(update, execution);
5121
+ execution.assertCurrent();
5122
+ if (verdict === "consume")
5123
+ return { kind: "complete" };
5124
+ }
3566
5125
  let immediate = true;
3567
5126
  let settlementReported = false;
3568
5127
  let outcome;
5128
+ let completionAttempted = false;
5129
+ let completedSource;
3569
5130
  const boundUpdate = bindTelegramUpdateExecutionFence(bindTelegramUpdateAdmissionSource(update, (next) => {
3570
- if (suspended || signal.aborted)
5131
+ if (!execution.isCurrent())
3571
5132
  return;
3572
5133
  if (next.kind === "deferred" && next.routingReview) {
3573
- if (!immediate || settlementReported || routingClaims > 0 || !deps.isHistoricalSource?.({ updateId: update.update_id, signal })) {
5134
+ if (!immediate ||
5135
+ settlementReported ||
5136
+ routingClaims > 0 ||
5137
+ !deps.isHistoricalSource?.({ updateId: update.update_id, signal })) {
3574
5138
  throw new TelegramUpdateAdmissionOutcomeError("Historical review cannot override accepted or selected work.");
3575
5139
  }
3576
5140
  suspended = true;
@@ -3584,15 +5148,34 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3584
5148
  if (!deps.onLateOutcome) {
3585
5149
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${update.update_id} reported a late outcome without an owner.`);
3586
5150
  }
5151
+ const expectedSource = next.kind === "complete" &&
5152
+ next.expectedSource &&
5153
+ !completionAttempted
5154
+ ? { ...next.expectedSource }
5155
+ : undefined;
5156
+ if (expectedSource)
5157
+ completionAttempted = true;
5158
+ const reported = expectedSource
5159
+ ? { ...next, expectedSource: { ...expectedSource } }
5160
+ : next;
3587
5161
  void Promise.resolve()
3588
- .then(() => {
3589
- if (suspended || signal.aborted)
5162
+ .then(async () => {
5163
+ if (!execution.isCurrent())
3590
5164
  return;
3591
- return deps.onLateOutcome(next, {
5165
+ const completion = await deps.onLateOutcome(reported, {
3592
5166
  updateId: update.update_id,
3593
5167
  ctx,
3594
5168
  signal,
3595
5169
  });
5170
+ if (expectedSource &&
5171
+ completion &&
5172
+ execution.isCurrent() &&
5173
+ completion.isCurrent() &&
5174
+ isDeepStrictEqual(completion.source, expectedSource))
5175
+ completedSource = {
5176
+ ...completion,
5177
+ source: { ...expectedSource },
5178
+ };
3596
5179
  })
3597
5180
  .catch((error) => {
3598
5181
  try {
@@ -3602,9 +5185,16 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3602
5185
  // Diagnostic sinks must not create an unhandled late Promise.
3603
5186
  }
3604
5187
  });
3605
- }, { abandon: (authority) => {
3606
- const isCurrent = () => !signal.aborted && routingClaims === 0 && !settlementReported && authority.isCurrent();
3607
- if (immediate || outcome?.kind !== "deferred" || !deps.abandonDeferred || !isCurrent())
5188
+ }, {
5189
+ abandon: (authority) => {
5190
+ const isCurrent = () => !signal.aborted &&
5191
+ routingClaims === 0 &&
5192
+ !settlementReported &&
5193
+ authority.isCurrent();
5194
+ if (immediate ||
5195
+ outcome?.kind !== "deferred" ||
5196
+ !deps.abandonDeferred ||
5197
+ !isCurrent())
3608
5198
  return undefined;
3609
5199
  if (abandoned)
3610
5200
  return { ...abandoned, duplicate: true };
@@ -3612,23 +5202,91 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3612
5202
  suspended = true;
3613
5203
  // Failed/unknown publication keeps dispatch suspended for exact cancellation retry.
3614
5204
  // An ineligible request that attempted no publication remains inert.
3615
- const result = deps.abandonDeferred({ ...authority, updateId: update.update_id, signal, isCurrent });
5205
+ const result = deps.abandonDeferred({
5206
+ ...authority,
5207
+ updateId: update.update_id,
5208
+ signal,
5209
+ isCurrent,
5210
+ });
3616
5211
  if (result)
3617
5212
  abandoned = result;
3618
5213
  else
3619
5214
  suspended = wasSuspended;
3620
5215
  return result;
3621
- }, inspectAbandoning: request => execution.isCurrent()
3622
- ? deps.inspectAbandoning?.({ ...request, signal }) : undefined,
3623
- inspectHistorical: request => execution.isCurrent() ? deps.inspectHistorical?.({ ...request, signal }) : undefined,
5216
+ },
5217
+ inspectAbandoning: (request) => execution.isCurrent()
5218
+ ? deps.inspectAbandoning?.({ ...request, signal })
5219
+ : undefined,
5220
+ inspectHistorical: (request) => execution.isCurrent()
5221
+ ? deps.inspectHistorical?.({ ...request, signal })
5222
+ : undefined,
3624
5223
  inspectSource: () => execution.isCurrent() && !immediate && !settlementReported
3625
- ? deps.inspectDeferredSource?.({ updateId: update.update_id, signal }) : undefined,
3626
- isHistorical: matchesOriginal => !signal.aborted && deps.isHistoricalSource?.({ updateId: update.update_id, signal,
3627
- ...(matchesOriginal ? { matchesOriginal } : {}) }) === true,
3628
- isHistoricalReviewHeld: () => !signal.aborted && suspended && outcome?.kind === "deferred" && outcome.routingReview === true,
3629
- supportsAbandonment: journalBindingKey => !signal.aborted && !settlementReported &&
3630
- deps.supportsDeferredAbandonment?.({ updateId: update.update_id, signal, journalBindingKey }) === true,
3631
- armRoutingInput: (operatorUserId, sourceUpdateIds) => {
5224
+ ? deps.inspectDeferredSource?.({
5225
+ updateId: update.update_id,
5226
+ signal,
5227
+ })
5228
+ : undefined,
5229
+ inspectSourceSnapshot: () => execution.isCurrent() && !immediate && !settlementReported
5230
+ ? deps.inspectDeferredSourceSnapshot?.({
5231
+ updateId: update.update_id,
5232
+ signal,
5233
+ })
5234
+ : undefined,
5235
+ inspectCompletion: () => execution.isCurrent() && completedSource?.isCurrent()
5236
+ ? { ...completedSource.source }
5237
+ : undefined,
5238
+ isSourceUnsettled: () => execution.isCurrent() &&
5239
+ !immediate &&
5240
+ !settlementReported &&
5241
+ !suspended,
5242
+ prepareQueueAdmission: () => execution.isCurrent() &&
5243
+ !immediate &&
5244
+ !settlementReported &&
5245
+ !suspended
5246
+ ? deps.prepareDeferredQueueAdmission?.({
5247
+ updateId: update.update_id,
5248
+ signal,
5249
+ })
5250
+ : undefined,
5251
+ prepareSourceCompletion: () => execution.isCurrent() &&
5252
+ !immediate &&
5253
+ !settlementReported &&
5254
+ !suspended
5255
+ ? deps.prepareDeferredSourceCompletion?.({
5256
+ updateId: update.update_id,
5257
+ signal,
5258
+ })
5259
+ : undefined,
5260
+ prepareLiveInput: (sourceUpdateIds, isCurrent) => execution.isCurrent() &&
5261
+ !immediate &&
5262
+ !settlementReported &&
5263
+ !suspended
5264
+ ? deps.prepareDeferredLiveInput?.({
5265
+ updateId: update.update_id,
5266
+ sourceUpdateIds,
5267
+ signal,
5268
+ isCurrent: () => execution.isCurrent() && isCurrent(),
5269
+ })
5270
+ : undefined,
5271
+ isHistorical: (matchesOriginal) => !signal.aborted &&
5272
+ deps.isHistoricalSource?.({
5273
+ updateId: update.update_id,
5274
+ signal,
5275
+ ...(matchesOriginal ? { matchesOriginal } : {}),
5276
+ }) === true,
5277
+ isHistoricalReviewHeld: () => !signal.aborted &&
5278
+ suspended &&
5279
+ outcome?.kind === "deferred" &&
5280
+ outcome.routingReview === true,
5281
+ supportsAbandonment: (journalBindingKey) => !signal.aborted &&
5282
+ !settlementReported &&
5283
+ deps.supportsDeferredAbandonment?.({
5284
+ updateId: update.update_id,
5285
+ signal,
5286
+ journalBindingKey,
5287
+ }) === true,
5288
+ getRoutingInput: () => deps.getRoutingInput?.({ updateId: update.update_id, signal }),
5289
+ armRoutingInput: (operatorUserId, sourceUpdateIds, chooser) => {
3632
5290
  execution.assertCurrent();
3633
5291
  if (settlementReported || routingClaims || suspended)
3634
5292
  return undefined;
@@ -3638,7 +5296,13 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3638
5296
  if (routingSourceIds && !isDeepStrictEqual(ids, routingSourceIds))
3639
5297
  throw new TelegramUpdateAdmissionOutcomeError("Telegram routing input source membership changed.");
3640
5298
  try {
3641
- routingLifetime = deps.armRoutingInput?.({ updateId: update.update_id, signal, operatorUserId, sourceUpdateIds: ids });
5299
+ routingLifetime = deps.armRoutingInput?.({
5300
+ updateId: update.update_id,
5301
+ signal,
5302
+ operatorUserId,
5303
+ sourceUpdateIds: ids,
5304
+ ...(chooser ? { chooser } : {}),
5305
+ });
3642
5306
  }
3643
5307
  catch (error) {
3644
5308
  routingClockUnknown = true;
@@ -3658,8 +5322,13 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3658
5322
  if (!isDeepStrictEqual(sourceUpdateIds ?? [update.update_id], routingSourceIds))
3659
5323
  throw new TelegramUpdateAdmissionOutcomeError("Telegram routing input selection changed its source membership.");
3660
5324
  routingSelectionAttempted = true;
3661
- routingSelectionConfirmed = deps.selectRoutingInput?.({ updateId: update.update_id, signal,
3662
- operatorUserId: routingLifetime.operatorUserId, sourceUpdateIds: routingSourceIds }) === true;
5325
+ routingSelectionConfirmed =
5326
+ deps.selectRoutingInput?.({
5327
+ updateId: update.update_id,
5328
+ signal,
5329
+ operatorUserId: routingLifetime.operatorUserId,
5330
+ sourceUpdateIds: routingSourceIds,
5331
+ }) === true;
3663
5332
  if (!routingSelectionConfirmed)
3664
5333
  throw new TelegramUpdateAdmissionOutcomeError("Telegram routing input choice is expired or unavailable.");
3665
5334
  }
@@ -3671,11 +5340,19 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3671
5340
  released = true;
3672
5341
  routingClaims -= 1;
3673
5342
  };
3674
- } }), execution);
5343
+ },
5344
+ }), execution);
3675
5345
  try {
3676
5346
  execution.assertCurrent();
3677
- await deps.defaultHandle(boundUpdate, ctx, execution);
3678
- if (outcome?.kind !== "deferred" || !outcome.routingReview || signal.aborted)
5347
+ if (capture) {
5348
+ capture(boundUpdate);
5349
+ reportTelegramUpdateDeferred(boundUpdate.message);
5350
+ }
5351
+ else
5352
+ await deps.defaultHandle(boundUpdate, ctx, execution);
5353
+ if (outcome?.kind !== "deferred" ||
5354
+ !outcome.routingReview ||
5355
+ signal.aborted)
3679
5356
  execution.assertCurrent();
3680
5357
  }
3681
5358
  finally {
@@ -3685,47 +5362,63 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3685
5362
  };
3686
5363
  }
3687
5364
  export function createTelegramCustodiedUpdateAdmissionHandle(deps) {
3688
- const session = createTelegramCustodiedExecutionSession({ journal: deps.journal,
3689
- recipientBindingKey: deps.recipientBindingKey });
5365
+ const session = createTelegramCustodiedExecutionSession({
5366
+ journal: deps.journal,
5367
+ recipientBindingKey: deps.recipientBindingKey,
5368
+ });
3690
5369
  const admission = createTelegramUpdateAdmissionHandle({
3691
5370
  defaultHandle: deps.defaultHandle,
3692
5371
  registry: deps.registry,
3693
5372
  async onLateOutcome(outcome, details) {
3694
5373
  const result = await session.settle(details.updateId, outcome);
3695
- deps.onCustodiedLateSettlement?.(result, { updateId: details.updateId, signal: details.signal });
5374
+ deps.onCustodiedLateSettlement?.(result, {
5375
+ updateId: details.updateId,
5376
+ signal: details.signal,
5377
+ });
3696
5378
  },
3697
5379
  onLateOutcomeError: deps.onLateOutcomeError,
3698
5380
  });
3699
- return (update, ctx, signal) => session.execute(update, started => admission(started, ctx, signal));
5381
+ return (update, ctx, signal) => session.execute(update, (started) => admission(started, ctx, signal));
3700
5382
  }
3701
5383
  // A dispatched prompt can remain in memory until agent_start. Remember only acknowledged
3702
5384
  // removal of these exact receipt objects for later local discard, never for dispatch/replay.
3703
5385
  const acknowledgedQueueReceiptCompletions = new WeakMap();
3704
5386
  const isQueueReceiptCompletionAcknowledged = (receipt) => {
3705
5387
  const acknowledged = acknowledgedQueueReceiptCompletions.get(receipt);
3706
- return acknowledged !== undefined && areTelegramQueueAdmissionReceiptsEqual(acknowledged, receipt);
5388
+ return (acknowledged !== undefined &&
5389
+ areTelegramQueueAdmissionReceiptsEqual(acknowledged, receipt));
3707
5390
  };
3708
5391
  export function createTelegramQueueAdmissionSettlementMuxRuntime(runtimes) {
3709
5392
  // Plan the whole request before mutation; unready is not an acknowledgement of completion.
3710
5393
  const complete = (items, ctx, kind) => {
3711
5394
  const plan = new Map();
3712
- const reason = kind === "prompt" ? "prompt-handoff" : kind === "control" ? "control-settlement" : "discard";
3713
- const settlementOwner = (runtime, receipt) => runtime.getQueueReceiptSettlementOwner ? runtime.getQueueReceiptSettlementOwner(receipt, ctx, reason) :
3714
- runtime.isItemReady({ admissionReceipts: [receipt] }) ? runtime.getQueueReceiptOwner(receipt) : undefined;
3715
- for (const receipt of items.flatMap(item => item.admissionReceipts ?? [])) {
5395
+ const reason = kind === "prompt"
5396
+ ? "prompt-handoff"
5397
+ : kind === "control"
5398
+ ? "control-settlement"
5399
+ : "discard";
5400
+ const settlementOwner = (runtime, receipt) => runtime.getQueueReceiptSettlementOwner
5401
+ ? runtime.getQueueReceiptSettlementOwner(receipt, ctx, reason)
5402
+ : runtime.isItemReady({ admissionReceipts: [receipt] })
5403
+ ? runtime.getQueueReceiptOwner(receipt)
5404
+ : undefined;
5405
+ for (const receipt of items.flatMap((item) => item.admissionReceipts ?? [])) {
3716
5406
  if (isQueueReceiptCompletionAcknowledged(receipt))
3717
5407
  continue;
3718
- const candidates = runtimes.filter(runtime => runtime.getQueueReceiptSettlementOwner ? !!settlementOwner(runtime, receipt)
5408
+ const candidates = runtimes.filter((runtime) => runtime.getQueueReceiptSettlementOwner
5409
+ ? !!settlementOwner(runtime, receipt)
3719
5410
  : runtime.isItemReady({ admissionReceipts: [receipt] }));
3720
5411
  const selected = candidates[0];
3721
5412
  if (!selected)
3722
5413
  return false;
3723
5414
  if (candidates.length > 1) {
3724
5415
  const owner = settlementOwner(selected, receipt);
3725
- if (!owner || candidates.some(runtime => {
3726
- const candidate = settlementOwner(runtime, receipt);
3727
- return !candidate || !areTelegramUpdateJournalQueueOwnersEqual(owner, candidate);
3728
- }))
5416
+ if (!owner ||
5417
+ candidates.some((runtime) => {
5418
+ const candidate = settlementOwner(runtime, receipt);
5419
+ return (!candidate ||
5420
+ !areTelegramUpdateJournalQueueOwnersEqual(owner, candidate));
5421
+ }))
3729
5422
  return false;
3730
5423
  }
3731
5424
  const receipts = plan.get(selected) ?? [];
@@ -3734,8 +5427,10 @@ export function createTelegramQueueAdmissionSettlementMuxRuntime(runtimes) {
3734
5427
  }
3735
5428
  for (const [runtime, receipts] of plan) {
3736
5429
  const item = { admissionReceipts: receipts };
3737
- const completed = kind === "discard" ? runtime.onItemsDiscarded([item], ctx)
3738
- : kind === "prompt" ? runtime.onPromptHandedOff(item, ctx)
5430
+ const completed = kind === "discard"
5431
+ ? runtime.onItemsDiscarded([item], ctx)
5432
+ : kind === "prompt"
5433
+ ? runtime.onPromptHandedOff(item, ctx)
3739
5434
  : runtime.onControlSettled(item, ctx);
3740
5435
  if (completed !== true)
3741
5436
  return false;
@@ -3772,12 +5467,17 @@ export function createTelegramQueueAdmissionSettlementRuntime(worker) {
3772
5467
  receipts.push(receipt);
3773
5468
  }
3774
5469
  }
3775
- return receipts.length === 0 || worker.completeQueueReceipts({ receipts, ctx, reason });
5470
+ return (receipts.length === 0 ||
5471
+ worker.completeQueueReceipts({ receipts, ctx, reason }));
3776
5472
  };
3777
5473
  return {
3778
5474
  isItemReady: (item) => (item.admissionReceipts ?? []).every(worker.isQueueReceiptCommitted),
3779
5475
  getQueueReceiptOwner: worker.getQueueReceiptOwner,
3780
- ...(worker.getQueueReceiptSettlementOwner ? { getQueueReceiptSettlementOwner: worker.getQueueReceiptSettlementOwner } : {}),
5476
+ ...(worker.getQueueReceiptSettlementOwner
5477
+ ? {
5478
+ getQueueReceiptSettlementOwner: worker.getQueueReceiptSettlementOwner,
5479
+ }
5480
+ : {}),
3781
5481
  onPromptHandedOff: (item, ctx) => complete([item], ctx, "prompt-handoff"),
3782
5482
  onControlSettled: (item, ctx) => complete([item], ctx, "control-settlement"),
3783
5483
  onItemsDiscarded: (items, ctx) => complete(items, ctx, "discard"),
@@ -3951,8 +5651,7 @@ export function createTelegramQueueHandoffReconciliationRuntimeAssembly(deps) {
3951
5651
  createRecipientJournalBindingKey(recipient) {
3952
5652
  if (!recipient.profileKey || !recipient.sessionId)
3953
5653
  return undefined;
3954
- return deps.createRecipientJournalResolver(recipient.profileKey, recipient.sessionId)()
3955
- ?.recoveryKey;
5654
+ return deps.createRecipientJournalResolver(recipient.profileKey, recipient.sessionId)()?.recoveryKey;
3956
5655
  },
3957
5656
  getQueuedItems: deps.queueStore.getQueuedItems,
3958
5657
  getReceiptOwner(receipt) {
@@ -4006,7 +5705,9 @@ export function createTelegramQueueHandoffReconciler(deps) {
4006
5705
  (followerRegistered && deps.canHandoffWithLeader?.() === false)) {
4007
5706
  return;
4008
5707
  }
4009
- const followers = deps.listFollowers().filter((follower) => follower.instanceId !== deps.donorInstanceId);
5708
+ const followers = deps
5709
+ .listFollowers()
5710
+ .filter((follower) => follower.instanceId !== deps.donorInstanceId);
4010
5711
  if (followers.length === 0)
4011
5712
  return;
4012
5713
  for (const item of [...deps.getQueuedItems()]) {
@@ -4052,7 +5753,12 @@ export function createTelegramQueueHandoffReconciler(deps) {
4052
5753
  stageRemote: async ({ payload: sourcePayload }) => {
4053
5754
  const payload = {
4054
5755
  ...sourcePayload,
4055
- admissionReceipts: [{ ...sourcePayload.admissionReceipts[0], journalBindingKey: recipientJournalBindingKey }],
5756
+ admissionReceipts: [
5757
+ {
5758
+ ...sourcePayload.admissionReceipts[0],
5759
+ journalBindingKey: recipientJournalBindingKey,
5760
+ },
5761
+ ],
4056
5762
  };
4057
5763
  if (followerRegistered) {
4058
5764
  return deps.stageThroughFollower({
@@ -4088,7 +5794,7 @@ export function createTelegramQueueHandoffReconciler(deps) {
4088
5794
  typeof response.result !== "object" ||
4089
5795
  !queueOwner) {
4090
5796
  throw new Error(response.kind === "bus.ack"
4091
- ? response.message ?? "Telegram queue handoff was rejected."
5797
+ ? (response.message ?? "Telegram queue handoff was rejected.")
4092
5798
  : "Telegram queue handoff returned no acknowledgement.");
4093
5799
  }
4094
5800
  return {
@@ -4149,7 +5855,7 @@ export function createTelegramUpdateAdmissionRuntimeBinding(deps) {
4149
5855
  let follower;
4150
5856
  let settlement;
4151
5857
  let inputCustodyBus;
4152
- const getActive = () => deps.isFollowerRegistered() ? follower : leader;
5858
+ const getActive = () => (deps.isFollowerRegistered() ? follower : leader);
4153
5859
  return {
4154
5860
  bind(input) {
4155
5861
  leader = input.leader;
@@ -4211,12 +5917,14 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4211
5917
  let foreignQueueOwnerLiveness;
4212
5918
  let releaseSourceReference;
4213
5919
  let sourceReferenceBinding;
5920
+ let activeContext;
4214
5921
  const withRetainedSourceReference = (operation) => {
4215
5922
  if (!journal || !worker)
4216
5923
  throw new Error("Telegram update admission worker is not active.");
4217
5924
  // Synchronous retained-source access needs a reference, not renewed execution authority.
4218
5925
  const release = !releaseSourceReference && sourceReferenceBinding
4219
- ? deps.acquireSourceReference?.(sourceReferenceBinding) : undefined;
5926
+ ? deps.acquireSourceReference?.(sourceReferenceBinding)
5927
+ : undefined;
4220
5928
  try {
4221
5929
  return operation(journal);
4222
5930
  }
@@ -4224,8 +5932,9 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4224
5932
  release?.();
4225
5933
  }
4226
5934
  };
4227
- const readObservedJournal = () => withRetainedSourceReference(source => source.read());
5935
+ const readObservedJournal = () => withRetainedSourceReference((source) => source.read());
4228
5936
  const stopCurrent = async (forget) => {
5937
+ activeContext = undefined;
4229
5938
  await worker?.stop();
4230
5939
  const release = releaseSourceReference;
4231
5940
  releaseSourceReference = undefined;
@@ -4233,7 +5942,9 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4233
5942
  release?.();
4234
5943
  }
4235
5944
  catch (error) {
4236
- deps.recordRuntimeEvent?.("inbound-worker", error, { phase: "source-reference-release" });
5945
+ deps.recordRuntimeEvent?.("inbound-worker", error, {
5946
+ phase: "source-reference-release",
5947
+ });
4237
5948
  }
4238
5949
  if (!forget)
4239
5950
  return;
@@ -4252,12 +5963,15 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4252
5963
  await stopCurrent(true);
4253
5964
  return;
4254
5965
  }
4255
- if (replace || !worker || activeRuntimeKey !== binding.runtimeKey ||
5966
+ if (replace ||
5967
+ !worker ||
5968
+ activeRuntimeKey !== binding.runtimeKey ||
4256
5969
  workerInputCustody !== binding.journal.inputCustody) {
4257
5970
  const { runtimeKey, recoveryKey } = binding;
4258
5971
  await stopCurrent(true);
4259
5972
  binding = deps.resolveBinding();
4260
- if (!binding || binding.runtimeKey !== runtimeKey ||
5973
+ if (!binding ||
5974
+ binding.runtimeKey !== runtimeKey ||
4261
5975
  binding.recoveryKey !== recoveryKey) {
4262
5976
  throw new Error("Telegram update source binding changed during startup.");
4263
5977
  }
@@ -4356,6 +6070,7 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4356
6070
  }
4357
6071
  }
4358
6072
  worker.start(ctx);
6073
+ activeContext = ctx;
4359
6074
  };
4360
6075
  const runExclusive = (task) => {
4361
6076
  const next = operation.then(task, task);
@@ -4413,7 +6128,7 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4413
6128
  if (!journal || !worker || !journal.cancelQueuedHandoff) {
4414
6129
  throw new Error("Telegram update journal queue handoff cancellation is not available.");
4415
6130
  }
4416
- const result = withRetainedSourceReference(source => source.cancelQueuedHandoff(input));
6131
+ const result = withRetainedSourceReference((source) => source.cancelQueuedHandoff(input));
4417
6132
  worker.signal();
4418
6133
  return result;
4419
6134
  },
@@ -4460,6 +6175,31 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4460
6175
  isTelegramReactionDependencyForQueueItem(entry.update, item)));
4461
6176
  },
4462
6177
  signal: () => worker?.signal(),
6178
+ prepareLiveInput(ctx, sourceUpdateIds, isCurrent) {
6179
+ const expectedWorker = worker, expectedBinding = sourceReferenceBinding, reference = releaseSourceReference;
6180
+ if (!expectedWorker?.prepareLiveInput ||
6181
+ !expectedBinding ||
6182
+ (deps.acquireSourceReference && !reference))
6183
+ return undefined;
6184
+ const current = () => {
6185
+ if (activeContext !== ctx ||
6186
+ worker !== expectedWorker ||
6187
+ sourceReferenceBinding !== expectedBinding ||
6188
+ releaseSourceReference !== reference ||
6189
+ expectedWorker.getState().phase === "stopped" ||
6190
+ isCurrent?.() === false)
6191
+ return false;
6192
+ const binding = deps.resolveBinding();
6193
+ return (binding?.runtimeKey === expectedBinding.runtimeKey &&
6194
+ binding.recoveryKey === expectedBinding.recoveryKey &&
6195
+ binding.journal.inputCustody ===
6196
+ expectedBinding.journal.inputCustody &&
6197
+ binding.hasAuthority?.() !== false);
6198
+ };
6199
+ if (!current())
6200
+ return undefined;
6201
+ return expectedWorker.prepareLiveInput(ctx, sourceUpdateIds, current);
6202
+ },
4463
6203
  getState: () => {
4464
6204
  const state = worker?.getState();
4465
6205
  if (!state)
@@ -4481,19 +6221,25 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4481
6221
  /** Compose source-bound routing and late grouped settlement under one worker. */
4482
6222
  export function createTelegramUpdateAdmissionWorkerRuntime(deps) {
4483
6223
  let worker;
4484
- const executeUpdate = createTelegramUpdateAdmissionHandle({
6224
+ const admissionDeps = {
4485
6225
  defaultHandle: deps.defaultHandle,
4486
6226
  registry: deps.registry,
4487
- abandonDeferred: input => worker?.abandonDeferred?.(input),
4488
- armRoutingInput: input => worker?.armRoutingInput?.(input),
4489
- selectRoutingInput: input => worker?.selectRoutingInput?.(input) === true,
4490
- supportsDeferredAbandonment: input => worker?.supportsDeferredAbandonment?.(input) === true,
4491
- inspectAbandoning: input => worker?.inspectAbandoning?.(input),
4492
- inspectHistorical: input => worker?.inspectHistorical?.(input),
4493
- inspectDeferredSource: input => worker?.inspectDeferredSource?.(input),
4494
- isHistoricalSource: input => worker?.isHistoricalSource?.(input) === true,
6227
+ abandonDeferred: (input) => worker?.abandonDeferred?.(input),
6228
+ armRoutingInput: (input) => worker?.armRoutingInput?.(input),
6229
+ selectRoutingInput: (input) => worker?.selectRoutingInput?.(input) === true,
6230
+ isRoutingInputCurrent: (input) => worker?.isRoutingInputCurrent?.(input) !== false,
6231
+ getRoutingInput: (input) => worker?.getRoutingInput?.(input),
6232
+ supportsDeferredAbandonment: (input) => worker?.supportsDeferredAbandonment?.(input) === true,
6233
+ inspectAbandoning: (input) => worker?.inspectAbandoning?.(input),
6234
+ inspectHistorical: (input) => worker?.inspectHistorical?.(input),
6235
+ inspectDeferredSource: (input) => worker?.inspectDeferredSource?.(input),
6236
+ inspectDeferredSourceSnapshot: (input) => worker?.inspectDeferredSourceSnapshot?.(input),
6237
+ prepareDeferredLiveInput: (input) => worker?.prepareDeferredLiveInput?.(input),
6238
+ prepareDeferredQueueAdmission: (input) => worker?.prepareDeferredQueueAdmission?.(input),
6239
+ prepareDeferredSourceCompletion: (input) => worker?.prepareDeferredSourceCompletion?.(input),
6240
+ isHistoricalSource: (input) => worker?.isHistoricalSource?.(input) === true,
4495
6241
  onLateOutcome(outcome, details) {
4496
- worker?.settleDeferred({
6242
+ return worker?.settleDeferred({
4497
6243
  updateId: details.updateId,
4498
6244
  outcome,
4499
6245
  signal: details.signal,
@@ -4505,24 +6251,54 @@ export function createTelegramUpdateAdmissionWorkerRuntime(deps) {
4505
6251
  updateId,
4506
6252
  });
4507
6253
  },
4508
- });
4509
- const custodyBindingKey = deps.inputCustody ? deps.getRecipientBindingKey?.() : undefined;
6254
+ };
6255
+ const executeUpdate = createTelegramUpdateAdmissionHandle(admissionDeps);
6256
+ const admitPreparedLiveInput = async (update, ctx, signal) => {
6257
+ let carrier;
6258
+ const admit = createTelegramUpdateSourceAdmissionHandle(admissionDeps, (value) => {
6259
+ if (!value.message || typeof value.message !== "object")
6260
+ throw new Error("Prepared live input needs one native message carrier.");
6261
+ carrier = value.message;
6262
+ });
6263
+ const outcome = await admit(deps.prepareUpdateForExecution?.(update) ??
6264
+ update, ctx, signal);
6265
+ return { outcome, carrier };
6266
+ };
6267
+ const custodyBindingKey = deps.inputCustody
6268
+ ? deps.getRecipientBindingKey?.()
6269
+ : undefined;
4510
6270
  if (deps.inputCustody && !custodyBindingKey)
4511
6271
  throw new Error("Telegram custodied admission worker requires one recipient binding key.");
4512
6272
  const executeCustodiedUpdate = deps.inputCustody && custodyBindingKey
4513
6273
  ? createTelegramCustodiedUpdateAdmissionHandle({
4514
- journal: deps.inputCustody, recipientBindingKey: custodyBindingKey,
4515
- defaultHandle: deps.defaultHandle, registry: deps.registry,
6274
+ journal: deps.inputCustody,
6275
+ recipientBindingKey: custodyBindingKey,
6276
+ defaultHandle: deps.defaultHandle,
6277
+ registry: deps.registry,
4516
6278
  onLateOutcomeError(error, updateId) {
4517
- deps.recordRuntimeEvent?.("inbound-worker", error, { phase: "late-custodied-admission-handler", updateId });
6279
+ deps.recordRuntimeEvent?.("inbound-worker", error, {
6280
+ phase: "late-custodied-admission-handler",
6281
+ updateId,
6282
+ });
4518
6283
  },
4519
6284
  onCustodiedLateSettlement(result, details) {
4520
- worker?.settleCustodied({ updateId: details.updateId, result, signal: details.signal });
6285
+ worker?.settleCustodied({
6286
+ updateId: details.updateId,
6287
+ result,
6288
+ signal: details.signal,
6289
+ });
4521
6290
  },
4522
- }) : undefined;
6291
+ })
6292
+ : undefined;
4523
6293
  worker = createTelegramUpdateWorkerRuntime({
4524
6294
  ...deps,
4525
- ...(executeCustodiedUpdate ? { executeCustodiedUpdate: async (update, ctx, signal) => executeCustodiedUpdate(deps.prepareUpdateForExecution?.(update) ?? update, ctx, signal) } : {}),
6295
+ admitPreparedLiveInput,
6296
+ ...(executeCustodiedUpdate
6297
+ ? {
6298
+ executeCustodiedUpdate: async (update, ctx, signal) => executeCustodiedUpdate(deps.prepareUpdateForExecution?.(update) ??
6299
+ update, ctx, signal),
6300
+ }
6301
+ : {}),
4526
6302
  async executeUpdate(update, ctx, signal) {
4527
6303
  const typedUpdate = update;
4528
6304
  try {
@@ -4542,13 +6318,19 @@ export function createTelegramUpdateAdmissionWorkerRuntime(deps) {
4542
6318
  export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4543
6319
  const leader = createTelegramUpdateAdmissionLifecycleRuntime({
4544
6320
  resolveBinding: deps.leader.resolveBinding,
4545
- ...(deps.acquireSourceReference ? { acquireSourceReference: binding => deps.acquireSourceReference("leader", binding) } : {}),
6321
+ ...(deps.acquireSourceReference
6322
+ ? {
6323
+ acquireSourceReference: (binding) => deps.acquireSourceReference("leader", binding),
6324
+ }
6325
+ : {}),
4546
6326
  getQueueOwnerIdentity: deps.worker.getQueueOwnerIdentity,
4547
6327
  createWorker(journal, binding) {
4548
6328
  return createTelegramUpdateAdmissionWorkerRuntime({
4549
6329
  ...deps.worker,
4550
6330
  journal,
4551
- ...(binding.journal.inputCustody ? { inputCustody: binding.journal.inputCustody } : {}),
6331
+ ...(binding.journal.inputCustody
6332
+ ? { inputCustody: binding.journal.inputCustody }
6333
+ : {}),
4552
6334
  getJournalBindingKey: () => binding.recoveryKey,
4553
6335
  getRecipientBindingKey: () => binding.recipientBindingKey,
4554
6336
  hasAuthority: deps.leader.hasAuthority,
@@ -4558,8 +6340,14 @@ export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4558
6340
  });
4559
6341
  const follower = createTelegramUpdateAdmissionLifecycleRuntime({
4560
6342
  getQueueOwnerIdentity: deps.worker.getQueueOwnerIdentity,
4561
- ...(deps.acquireSourceReference ? { acquireSourceReference: binding => deps.acquireSourceReference("follower", binding) } : {}),
4562
- ...(deps.follower.prepareBinding ? { prepareBinding: deps.follower.prepareBinding } : {}),
6343
+ ...(deps.acquireSourceReference
6344
+ ? {
6345
+ acquireSourceReference: (binding) => deps.acquireSourceReference("follower", binding),
6346
+ }
6347
+ : {}),
6348
+ ...(deps.follower.prepareBinding
6349
+ ? { prepareBinding: deps.follower.prepareBinding }
6350
+ : {}),
4563
6351
  resolveBinding() {
4564
6352
  const generation = deps.follower.getGeneration();
4565
6353
  if (!deps.follower.isRegistered() || !generation)
@@ -4580,7 +6368,9 @@ export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4580
6368
  return createTelegramUpdateAdmissionWorkerRuntime({
4581
6369
  ...deps.worker,
4582
6370
  journal,
4583
- ...(binding.journal.inputCustody ? { inputCustody: binding.journal.inputCustody } : {}),
6371
+ ...(binding.journal.inputCustody
6372
+ ? { inputCustody: binding.journal.inputCustody }
6373
+ : {}),
4584
6374
  getJournalBindingKey: () => binding.recoveryKey,
4585
6375
  getRecipientBindingKey: () => binding.recipientBindingKey,
4586
6376
  hasAuthority: () => binding.hasAuthority?.() ?? false,
@@ -4594,8 +6384,11 @@ export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4594
6384
  },
4595
6385
  recordRuntimeEvent: deps.recordRuntimeEvent,
4596
6386
  });
4597
- deps.runtimeBinding.bind({ leader, follower,
4598
- ...(deps.inputCustodyBus ? { inputCustodyBus: deps.inputCustodyBus } : {}) });
6387
+ deps.runtimeBinding.bind({
6388
+ leader,
6389
+ follower,
6390
+ ...(deps.inputCustodyBus ? { inputCustodyBus: deps.inputCustodyBus } : {}),
6391
+ });
4599
6392
  return { leader, follower };
4600
6393
  }
4601
6394
  /** Own queue-owner projection and shared leader/follower worker composition. */