@llblab/pi-telegram 0.52.2 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/AGENTS.md +9 -7
  2. package/BACKLOG.md +1 -18
  3. package/CHANGELOG.md +11 -0
  4. package/README.md +7 -11
  5. package/api/commands.ts +3 -0
  6. package/dist/api/commands.d.ts +1 -1
  7. package/dist/lib/activity-verbosity.d.ts +1 -5
  8. package/dist/lib/activity-verbosity.js +15 -42
  9. package/dist/lib/activity.d.ts +9 -0
  10. package/dist/lib/activity.js +39 -10
  11. package/dist/lib/agent-messages.js +7 -3
  12. package/dist/lib/bindings.d.ts +43 -5
  13. package/dist/lib/bindings.js +163 -20
  14. package/dist/lib/bus-api.d.ts +12 -0
  15. package/dist/lib/bus-api.js +182 -79
  16. package/dist/lib/bus-follower.d.ts +111 -31
  17. package/dist/lib/bus-follower.js +1324 -359
  18. package/dist/lib/bus-leader.d.ts +41 -91
  19. package/dist/lib/bus-leader.js +969 -637
  20. package/dist/lib/bus-transport.js +16 -9
  21. package/dist/lib/bus.d.ts +215 -18
  22. package/dist/lib/bus.js +1048 -327
  23. package/dist/lib/channel-posts.d.ts +5 -0
  24. package/dist/lib/channel-posts.js +434 -261
  25. package/dist/lib/command-templates.d.ts +26 -5
  26. package/dist/lib/command-templates.js +92 -176
  27. package/dist/lib/commands.d.ts +168 -82
  28. package/dist/lib/commands.js +1409 -291
  29. package/dist/lib/config.d.ts +1 -8
  30. package/dist/lib/config.js +34 -76
  31. package/dist/lib/delivery.d.ts +2 -2
  32. package/dist/lib/delivery.js +16 -16
  33. package/dist/lib/extension.js +639 -171
  34. package/dist/lib/generative-app-worker.mjs +19 -7
  35. package/dist/lib/generative-apps.d.ts +0 -5
  36. package/dist/lib/generative-apps.js +61 -49
  37. package/dist/lib/inbound.d.ts +5 -0
  38. package/dist/lib/inbound.js +35 -59
  39. package/dist/lib/journal.d.ts +33 -3
  40. package/dist/lib/journal.js +2063 -782
  41. package/dist/lib/lifecycle.d.ts +2 -6
  42. package/dist/lib/lifecycle.js +75 -58
  43. package/dist/lib/locks.d.ts +29 -3
  44. package/dist/lib/locks.js +289 -108
  45. package/dist/lib/logging.js +50 -14
  46. package/dist/lib/media.d.ts +22 -1
  47. package/dist/lib/media.js +46 -28
  48. package/dist/lib/menu-model.d.ts +19 -5
  49. package/dist/lib/menu-model.js +53 -11
  50. package/dist/lib/menu-queue.d.ts +5 -4
  51. package/dist/lib/menu-queue.js +45 -63
  52. package/dist/lib/menu-settings.d.ts +5 -4
  53. package/dist/lib/menu-settings.js +114 -78
  54. package/dist/lib/menu-status.d.ts +1 -1
  55. package/dist/lib/menu-status.js +8 -25
  56. package/dist/lib/menu-thinking.d.ts +6 -1
  57. package/dist/lib/menu-thinking.js +14 -21
  58. package/dist/lib/menu.d.ts +8 -61
  59. package/dist/lib/menu.js +92 -164
  60. package/dist/lib/model.d.ts +1 -1
  61. package/dist/lib/model.js +2 -16
  62. package/dist/lib/outbound-attachments.d.ts +6 -27
  63. package/dist/lib/outbound-attachments.js +45 -38
  64. package/dist/lib/outbound-buttons.js +22 -16
  65. package/dist/lib/outbound-markup.d.ts +0 -2
  66. package/dist/lib/outbound-markup.js +51 -64
  67. package/dist/lib/outbound-voice.js +6 -3
  68. package/dist/lib/outbound.d.ts +6 -6
  69. package/dist/lib/outbound.js +61 -51
  70. package/dist/lib/ownership.js +6 -2
  71. package/dist/lib/paths.d.ts +5 -0
  72. package/dist/lib/paths.js +37 -21
  73. package/dist/lib/pi.d.ts +2 -0
  74. package/dist/lib/pi.js +8 -1
  75. package/dist/lib/polling.d.ts +21 -37
  76. package/dist/lib/polling.js +59 -50
  77. package/dist/lib/preview.js +39 -19
  78. package/dist/lib/process-identity.d.ts +24 -0
  79. package/dist/lib/process-identity.js +133 -0
  80. package/dist/lib/prompts.js +2 -7
  81. package/dist/lib/queue.d.ts +14 -16
  82. package/dist/lib/queue.js +249 -126
  83. package/dist/lib/recovery.d.ts +2 -83
  84. package/dist/lib/recovery.js +20 -274
  85. package/dist/lib/replies.d.ts +16 -24
  86. package/dist/lib/replies.js +99 -116
  87. package/dist/lib/routing.d.ts +120 -7
  88. package/dist/lib/routing.js +4667 -1152
  89. package/dist/lib/runtime.d.ts +1 -11
  90. package/dist/lib/sections.d.ts +4 -29
  91. package/dist/lib/sections.js +7 -20
  92. package/dist/lib/skills.js +4 -3
  93. package/dist/lib/status.js +86 -57
  94. package/dist/lib/sync.d.ts +1 -3
  95. package/dist/lib/sync.js +67 -43
  96. package/dist/lib/target.d.ts +4 -0
  97. package/dist/lib/target.js +18 -0
  98. package/dist/lib/telegram-api.d.ts +18 -13
  99. package/dist/lib/telegram-api.js +148 -53
  100. package/dist/lib/text-groups.js +4 -39
  101. package/dist/lib/thread-cleanup-manager.d.ts +6 -27
  102. package/dist/lib/thread-cleanup-manager.js +369 -186
  103. package/dist/lib/thread-display.d.ts +1 -2
  104. package/dist/lib/thread-display.js +58 -38
  105. package/dist/lib/thread-naming.d.ts +28 -8
  106. package/dist/lib/thread-naming.js +175 -66
  107. package/dist/lib/thread-reconciler.d.ts +42 -16
  108. package/dist/lib/thread-reconciler.js +90 -50
  109. package/dist/lib/threads.d.ts +82 -20
  110. package/dist/lib/threads.js +2247 -855
  111. package/dist/lib/time-injection.js +1 -2
  112. package/dist/lib/turns.js +1 -19
  113. package/dist/lib/updates.d.ts +169 -80
  114. package/dist/lib/updates.js +2418 -641
  115. package/dist/lib/voice.d.ts +7 -22
  116. package/dist/lib/voice.js +7 -22
  117. package/dist/lib/wire.js +1 -1
  118. package/dist/lib/workspace-admission.d.ts +3 -1
  119. package/dist/lib/workspace-admission.js +425 -211
  120. package/dist/lib/workspace-identity.js +8 -13
  121. package/dist/lib/workspace-retirement.d.ts +1 -1
  122. package/dist/lib/workspace-retirement.js +328 -140
  123. package/dist/lib/workspace-slots.js +8 -4
  124. package/dist/package.json +1 -1
  125. package/docs/architecture.md +31 -29
  126. package/docs/inbound.md +1 -1
  127. package/docs/multi-instance-bus.md +15 -3
  128. package/docs/outbound.md +1 -1
  129. package/docs/public-api.md +29 -1
  130. package/docs/ui-style.md +25 -8
  131. package/lib/activity-verbosity.ts +59 -82
  132. package/lib/activity.ts +146 -102
  133. package/lib/agent-messages.ts +12 -5
  134. package/lib/bindings.ts +389 -82
  135. package/lib/bus-api.ts +263 -102
  136. package/lib/bus-follower.ts +2440 -601
  137. package/lib/bus-leader.ts +2209 -1290
  138. package/lib/bus-transport.ts +46 -12
  139. package/lib/bus.ts +1673 -444
  140. package/lib/channel-posts.ts +848 -327
  141. package/lib/command-templates.ts +152 -265
  142. package/lib/commands.ts +2398 -555
  143. package/lib/config.ts +177 -170
  144. package/lib/delivery.ts +41 -29
  145. package/lib/extension.ts +1206 -433
  146. package/lib/generative-app-worker.mjs +19 -7
  147. package/lib/generative-apps.ts +324 -211
  148. package/lib/inbound.ts +95 -97
  149. package/lib/journal.ts +5311 -1763
  150. package/lib/keyboard.ts +1 -3
  151. package/lib/lifecycle.ts +128 -78
  152. package/lib/locks.ts +677 -203
  153. package/lib/logging.ts +147 -58
  154. package/lib/media.ts +82 -43
  155. package/lib/menu-model.ts +85 -18
  156. package/lib/menu-queue.ts +88 -129
  157. package/lib/menu-settings.ts +260 -155
  158. package/lib/menu-status.ts +20 -60
  159. package/lib/menu-thinking.ts +41 -56
  160. package/lib/menu.ts +112 -285
  161. package/lib/model.ts +8 -22
  162. package/lib/outbound-attachments.ts +156 -125
  163. package/lib/outbound-buttons.ts +76 -49
  164. package/lib/outbound-markup.ts +96 -94
  165. package/lib/outbound-voice.ts +33 -27
  166. package/lib/outbound.ts +125 -125
  167. package/lib/ownership.ts +19 -10
  168. package/lib/paths.ts +148 -49
  169. package/lib/pi.ts +35 -15
  170. package/lib/polling.ts +144 -147
  171. package/lib/preview.ts +93 -40
  172. package/lib/process-identity.ts +164 -0
  173. package/lib/prompts.ts +4 -11
  174. package/lib/queue.ts +511 -300
  175. package/lib/recovery.ts +50 -454
  176. package/lib/rendering.ts +1 -3
  177. package/lib/replies.ts +248 -209
  178. package/lib/routing.ts +8509 -2186
  179. package/lib/runtime.ts +1 -12
  180. package/lib/sections.ts +24 -89
  181. package/lib/setup.ts +1 -3
  182. package/lib/skills.ts +9 -4
  183. package/lib/status.ts +215 -110
  184. package/lib/sync.ts +448 -361
  185. package/lib/target.ts +22 -3
  186. package/lib/telegram-api.ts +302 -127
  187. package/lib/text-groups.ts +5 -39
  188. package/lib/thread-cleanup-manager.ts +833 -331
  189. package/lib/thread-display.ts +213 -97
  190. package/lib/thread-naming.ts +232 -87
  191. package/lib/thread-reconciler.ts +155 -72
  192. package/lib/threads.ts +5318 -1845
  193. package/lib/time-injection.ts +1 -4
  194. package/lib/turns.ts +15 -28
  195. package/lib/updates.ts +5184 -1612
  196. package/lib/voice.ts +7 -35
  197. package/lib/wire.ts +1 -1
  198. package/lib/workspace-admission.ts +996 -416
  199. package/lib/workspace-identity.ts +14 -17
  200. package/lib/workspace-retirement.ts +878 -376
  201. package/lib/workspace-slots.ts +22 -13
  202. package/package.json +1 -1
  203. package/scripts/check-downgrade.mjs +0 -472
  204. package/scripts/measure-workspace.mjs +0 -108
@@ -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,21 +1972,32 @@ 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));
1980
+ const binding = deps.getJournalBindingKey?.(), snapshotIds = new Set(snapshot.entries.map((entry) => entry.updateId));
1704
1981
  // Lost expiry ACKs reconcile the exact held original against its body-free discard tombstone.
1705
- const candidates = [...snapshot.entries, ...[...deferredSources.values()]
1706
- .filter(source => source.journalBindingKey === binding && !snapshotIds.has(source.entry.updateId))
1707
- .map(source => source.entry)];
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 (!lifetime || entry.state !== "pending" || !binding || claims.get(entry.updateId) === "queued" ||
1991
+ if (!lifetime ||
1992
+ entry.state !== "pending" ||
1993
+ !binding ||
1994
+ claims.get(entry.updateId) === "queued" ||
1711
1995
  unsettledExecutionsByUpdateId.get(entry.updateId)?.size)
1712
1996
  continue;
1713
1997
  // Classify the first startup snapshot before constructing any historical expiry carrier.
1714
- if (expectedOwner.startupUpdateIds?.has(entry.updateId) && !claims.has(entry.updateId) && deps.shouldReviewHistoricalInput) {
1998
+ if (expectedOwner.startupUpdateIds?.has(entry.updateId) &&
1999
+ !claims.has(entry.updateId) &&
2000
+ deps.shouldReviewHistoricalInput) {
1715
2001
  nextAtMs = Math.min(nextAtMs ?? Infinity, Math.max(getNowMs() + 1, lifetime.expiresAtMs));
1716
2002
  continue;
1717
2003
  }
@@ -1724,30 +2010,48 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1724
2010
  // Lost clock/selection ACKs may leave only this immutable metadata behind its exact source; reconciliation grants expiry, never another dispatch.
1725
2011
  const previous = existing.entry.routingInput;
1726
2012
  if (!isDeepStrictEqual({ ...existing.entry, routingInput: lifetime }, entry) ||
1727
- (previous && (!isDeepStrictEqual({ ...previous, phase: lifetime.phase }, lifetime) ||
1728
- (previous.phase === "selected" && lifetime.phase !== "selected"))))
2013
+ (previous &&
2014
+ (!isDeepStrictEqual({ ...previous, phase: lifetime.phase }, lifetime) ||
2015
+ (previous.phase === "selected" && lifetime.phase !== "selected"))))
1729
2016
  continue;
1730
2017
  existing.entry = structuredClone(entry);
1731
2018
  }
1732
- const source = existing ?? { entry: structuredClone(entry), journalBindingKey: binding, historical: true };
2019
+ const source = existing ?? {
2020
+ entry: structuredClone(entry),
2021
+ journalBindingKey: binding,
2022
+ historical: true,
2023
+ };
1733
2024
  deferredSources.set(entry.updateId, source);
1734
2025
  if (!claims.has(entry.updateId))
1735
2026
  claims.set(entry.updateId, "historical");
1736
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted &&
1737
- deps.isContextCurrent?.(expectedOwner.ctx) !== false && deps.getJournalBindingKey?.() === binding &&
1738
- deps.hasAuthority(expectedOwner.ctx) && isQueueOwnerIdentityCurrent(expectedOwner) && deferredSources.get(entry.updateId) === source &&
1739
- 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";
1740
2036
  let committed;
1741
2037
  try {
1742
2038
  if (current())
1743
- await expireRoutingObserver({ original: structuredClone(entry), journalBindingKey: binding, isCurrent: current,
2039
+ await expireRoutingObserver({
2040
+ original: structuredClone(entry),
2041
+ journalBindingKey: binding,
2042
+ isCurrent: current,
1744
2043
  expire() {
1745
2044
  if (!current())
1746
2045
  return undefined;
1747
2046
  if (committed)
1748
2047
  return { ...committed, duplicate: true };
1749
2048
  claims.set(entry.updateId, "abandoning");
1750
- 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
+ });
1751
2055
  // Source expiry is terminal before chooser/cleanup awaits; later failures must not retain a deferred/retry source.
1752
2056
  claims.delete(entry.updateId);
1753
2057
  deferredSources.delete(entry.updateId);
@@ -1757,19 +2061,27 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1757
2061
  notifyStateChange();
1758
2062
  changed ||= !committed.duplicate;
1759
2063
  return committed;
1760
- } }, expectedOwner.ctx, expectedOwner.controller.signal);
2064
+ },
2065
+ }, expectedOwner.ctx, expectedOwner.controller.signal);
1761
2066
  }
1762
2067
  catch (error) {
1763
- recordRuntimeEvent(error, { phase: "routing-input-expiry", updateId: entry.updateId });
2068
+ recordRuntimeEvent(error, {
2069
+ phase: "routing-input-expiry",
2070
+ updateId: entry.updateId,
2071
+ });
1764
2072
  }
1765
2073
  if (!committed)
1766
2074
  nextAtMs = Math.min(nextAtMs ?? Infinity, getNowMs() + 60_000);
1767
2075
  }
1768
- if (nextAtMs !== undefined && owner === expectedOwner && !expectedOwner.controller.signal.aborted) {
2076
+ if (nextAtMs !== undefined &&
2077
+ owner === expectedOwner &&
2078
+ !expectedOwner.controller.signal.aborted) {
1769
2079
  const token = {};
1770
2080
  routingTimerToken = token;
1771
2081
  routingTimer = scheduleRetry(() => {
1772
- if (routingTimerToken !== token || owner !== expectedOwner || expectedOwner.controller.signal.aborted)
2082
+ if (routingTimerToken !== token ||
2083
+ owner !== expectedOwner ||
2084
+ expectedOwner.controller.signal.aborted)
1773
2085
  return;
1774
2086
  routingTimer = undefined;
1775
2087
  routingTimerToken = undefined;
@@ -1781,32 +2093,69 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1781
2093
  };
1782
2094
  const mutateRoutingSource = (input, select) => {
1783
2095
  const expectedOwner = owner, ids = [...input.sourceUpdateIds];
1784
- 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))
1785
2100
  return undefined;
1786
- const sources = ids.map(id => deferredSources.get(id)), binding = sources[0]?.journalBindingKey;
1787
- const current = () => !!expectedOwner && owner === expectedOwner && expectedOwner.controller.signal === input.signal && !input.signal.aborted &&
1788
- !!binding && sources.every((source, index) => !!source && deferredSources.get(ids[index]) === source && !source.historical &&
1789
- source.journalBindingKey === binding && claims.get(ids[index]) !== "queued" && claims.get(ids[index]) !== "abandoning") &&
1790
- deps.getJournalBindingKey?.() === binding && deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
1791
- 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);
1792
2117
  if (!current() || !armRouting || !selectRouting || !expireRoutingObserver)
1793
2118
  return undefined;
1794
- const authority = { journalBindingKey: binding, entries: sources.map(source => structuredClone(source.entry)), operatorUserId: input.operatorUserId, isCurrent: current };
1795
- const result = select ? selectRouting(authority) : { issued: false, entries: armRouting({ ...authority, publishedAtMs: getNowMs() }) };
1796
- 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()) {
1797
2138
  throw new Error("Telegram routing input lifetime ACK was not confirmed.");
1798
2139
  }
1799
2140
  const retained = deps.journal.read().entries;
1800
- 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()) {
1801
2143
  throw new Error("Telegram routing input lifetime ACK was not retained.");
1802
2144
  }
1803
- 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
+ });
1804
2149
  pendingSignal = true;
1805
2150
  launchDrain();
1806
- return { issued: result.issued, lifetime: { ...result.entries[0].routingInput } };
2151
+ return {
2152
+ issued: result.issued,
2153
+ lifetime: { ...result.entries[0].routingInput },
2154
+ };
1807
2155
  };
1808
2156
  const drain = async (expectedOwner) => {
1809
- while (owner === expectedOwner && !expectedOwner.controller.signal.aborted) {
2157
+ while (owner === expectedOwner &&
2158
+ !expectedOwner.controller.signal.aborted) {
1810
2159
  const authorityResult = checkAuthority(expectedOwner);
1811
2160
  if (authorityResult)
1812
2161
  return authorityResult;
@@ -1817,7 +2166,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1817
2166
  scheduledRetryAtMs = await refreshJournalState(snapshot, expectedOwner);
1818
2167
  // One complete validated baseline per generation, before batching or execution.
1819
2168
  // Replayed v1 entries do not prove that a prior process never forwarded them.
1820
- expectedOwner.startupUpdateIds ??= new Set(snapshot.entries.map(entry => entry.updateId));
2169
+ expectedOwner.startupUpdateIds ??= new Set(snapshot.entries.map((entry) => entry.updateId));
1821
2170
  }
1822
2171
  catch (error) {
1823
2172
  return blockWithFailure("journal-read", "journal-read", error);
@@ -1834,24 +2183,39 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1834
2183
  let blockedInputCustody;
1835
2184
  delete state.blockedInputCustody;
1836
2185
  for (const candidate of snapshot.entries) {
2186
+ if (livePreparation?.ids.has(candidate.updateId) &&
2187
+ (!livePreparation.released || livePreparation.status?.issued))
2188
+ continue;
1837
2189
  if (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION &&
1838
2190
  (candidate.state === "retry-wait" || candidate.state === "failed")) {
1839
2191
  hasUnavailableInputCustody = true;
1840
- blockedInputCustody ??= { updateId: candidate.updateId, kind: "legacy-retry-state" };
2192
+ blockedInputCustody ??= {
2193
+ updateId: candidate.updateId,
2194
+ kind: "legacy-retry-state",
2195
+ };
1841
2196
  continue;
1842
2197
  }
1843
- if (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION && candidate.inputClaim) {
2198
+ if (snapshot.version === TELEGRAM_UPDATE_JOURNAL_CUSTODY_VERSION &&
2199
+ candidate.inputClaim) {
1844
2200
  if (candidate.inputClaim.phase === "running") {
1845
2201
  hasOutcomeUnknownInput = true;
1846
- if (!blockedInputCustody || blockedInputCustody.kind !== "running-outcome-unknown")
1847
- blockedInputCustody = { updateId: candidate.updateId,
1848
- 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
+ };
1849
2208
  continue;
1850
2209
  }
1851
- if (candidate.inputClaim.handoff || !isTelegramUpdateJournalQueueOwnerProcess(candidate.inputClaim.owner, expectedOwner.queueOwnerIdentity)) {
2210
+ if (candidate.inputClaim.handoff ||
2211
+ !isTelegramUpdateJournalQueueOwnerProcess(candidate.inputClaim.owner, expectedOwner.queueOwnerIdentity)) {
1852
2212
  hasUnavailableInputCustody = true;
1853
- blockedInputCustody ??= { updateId: candidate.updateId,
1854
- 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
+ };
1855
2219
  continue;
1856
2220
  }
1857
2221
  }
@@ -1881,6 +2245,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1881
2245
  const completedUpdateIds = [];
1882
2246
  let snapshotInvalidated = false;
1883
2247
  for (const entry of entries) {
2248
+ if (livePreparation?.ids.has(entry.updateId) &&
2249
+ (!livePreparation.released || livePreparation.status?.issued))
2250
+ continue;
1884
2251
  const priorExecutions = unsettledExecutionsByUpdateId.get(entry.updateId);
1885
2252
  if (priorExecutions?.size) {
1886
2253
  const completionResult = commitCompletedBatch(expectedOwner, completedUpdateIds);
@@ -1899,7 +2266,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1899
2266
  completedUpdateIds.push({ updateId: entry.updateId });
1900
2267
  continue;
1901
2268
  }
1902
- if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) && entry.state === "pending" && deps.journal.inspectPendingRetention) {
2269
+ if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) &&
2270
+ entry.state === "pending" &&
2271
+ deps.journal.inspectPendingRetention) {
1903
2272
  const authorityResult = checkAuthority(expectedOwner, entry.updateId);
1904
2273
  if (authorityResult)
1905
2274
  return authorityResult;
@@ -1910,7 +2279,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1910
2279
  }
1911
2280
  catch (error) {
1912
2281
  unverifiable = true;
1913
- recordRuntimeEvent(error, { phase: "pending-retention-inspection", updateId: entry.updateId });
2282
+ recordRuntimeEvent(error, {
2283
+ phase: "pending-retention-inspection",
2284
+ updateId: entry.updateId,
2285
+ });
1914
2286
  }
1915
2287
  if (retained || unverifiable) {
1916
2288
  // A copy is protection evidence only. A fresh owner must explicitly retry
@@ -1923,19 +2295,28 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1923
2295
  continue;
1924
2296
  }
1925
2297
  }
1926
- if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) && (entry.state === "pending" || entry.state === "retry-wait")) {
2298
+ if (isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) &&
2299
+ (entry.state === "pending" || entry.state === "retry-wait")) {
1927
2300
  const historical = expectedOwner.startupUpdateIds.has(entry.updateId);
2301
+ let revived = false;
1928
2302
  const journalBindingKey = deps.getJournalBindingKey?.();
1929
2303
  // Classifiers and handler projections receive no mutable alias to CAS evidence.
1930
2304
  if (journalBindingKey)
1931
- deferredSources.set(entry.updateId, { entry: structuredClone(entry), journalBindingKey,
1932
- ...(historical ? { historical: true } : {}) });
1933
- 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;
1934
2313
  if (classifyPending) {
1935
2314
  const checkHistoricalAuthority = () => {
1936
- if (owner !== expectedOwner || expectedOwner.controller.signal.aborted)
2315
+ if (owner !== expectedOwner ||
2316
+ expectedOwner.controller.signal.aborted)
1937
2317
  return "aborted";
1938
- if (deps.getJournalBindingKey?.() !== journalBindingKey || deps.isContextCurrent?.(expectedOwner.ctx) === false) {
2318
+ if (deps.getJournalBindingKey?.() !== journalBindingKey ||
2319
+ deps.isContextCurrent?.(expectedOwner.ctx) === false) {
1939
2320
  transition("blocked", entry.updateId, "authority-lost");
1940
2321
  return "blocked";
1941
2322
  }
@@ -1957,12 +2338,18 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1957
2338
  const authorityResult = checkHistoricalAuthority();
1958
2339
  if (authorityResult)
1959
2340
  return authorityResult;
1960
- 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())))))
1961
2347
  return blockWithFailure("execution", "historical-review-classification", new TelegramUpdateAdmissionOutcomeError("Historical classification must return a boolean or retain verdict."), entry.updateId);
1962
2348
  // Classification may await a domain read; accepted/changed source evidence wins.
1963
2349
  try {
1964
2350
  const current = deps.journal.read();
1965
- 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)) {
1966
2353
  snapshotInvalidated = true;
1967
2354
  break;
1968
2355
  }
@@ -1970,34 +2357,96 @@ export function createTelegramUpdateWorkerRuntime(deps) {
1970
2357
  catch (error) {
1971
2358
  return blockWithFailure("journal-read", "historical-source-recheck", error, entry.updateId);
1972
2359
  }
1973
- 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") {
1974
2369
  claims.set(entry.updateId, "retained");
1975
2370
  transition("deferred", entry.updateId);
1976
2371
  continue;
1977
2372
  }
1978
- if (review && historical && spendHistorical && !entry.routingInput && classifyPending === deps.shouldReviewHistoricalInput) {
1979
- 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
+ });
1980
2382
  continue;
1981
2383
  }
1982
- if (review) {
2384
+ if (review === true) {
1983
2385
  claims.set(entry.updateId, "historical");
1984
2386
  transition("deferred", entry.updateId);
1985
2387
  continue;
1986
2388
  }
1987
2389
  }
1988
- if (entry.state === "pending" && entry.routingInput) {
2390
+ if (!revived && entry.state === "pending" && entry.routingInput) {
1989
2391
  claims.set(entry.updateId, "historical");
1990
2392
  transition("deferred", entry.updateId);
1991
2393
  continue;
1992
2394
  }
1993
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
+ }
1994
2422
  clearRetryTimer();
1995
2423
  transition("executing", entry.updateId);
1996
- 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);
1997
2438
  if (execution === TELEGRAM_UPDATE_WORKER_EXECUTION_ABORTED) {
1998
2439
  return "aborted";
1999
2440
  }
2000
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
+ }
2001
2450
  if (deps.executeCustodiedUpdate) {
2002
2451
  transition("blocked", entry.updateId, "execution");
2003
2452
  return "blocked";
@@ -2046,7 +2495,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2046
2495
  return postExecutionAuthority;
2047
2496
  const claimableUpdateIds = new Set([
2048
2497
  entry.updateId,
2049
- ...[...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),
2050
2504
  ]);
2051
2505
  let outcome;
2052
2506
  try {
@@ -2056,11 +2510,37 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2056
2510
  return blockWithFailure("invalid-outcome", "invalid-outcome", error, entry.updateId);
2057
2511
  }
2058
2512
  if (outcome.kind === "deferred") {
2059
- if (outcome.routingReview && !deferredSources.get(entry.updateId)?.historical) {
2513
+ if (outcome.routingReview &&
2514
+ !deferredSources.get(entry.updateId)?.historical) {
2060
2515
  return blockWithFailure("invalid-outcome", "historical-review-source", new TelegramUpdateAdmissionOutcomeError("Historical review needs an exact startup source."), entry.updateId);
2061
2516
  }
2062
2517
  claims.set(entry.updateId, outcome.routingReview ? "historical" : "deferred");
2063
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
+ }
2064
2544
  continue;
2065
2545
  }
2066
2546
  if (outcome.kind === "queued") {
@@ -2075,7 +2555,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2075
2555
  snapshotInvalidated = true;
2076
2556
  break;
2077
2557
  }
2078
- 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
+ });
2079
2564
  }
2080
2565
  const completionResult = commitCompletedBatch(expectedOwner, completedUpdateIds);
2081
2566
  if (completionResult)
@@ -2083,8 +2568,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2083
2568
  if (!snapshotInvalidated && !hasMoreEntries)
2084
2569
  continue;
2085
2570
  await yieldToEventLoop();
2086
- if (owner !== expectedOwner ||
2087
- expectedOwner.controller.signal.aborted) {
2571
+ if (owner !== expectedOwner || expectedOwner.controller.signal.aborted) {
2088
2572
  return "aborted";
2089
2573
  }
2090
2574
  }
@@ -2099,7 +2583,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2099
2583
  try {
2100
2584
  startupKey = deps.getJournalBindingKey?.();
2101
2585
  }
2102
- catch { /* Missing startup identity grants no notification. */ }
2586
+ catch {
2587
+ /* Missing startup identity grants no notification. */
2588
+ }
2103
2589
  }
2104
2590
  let lastResult;
2105
2591
  const run = async () => {
@@ -2120,30 +2606,55 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2120
2606
  if (pendingSignal && !expectedOwner.controller.signal.aborted) {
2121
2607
  launchDrain();
2122
2608
  }
2123
- else if (lastResult === "idle" && startupKey && onHeldSourcesPrepared && !expectedOwner.heldSourcesPreparedIssued) {
2609
+ else if (lastResult === "idle" &&
2610
+ startupKey &&
2611
+ onHeldSourcesPrepared &&
2612
+ !expectedOwner.heldSourcesPreparedIssued) {
2124
2613
  try {
2125
2614
  const ctx = expectedOwner.ctx, key = startupKey;
2126
- const isCurrent = () => owner === expectedOwner && !blocked && !expectedOwner.controller.signal.aborted &&
2127
- 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);
2128
2621
  if (!isCurrent())
2129
2622
  return;
2130
2623
  expectedOwner.heldSourcesPreparedIssued = true;
2131
2624
  // Do not couple lifecycle/stop or the worker drain to a held controller/API reply.
2132
- void Promise.resolve().then(() => isCurrent() ? onHeldSourcesPrepared({ ctx,
2133
- journalBindingKey: key, signal: expectedOwner.controller.signal, isCurrent,
2134
- routingSourceIds: [...deferredSources].filter(([, source]) => source.journalBindingKey === key && !!source.entry.routingInput)
2135
- .map(([id]) => id) }) : 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) => {
2136
2639
  try {
2137
- 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. */
2138
2646
  }
2139
- catch { /* Diagnostic only. */ }
2140
2647
  });
2141
2648
  }
2142
2649
  catch (error) {
2143
2650
  try {
2144
- 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. */
2145
2657
  }
2146
- catch { /* Diagnostic only. */ }
2147
2658
  }
2148
2659
  }
2149
2660
  }
@@ -2162,22 +2673,34 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2162
2673
  const source = deferredSources.get(input.updateId);
2163
2674
  const abandonPending = deps.journal.abandonPending;
2164
2675
  const isCurrent = () => {
2165
- if (!expectedOwner || owner !== expectedOwner || input.signal.aborted ||
2166
- expectedOwner.controller.signal !== input.signal || !source ||
2167
- source.entry.routingInput?.phase === "selected" || (source.historical && historicalSource !== source) ||
2168
- (claims.get(input.updateId) !== "deferred" && claims.get(input.updateId) !== "abandoning" &&
2169
- !(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)))
2170
2687
  return false;
2171
- return input.isCurrent() && deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2172
- 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));
2173
2692
  };
2174
2693
  if (!source || !abandonPending || !isCurrent())
2175
2694
  return undefined;
2176
2695
  // Retain this claim on failure, including unknown commit: never reopen dispatch.
2177
2696
  claims.set(input.updateId, "abandoning");
2178
2697
  try {
2179
- const result = abandonPending({ ...input, entry: source.entry,
2180
- journalBindingKey: source.journalBindingKey, isCurrent });
2698
+ const result = abandonPending({
2699
+ ...input,
2700
+ entry: source.entry,
2701
+ journalBindingKey: source.journalBindingKey,
2702
+ isCurrent,
2703
+ });
2181
2704
  claims.delete(input.updateId);
2182
2705
  deferredSources.delete(input.updateId);
2183
2706
  state.journalEntryCount = result.entryCount;
@@ -2188,7 +2711,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2188
2711
  return result;
2189
2712
  }
2190
2713
  catch (error) {
2191
- recordRuntimeEvent(error, { phase: "pending-abandonment", updateId: input.updateId });
2714
+ recordRuntimeEvent(error, {
2715
+ phase: "pending-abandonment",
2716
+ updateId: input.updateId,
2717
+ });
2192
2718
  updateClaimCounts();
2193
2719
  notifyStateChange();
2194
2720
  throw error;
@@ -2198,34 +2724,93 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2198
2724
  const expectedOwner = owner;
2199
2725
  if (!expectedOwner)
2200
2726
  return undefined;
2201
- const { signal, journalBindingKey, isCurrent: callerIsCurrent, afterUpdateId } = input;
2202
- const isOwnerCurrent = () => owner === expectedOwner && expectedOwner.controller.signal === signal && !signal.aborted;
2203
- const isCurrent = () => isOwnerCurrent() && callerIsCurrent() && deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2204
- deps.getJournalBindingKey?.() === journalBindingKey && deps.hasAuthority(expectedOwner.ctx);
2205
- if (!isCurrent() || !deps.journal.abandonPending || !deps.journal.inspectPendingRetention ||
2206
- (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)))
2207
2741
  return undefined;
2208
- const matches = [...deferredSources].filter(([id, source]) => claims.get(id) === kind &&
2209
- 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))
2210
2746
  .sort(([a], [b]) => a - b);
2211
- 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]) => {
2212
2750
  let committed;
2213
- return { original: structuredClone(source.entry), retry(authority) {
2751
+ return {
2752
+ original: structuredClone(source.entry),
2753
+ retry(authority) {
2214
2754
  const authorized = () => isOwnerCurrent() && authority.isCurrent() && isCurrent();
2215
2755
  if (!authorized())
2216
2756
  return undefined;
2217
2757
  if (committed)
2218
2758
  return { ...committed, duplicate: true };
2219
- 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") &&
2220
2762
  deferredSources.get(updateId) === source;
2221
2763
  if (!stillProtected())
2222
2764
  return undefined;
2223
2765
  committed = abandonSource({ ...authority, updateId, signal, isCurrent: stillProtected }, source);
2224
2766
  return committed;
2225
- } };
2767
+ },
2768
+ };
2226
2769
  });
2227
- return isCurrent() ? { sources,
2228
- ...(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
+ };
2229
2814
  };
2230
2815
  return {
2231
2816
  start(ctx) {
@@ -2247,6 +2832,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2247
2832
  controller: createAbortController(),
2248
2833
  queueOwnerIdentity,
2249
2834
  };
2835
+ livePreparation = undefined;
2836
+ delete state.preparedInputCount;
2250
2837
  claims.clear();
2251
2838
  deferredSources.clear();
2252
2839
  committedQueueReceipts.clear();
@@ -2260,25 +2847,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2260
2847
  state.journalEntryCount = 0;
2261
2848
  state.journalSerializedBytes = 0;
2262
2849
  state.oldestAdmittedAtMs = undefined;
2263
- state.deferredClaimCount = 0;
2264
- delete state.abandoningClaimCount;
2265
- delete state.historicalClaimCount;
2266
- state.queuedClaimCount = 0;
2267
- state.foreignQueuedCount = 0;
2268
- delete state.foreignQueuedOwner;
2269
- state.retryWaitCount = 0;
2270
- state.failedCount = 0;
2271
- state.nextRetryUpdateId = undefined;
2272
- state.nextRetryAtMs = undefined;
2273
- state.nextRetryAttemptCount = undefined;
2274
- state.nextRetryFailureClass = undefined;
2275
- state.failedUpdateId = undefined;
2276
- state.failedFailureId = undefined;
2277
- state.failedAttemptCount = undefined;
2278
- state.failedClass = undefined;
2279
- state.failedSummary = undefined;
2280
- state.terminalFailureAtMs = undefined;
2281
- state.unsettledExecutionCount = unsettledExecutions.size;
2850
+ clearTelegramUpdateWorkerClaimState(state, unsettledExecutions.size);
2282
2851
  state.lastCompletedUpdateId = undefined;
2283
2852
  state.lastCompletedAtMs = undefined;
2284
2853
  state.lastFailureAtMs = undefined;
@@ -2286,6 +2855,401 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2286
2855
  notifyStateChange();
2287
2856
  launchDrain();
2288
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
+ },
2289
3253
  signal() {
2290
3254
  if (!owner || owner.controller.signal.aborted)
2291
3255
  return;
@@ -2293,55 +3257,294 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2293
3257
  pendingSignal = true;
2294
3258
  launchDrain();
2295
3259
  },
2296
- armRoutingInput(input) { return mutateRoutingSource(input, false)?.lifetime; },
2297
- selectRoutingInput(input) { return mutateRoutingSource(input, true)?.issued === true; },
3260
+ armRoutingInput(input) {
3261
+ return mutateRoutingSource(input, false)?.lifetime;
3262
+ },
3263
+ selectRoutingInput(input) {
3264
+ return mutateRoutingSource(input, true)?.issued === true;
3265
+ },
2298
3266
  isRoutingInputCurrent(input) {
2299
- return owner?.controller.signal === input.signal && !input.signal.aborted &&
2300
- deferredSources.has(input.updateId) && claims.get(input.updateId) !== "abandoning";
3267
+ return (owner?.controller.signal === input.signal &&
3268
+ !input.signal.aborted &&
3269
+ deferredSources.has(input.updateId) &&
3270
+ claims.get(input.updateId) !== "abandoning");
2301
3271
  },
2302
- inspectDeferredSource(input) {
2303
- const expectedOwner = owner;
2304
- const source = deferredSources.get(input.updateId);
2305
- const current = () => {
2306
- if (!expectedOwner || owner !== expectedOwner || input.signal.aborted || expectedOwner.controller.signal !== input.signal ||
2307
- !source || deferredSources.get(input.updateId) !== source || claims.get(input.updateId) !== "deferred" ||
2308
- deps.getJournalBindingKey?.() !== source.journalBindingKey || deps.isContextCurrent?.(expectedOwner.ctx) === false ||
2309
- !deps.hasAuthority(expectedOwner.ctx))
2310
- return false;
2311
- return isQueueOwnerIdentityCurrent(expectedOwner);
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,
2312
3323
  };
2313
- 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())
2314
3345
  return undefined;
2315
- const snapshot = deps.journal.read();
2316
- const entry = snapshot.entries.find(value => value.updateId === input.updateId);
2317
- if (!isTelegramUpdateJournalLegacyFamilyVersion(snapshot.version) || !entry ||
2318
- 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
+ });
2319
3355
  return undefined;
2320
- 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
+ });
2321
3500
  },
2322
3501
  isHistoricalSource(input) {
2323
3502
  const source = deferredSources.get(input.updateId);
2324
- const isCurrent = () => owner?.controller.signal === input.signal && !input.signal.aborted &&
2325
- 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;
2326
3507
  if (!isCurrent())
2327
3508
  return false;
2328
3509
  // Domain eligibility observes original evidence, never mutable handler projections.
2329
- 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;
2330
3521
  },
2331
3522
  supportsDeferredAbandonment(input) {
2332
3523
  const expectedOwner = owner;
2333
3524
  const source = deferredSources.get(input.updateId);
2334
- if (!expectedOwner || input.signal.aborted || expectedOwner.controller.signal !== input.signal ||
2335
- !source || source.historical || input.journalBindingKey !== source.journalBindingKey ||
2336
- !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 ||
2337
3533
  claims.get(input.updateId) === "queued")
2338
3534
  return false;
2339
- return deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
2340
- 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");
2341
3547
  },
2342
- abandonDeferred(input) { return abandonSource(input); },
2343
- inspectAbandoning(input) { return inspectRecovery(input, "abandoning"); },
2344
- inspectHistorical(input) { return inspectRecovery(input, "historical"); },
2345
3548
  async settleDeferred(input) {
2346
3549
  const expectedOwner = owner;
2347
3550
  if (!expectedOwner ||
@@ -2359,7 +3562,11 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2359
3562
  return;
2360
3563
  }
2361
3564
  const claim = claims.get(input.updateId);
2362
- 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" ||
2363
3570
  (!claim && routingJournal?.inspectExpiry(input.updateId)))
2364
3571
  return;
2365
3572
  if (input.outcome.kind === "complete") {
@@ -2376,10 +3583,27 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2376
3583
  }
2377
3584
  if (completion.kind !== "complete")
2378
3585
  return;
2379
- const result = commitCompletedBatch(expectedOwner, [{ updateId: input.updateId,
2380
- ...(completion.expectedSource ? { expectedSource: completion.expectedSource } : {}) }]);
2381
- 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) {
2382
3603
  transition("idle", input.updateId);
3604
+ if (source && isCurrent())
3605
+ return { source, isCurrent };
3606
+ }
2383
3607
  return;
2384
3608
  }
2385
3609
  if (input.outcome.kind === "deferred") {
@@ -2394,7 +3618,14 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2394
3618
  }
2395
3619
  let outcome;
2396
3620
  try {
2397
- 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
+ ]));
2398
3629
  }
2399
3630
  catch (error) {
2400
3631
  blockWithFailure("invalid-outcome", "late-invalid-outcome", error, input.updateId);
@@ -2422,7 +3653,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2422
3653
  },
2423
3654
  async settleCustodied(input) {
2424
3655
  const expectedOwner = owner;
2425
- if (!expectedOwner || expectedOwner.controller.signal !== input.signal || input.signal.aborted)
3656
+ if (!expectedOwner ||
3657
+ expectedOwner.controller.signal !== input.signal ||
3658
+ input.signal.aborted)
2426
3659
  return;
2427
3660
  if (checkAuthority(expectedOwner, input.updateId))
2428
3661
  return;
@@ -2446,7 +3679,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2446
3679
  }
2447
3680
  const receipt = normalizeQueueReceipt(input.result.queueReceipt);
2448
3681
  const existing = committedQueueReceipts.get(receipt.receiptId);
2449
- if (existing && areTelegramQueueAdmissionReceiptsEqual(existing.receipt, receipt) &&
3682
+ if (existing &&
3683
+ areTelegramQueueAdmissionReceiptsEqual(existing.receipt, receipt) &&
2450
3684
  areTelegramUpdateJournalQueueOwnersEqual(existing.queueOwner, input.result.queueReceipt.queueOwner))
2451
3685
  return;
2452
3686
  if (existing) {
@@ -2454,7 +3688,7 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2454
3688
  return;
2455
3689
  }
2456
3690
  if (!receipt.sourceUpdateIds.includes(input.updateId) ||
2457
- receipt.sourceUpdateIds.some(updateId => claims.get(updateId) !== "deferred")) {
3691
+ receipt.sourceUpdateIds.some((updateId) => claims.get(updateId) !== "deferred")) {
2458
3692
  blockWithFailure("invalid-outcome", "late-custody-unclaimed", new TelegramUpdateAdmissionOutcomeError(`Telegram update ${input.updateId} reported custodied queue without exact deferred claims.`), input.updateId);
2459
3693
  return;
2460
3694
  }
@@ -2473,21 +3707,29 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2473
3707
  }
2474
3708
  transition("queued", input.updateId);
2475
3709
  },
2476
- isQueueReceiptCommitted: receipt => getCurrentQueueReceipt(receipt) !== undefined,
3710
+ isQueueReceiptCommitted: (receipt) => getCurrentQueueReceipt(receipt) !== undefined,
2477
3711
  getQueueReceiptOwner(receipt) {
2478
3712
  const committed = getCurrentQueueReceipt(receipt);
2479
3713
  return committed ? { ...committed.queueOwner } : undefined;
2480
3714
  },
2481
3715
  getQueueReceiptSettlementOwner(receipt, ctx, reason) {
2482
3716
  const expectedOwner = owner, committed = committedQueueReceipts.get(receipt.receiptId);
2483
- if (!expectedOwner || expectedOwner.controller.signal.aborted ||
2484
- !(deps.isContextCurrent?.(ctx) ?? expectedOwner.ctx === ctx) || !isQueueOwnerIdentityCurrent(expectedOwner) ||
2485
- !committed || !areTelegramQueueAdmissionReceiptsEqual(committed.receipt, normalizeQueueReceipt(receipt)) ||
2486
- (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()) ||
2487
3726
  !isTelegramUpdateJournalQueueOwnerProcess(committed.queueOwner, expectedOwner.queueOwnerIdentity))
2488
3727
  return undefined;
2489
3728
  const attempt = scopedQueueCompletionAttempts.get(receipt.receiptId);
2490
- 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?.()
2491
3733
  : !getCurrentQueueReceipt(receipt))
2492
3734
  return undefined;
2493
3735
  return { ...committed.queueOwner };
@@ -2500,7 +3742,9 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2500
3742
  return false;
2501
3743
  }
2502
3744
  // An ACK permits only local completion reconciliation, never execution readiness.
2503
- 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;
2504
3748
  if (receipts.length === 0)
2505
3749
  return input.sourceCompletions === undefined;
2506
3750
  const normalizedReceipts = receipts.map(normalizeQueueReceipt);
@@ -2528,83 +3772,138 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2528
3772
  }
2529
3773
  }
2530
3774
  const completionBinding = deps.getJournalBindingKey?.();
2531
- const ordinaryObserverBound = normalizedReceipts.every(receipt => committedQueueReceipts.get(receipt.receiptId).receipt.journalBindingKey === completionBinding);
2532
- const ordinaryCompletionCurrent = () => ordinaryObserverBound && owner === expectedOwner &&
2533
- !expectedOwner.controller.signal.aborted && (deps.isContextCurrent?.(input.ctx) ?? expectedOwner.ctx === input.ctx) &&
2534
- 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;
2535
3784
  let removedUpdateIds;
2536
3785
  let scopedCompletionCurrent;
2537
3786
  try {
2538
- const prepared = normalizedReceipts.flatMap(receipt => committedQueueReceipts.get(receipt.receiptId).sourceCompletions ?? []);
2539
- const sourceCompletions = input.sourceCompletions === undefined ? (prepared.length ? prepared : undefined) : input.sourceCompletions;
2540
- 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)))) {
2541
3796
  throw new Error("Telegram queued completion conflicts with its prepared source scopes.");
2542
3797
  }
2543
- if (input.sourceCompletions === undefined && prepared.length && receipts.some(receipt => !requiredScopedQueueReceipts.has(receipt.receiptId))) {
2544
- const scoped = receipts.filter(receipt => requiredScopedQueueReceipts.has(receipt.receiptId));
2545
- 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));
2546
3803
  if (!scoped.length || !ordinary.length)
2547
3804
  throw new Error("Telegram mixed queue completion has incomplete scoped authority.");
2548
3805
  const binding = deps.getJournalBindingKey?.();
2549
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted &&
2550
- (deps.isContextCurrent?.(input.ctx) ?? expectedOwner.ctx === input.ctx) && isQueueOwnerIdentityCurrent(expectedOwner) &&
2551
- !!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;
2552
3813
  // Independent whole receipts need independent ACKs. Ordinary siblings must not acquire invented scopes.
2553
- 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())
2554
3821
  return false;
2555
- 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
+ });
2556
3827
  }
2557
3828
  if (sourceCompletions === undefined) {
2558
- if (queuedCompletions.some(receipt => requiredScopedQueueReceipts.has(receipt.receiptId))) {
3829
+ if (queuedCompletions.some((receipt) => requiredScopedQueueReceipts.has(receipt.receiptId))) {
2559
3830
  throw new Error("Telegram scoped queue completion cannot downgrade required source scopes.");
2560
3831
  }
2561
- removedUpdateIds = deps.journal.completeQueued(queuedCompletions).removedUpdateIds;
3832
+ removedUpdateIds =
3833
+ deps.journal.completeQueued(queuedCompletions).removedUpdateIds;
2562
3834
  }
2563
3835
  else {
2564
3836
  for (const receipt of queuedCompletions)
2565
3837
  requiredScopedQueueReceipts.add(receipt.receiptId);
2566
3838
  queuedCompletions.sort((a, b) => a.receiptId < b.receiptId ? -1 : a.receiptId > b.receiptId ? 1 : 0);
2567
3839
  const journalBindingKey = deps.getJournalBindingKey?.();
2568
- const current = () => owner === expectedOwner && !expectedOwner.controller.signal.aborted &&
2569
- (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) &&
2570
3845
  deps.getJournalBindingKey?.() === journalBindingKey;
2571
- if (!journalBindingKey || !current() || !completeQueuedExact || !inspectCompletion ||
2572
- !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)) {
2573
3853
  throw new Error("Telegram scoped queue completion requires full current source authority.");
2574
3854
  }
2575
3855
  scopedCompletionCurrent = current;
2576
- const completions = normalizeTelegramQueueSourceCompletions(sourceCompletions, sourceUpdateIds, journalBindingKey, input.sourceCompletions !== undefined)
2577
- .map(({ journalBindingKey: _binding, ...completion }) => completion);
2578
- 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)))) {
2579
3858
  throw new Error("Telegram scoped queue completion requires a proven origin for every whole receipt.");
2580
3859
  }
2581
- const previous = queuedCompletions.map(receipt => scopedQueueCompletionAttempts.get(receipt.receiptId));
2582
- const retained = previous.find(attempt => attempt !== undefined);
2583
- if (retained && (previous.some(attempt => attempt !== retained) || retained.owner !== expectedOwner ||
2584
- retained.journalBindingKey !== journalBindingKey || retained.reason !== input.reason ||
2585
- !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))) {
2586
3869
  throw new Error("Telegram scoped queue completion has conflicting issued authority.");
2587
3870
  }
2588
3871
  if (!retained) {
2589
- const attempt = { owner: expectedOwner, journalBindingKey, reason: input.reason,
2590
- receipts: queuedCompletions.map(receipt => ({ ...receipt, sourceUpdateIds: [...receipt.sourceUpdateIds], queueOwner: { ...receipt.queueOwner } })),
2591
- 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
+ };
2592
3883
  if (!current())
2593
3884
  throw new Error("Telegram scoped queue completion authority changed before disposal.");
2594
3885
  // Mark before issuance: an exception may follow a committed rename, and cannot license another disposal.
2595
3886
  for (const receipt of queuedCompletions)
2596
3887
  scopedQueueCompletionAttempts.set(receipt.receiptId, attempt);
2597
- const result = completeQueuedExact(queuedCompletions.map(receipt => ({ ...receipt, sourceUpdateIds: [...receipt.sourceUpdateIds], queueOwner: { ...receipt.queueOwner } })), completions.map(completion => ({ ...completion })));
2598
- 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)) {
2599
3895
  throw new Error("Telegram scoped queue completion ACK was not confirmed.");
2600
3896
  }
2601
3897
  const removed = new Set(result.removedUpdateIds);
2602
- if (removed.size !== sourceUpdateIds.size || [...sourceUpdateIds].some(id => !removed.has(id))) {
3898
+ if (removed.size !== sourceUpdateIds.size ||
3899
+ [...sourceUpdateIds].some((id) => !removed.has(id))) {
2603
3900
  throw new Error("Telegram scoped queue completion did not remove every source.");
2604
3901
  }
2605
3902
  }
2606
3903
  for (const completion of completions) {
2607
- if (!current() || !isDeepStrictEqual(inspectCompletion({ ...completion }), completion) || !current()) {
3904
+ if (!current() ||
3905
+ !isDeepStrictEqual(inspectCompletion({ ...completion }), completion) ||
3906
+ !current()) {
2608
3907
  throw new Error("Telegram scoped queue completion ACK was not retained under current authority.");
2609
3908
  }
2610
3909
  }
@@ -2647,7 +3946,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2647
3946
  observeQueuedCompletion?.(normalizeQueueReceipt(receipt), input.ctx);
2648
3947
  }
2649
3948
  catch (error) {
2650
- recordRuntimeEvent(error, { phase: "queue-receipt-completion-observer", receiptId: receipt.receiptId });
3949
+ recordRuntimeEvent(error, {
3950
+ phase: "queue-receipt-completion-observer",
3951
+ receiptId: receipt.receiptId,
3952
+ });
2651
3953
  }
2652
3954
  }
2653
3955
  // Ordinary queued disposal is source completion too; notify only after a whole positive ACK and fresh origin authority.
@@ -2659,7 +3961,10 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2659
3961
  deps.onUpdateCompleted?.(updateId, input.ctx, completionBinding);
2660
3962
  }
2661
3963
  catch (error) {
2662
- recordRuntimeEvent(error, { phase: "queue-source-completion-observer", updateId });
3964
+ recordRuntimeEvent(error, {
3965
+ phase: "queue-source-completion-observer",
3966
+ updateId,
3967
+ });
2663
3968
  }
2664
3969
  }
2665
3970
  if (!scopedCompletionCurrent || scopedCompletionCurrent()) {
@@ -2680,6 +3985,8 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2680
3985
  if (owner !== expectedOwner)
2681
3986
  return;
2682
3987
  owner = undefined;
3988
+ livePreparation = undefined;
3989
+ delete state.preparedInputCount;
2683
3990
  drainPromise = undefined;
2684
3991
  claims.clear();
2685
3992
  deferredSources.clear();
@@ -2689,30 +3996,12 @@ export function createTelegramUpdateWorkerRuntime(deps) {
2689
3996
  state.phaseStartedAtMs = getNowMs();
2690
3997
  state.currentUpdateId = undefined;
2691
3998
  state.blockedReason = undefined;
2692
- state.deferredClaimCount = 0;
2693
- delete state.abandoningClaimCount;
2694
- delete state.historicalClaimCount;
2695
- state.queuedClaimCount = 0;
2696
- state.foreignQueuedCount = 0;
2697
- delete state.foreignQueuedOwner;
2698
- state.retryWaitCount = 0;
2699
- state.failedCount = 0;
2700
- state.nextRetryUpdateId = undefined;
2701
- state.nextRetryAtMs = undefined;
2702
- state.nextRetryAttemptCount = undefined;
2703
- state.nextRetryFailureClass = undefined;
2704
- state.failedUpdateId = undefined;
2705
- state.failedFailureId = undefined;
2706
- state.failedAttemptCount = undefined;
2707
- state.failedClass = undefined;
2708
- state.failedSummary = undefined;
2709
- state.terminalFailureAtMs = undefined;
2710
- state.unsettledExecutionCount = unsettledExecutions.size;
3999
+ clearTelegramUpdateWorkerClaimState(state, unsettledExecutions.size);
2711
4000
  notifyStateChange();
2712
4001
  },
2713
4002
  async waitForDrain() {
2714
4003
  await (drainPromise ?? Promise.resolve());
2715
- await Promise.allSettled([...pendingQueuePublications.values()].map(value => value.task));
4004
+ await Promise.allSettled([...pendingQueuePublications.values()].map((value) => value.task));
2716
4005
  },
2717
4006
  getState() {
2718
4007
  return getTelegramUpdateWorkerStateSnapshot(state);
@@ -2805,19 +4094,6 @@ function getOrCreateUpdateHandlerRegistry() {
2805
4094
  export function getTelegramUpdateHandlerRegistry() {
2806
4095
  return getOrCreateUpdateHandlerRegistry();
2807
4096
  }
2808
- /**
2809
- * Wrap a default polling `handleUpdate` with the public update handler registry.
2810
- */
2811
- export function createTelegramUpdateHandle(deps) {
2812
- const registry = deps.registry ?? getOrCreateUpdateHandlerRegistry();
2813
- const { defaultHandle } = deps;
2814
- return async (update, ctx) => {
2815
- const verdict = await registry.dispatch(update);
2816
- if (verdict === "consume")
2817
- return;
2818
- await defaultHandle(update, ctx);
2819
- };
2820
- }
2821
4097
  function mergeTelegramReportedAdmissionOutcome(current, next, updateId) {
2822
4098
  if (!current || current.kind === "deferred")
2823
4099
  return next;
@@ -2829,16 +4105,20 @@ function mergeTelegramReportedAdmissionOutcome(current, next, updateId) {
2829
4105
  if (!next.expectedSource)
2830
4106
  return current;
2831
4107
  if (current.expectedSource.updateId === next.expectedSource.updateId &&
2832
- current.expectedSource.journalBindingKey === next.expectedSource.journalBindingKey &&
4108
+ current.expectedSource.journalBindingKey ===
4109
+ next.expectedSource.journalBindingKey &&
2833
4110
  current.expectedSource.sourceSha256 === next.expectedSource.sourceSha256) {
2834
4111
  if (!current.expectedSource.completionSha256)
2835
4112
  return next;
2836
- if (!next.expectedSource.completionSha256 || current.expectedSource.completionSha256 === next.expectedSource.completionSha256)
4113
+ if (!next.expectedSource.completionSha256 ||
4114
+ current.expectedSource.completionSha256 ===
4115
+ next.expectedSource.completionSha256)
2837
4116
  return current;
2838
4117
  }
2839
4118
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${updateId} reported conflicting completion sources.`);
2840
4119
  }
2841
- if (current.kind === "queued" && next.kind === "queued" &&
4120
+ if (current.kind === "queued" &&
4121
+ next.kind === "queued" &&
2842
4122
  areTelegramQueueAdmissionReceiptsEqual(current, next))
2843
4123
  return current;
2844
4124
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${updateId} reported conflicting queue outcomes.`);
@@ -2850,17 +4130,23 @@ export function createTelegramInputCustodyWorkerJournalPort(store) {
2850
4130
  return {
2851
4131
  read: () => store.read(),
2852
4132
  isQueueReceiptCurrent(receipt, owner) {
2853
- const entries = store.read().entries.filter(entry => entry.queueReceiptId === receipt.receiptId);
2854
- 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 &&
2855
4138
  entries.every((entry, index) => entry.updateId === receipt.sourceUpdateIds[index] &&
2856
- entry.state === "queued" && entry.queueKind === receipt.queueKind && !entry.queueHandoff &&
2857
- 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)));
2858
4144
  },
2859
4145
  markQueued: legacyMutation,
2860
4146
  markExecutionFailure: legacyMutation,
2861
4147
  // The common drain batches excluded IDs here; the store rejects all non-vetoed input atomically.
2862
- removeCompleted: updateIds => store.removeExcluded(updateIds),
2863
- completeQueued: receipts => store.completeQueued(receipts),
4148
+ removeCompleted: (updateIds) => store.removeExcluded(updateIds),
4149
+ completeQueued: (receipts) => store.completeQueued(receipts),
2864
4150
  inputCustody: {
2865
4151
  acquireInput: store.acquireInput,
2866
4152
  startInput: store.startInput,
@@ -2873,7 +4159,7 @@ export function createTelegramInputCustodyLegacyDispositionRuntime(deps) {
2873
4159
  const withBinding = (recoveryKey, operation) => {
2874
4160
  if (!recoveryKey)
2875
4161
  throw new TelegramUpdateAdmissionOutcomeError("Telegram legacy custody disposition binding is unavailable.");
2876
- return deps.withBindingReference(recoveryKey, binding => {
4162
+ return deps.withBindingReference(recoveryKey, (binding) => {
2877
4163
  if (binding.recoveryKey !== recoveryKey)
2878
4164
  throw new TelegramUpdateAdmissionOutcomeError("Telegram legacy custody disposition binding is unavailable.");
2879
4165
  return operation(binding.journal);
@@ -2881,10 +4167,10 @@ export function createTelegramInputCustodyLegacyDispositionRuntime(deps) {
2881
4167
  };
2882
4168
  return {
2883
4169
  list(recoveryKey) {
2884
- return withBinding(recoveryKey, journal => journal.listLegacyCustodyCandidates());
4170
+ return withBinding(recoveryKey, (journal) => journal.listLegacyCustodyCandidates());
2885
4171
  },
2886
4172
  apply(recoveryKey, authority) {
2887
- return withBinding(recoveryKey, journal => journal.applyLegacyCustodyDisposition(authority));
4173
+ return withBinding(recoveryKey, (journal) => journal.applyLegacyCustodyDisposition(authority));
2888
4174
  },
2889
4175
  };
2890
4176
  }
@@ -2893,44 +4179,62 @@ export function createTelegramInputCustodyHandoffClient(deps) {
2893
4179
  async transfer(input) {
2894
4180
  const reconciled = deps.resolveAcceptedReference?.({
2895
4181
  sourceUpdateId: input.receipt.updateId,
2896
- recipientBindingKey: input.recipientBindingKey
4182
+ recipientBindingKey: input.recipientBindingKey,
2897
4183
  });
2898
4184
  if (reconciled)
2899
4185
  return { ...reconciled, duplicate: true };
2900
- const offered = deps.journal.offerInputHandoff({ receipt: input.receipt,
2901
- 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
+ });
2902
4191
  const envelope = {
2903
- kind: "leader.offerInputCustodyHandoff", requestId: input.requestId,
4192
+ kind: "leader.offerInputCustodyHandoff",
4193
+ requestId: input.requestId,
2904
4194
  recipientInstanceId: input.recipientInstanceId,
2905
4195
  recipientRegistrationGeneration: input.recipientRegistrationGeneration,
2906
4196
  recipientBindingKey: input.recipientBindingKey,
2907
4197
  sourceRecoveryKey: offered.source.journalBindingKey,
2908
- source: offered.source, handoffId: offered.handoff.handoffId,
2909
- 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 } : {}),
2910
4202
  };
2911
4203
  const response = await deps.sendEnvelope(envelope);
2912
- if (response?.kind !== "bus.ack" || response.requestId !== input.requestId ||
2913
- !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" ||
2914
4209
  Array.isArray(response.result))
2915
4210
  throw new Error("Telegram input custody handoff acknowledgement is missing or rejected.");
2916
4211
  const result = response.result;
2917
4212
  const source = result.source;
2918
4213
  if (result.sourceRecoveryKey !== offered.source.journalBindingKey ||
2919
- !source || typeof source !== "object" || Array.isArray(source) ||
2920
- source.updateId !== offered.source.updateId ||
4214
+ !source ||
4215
+ typeof source !== "object" ||
4216
+ Array.isArray(source) ||
4217
+ source.updateId !==
4218
+ offered.source.updateId ||
2921
4219
  !source.owner ||
2922
4220
  typeof source.owner !== "object" ||
2923
4221
  Array.isArray(source.owner) ||
2924
4222
  typeof source.owner.acquisitionId !== "string" ||
2925
- source.owner.handoffId !== offered.handoff.handoffId ||
4223
+ source.owner
4224
+ .handoffId !== offered.handoff.handoffId ||
2926
4225
  typeof result.duplicate !== "boolean")
2927
4226
  throw new Error("Telegram input custody handoff acknowledgement returned mismatched authority.");
2928
- return { sourceRecoveryKey: result.sourceRecoveryKey,
2929
- source: { updateId: source.updateId, owner: {
4227
+ return {
4228
+ sourceRecoveryKey: result.sourceRecoveryKey,
4229
+ source: {
4230
+ updateId: source.updateId,
4231
+ owner: {
2930
4232
  acquisitionId: source.owner.acquisitionId,
2931
- handoffId: source.owner.handoffId
2932
- } },
2933
- duplicate: result.duplicate };
4233
+ handoffId: source.owner.handoffId,
4234
+ },
4235
+ },
4236
+ duplicate: result.duplicate,
4237
+ };
2934
4238
  },
2935
4239
  };
2936
4240
  }
@@ -2938,56 +4242,88 @@ export function createTelegramInputCustodyHandoffAcceptanceRuntime(deps) {
2938
4242
  return {
2939
4243
  accept(input, ctx) {
2940
4244
  const binding = deps.resolveBinding(input.sourceRecoveryKey);
2941
- if (!binding || binding.recoveryKey !== input.sourceRecoveryKey ||
4245
+ if (!binding ||
4246
+ binding.recoveryKey !== input.sourceRecoveryKey ||
2942
4247
  binding.recipientBindingKey !== input.recipientBindingKey ||
2943
4248
  input.source.journalBindingKey !== input.sourceRecoveryKey)
2944
4249
  throw new Error("Telegram input custody handoff binding is unavailable or changed.");
2945
- const accepted = binding.journal.acceptInputHandoff({ source: input.source,
2946
- 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
+ });
2947
4255
  const acceptedHandoffId = accepted.receipt.owner.handoffId;
2948
4256
  if (!acceptedHandoffId || acceptedHandoffId !== input.handoffId)
2949
4257
  throw new Error("Telegram input custody handoff acceptance returned mismatched authority.");
2950
4258
  binding.signalWorker(ctx);
2951
- return { sourceRecoveryKey: binding.recoveryKey,
2952
- source: { updateId: accepted.receipt.updateId,
2953
- owner: { acquisitionId: accepted.receipt.owner.acquisitionId,
2954
- 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
+ };
2955
4270
  },
2956
4271
  };
2957
4272
  }
2958
4273
  export function createTelegramInputCustodyForwardReferenceResolver(deps) {
2959
- return input => {
4274
+ return (input) => {
2960
4275
  if (input.recipientBindingKey !== deps.recipientBindingKey)
2961
4276
  return undefined;
2962
- 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);
2963
4280
  const claim = entry?.inputClaim;
2964
- if (!entry || entry.state !== "pending" || entry.preApprovalExcluded !== false ||
2965
- !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 ||
2966
4287
  claim.recipientBindingKey !== deps.recipientBindingKey ||
2967
4288
  !claim.owner.handoffId ||
2968
4289
  claim.owner.sessionGeneration !== deps.recipientOwner.sessionGeneration ||
2969
4290
  !isTelegramUpdateJournalQueueOwnerProcess(claim.owner, deps.recipientOwner))
2970
4291
  return undefined;
2971
- return { sourceRecoveryKey: deps.recoveryKey, source: { updateId: entry.updateId,
2972
- owner: { acquisitionId: claim.owner.acquisitionId,
2973
- 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
+ };
2974
4302
  };
2975
4303
  }
2976
4304
  export function createTelegramInputCustodySourceReferenceWakeRuntime(deps) {
2977
4305
  return {
2978
4306
  wakeSource(input, ctx) {
2979
4307
  const binding = deps.resolveBinding(input.sourceRecoveryKey);
2980
- if (!binding || binding.recoveryKey !== input.sourceRecoveryKey ||
4308
+ if (!binding ||
4309
+ binding.recoveryKey !== input.sourceRecoveryKey ||
2981
4310
  binding.recipientBindingKey !== input.recipientBindingKey)
2982
4311
  throw new Error("Telegram follower source-reference binding is unavailable or changed.");
2983
- 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);
2984
4315
  const claim = entry?.inputClaim;
2985
- if (!entry || entry.state !== "pending" || entry.preApprovalExcluded !== false ||
2986
- !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 ||
2987
4322
  claim.recipientBindingKey !== input.recipientBindingKey ||
2988
4323
  claim.owner.acquisitionId !== input.sourceClaim.acquisitionId ||
2989
4324
  claim.owner.handoffId !== input.sourceClaim.handoffId ||
2990
- claim.owner.sessionGeneration !== binding.recipientOwner.sessionGeneration ||
4325
+ claim.owner.sessionGeneration !==
4326
+ binding.recipientOwner.sessionGeneration ||
2991
4327
  !isTelegramUpdateJournalQueueOwnerProcess(claim.owner, binding.recipientOwner))
2992
4328
  throw new Error("Telegram follower source-reference claim is unavailable or changed.");
2993
4329
  binding.signalWorker(ctx);
@@ -2996,10 +4332,10 @@ export function createTelegramInputCustodySourceReferenceWakeRuntime(deps) {
2996
4332
  }
2997
4333
  export function createTelegramInputCustodyBusBindingRuntime(deps) {
2998
4334
  const acceptance = createTelegramInputCustodyHandoffAcceptanceRuntime({
2999
- resolveBinding: deps.resolveBinding
4335
+ resolveBinding: deps.resolveBinding,
3000
4336
  });
3001
4337
  const wake = createTelegramInputCustodySourceReferenceWakeRuntime({
3002
- resolveBinding: deps.resolveBinding
4338
+ resolveBinding: deps.resolveBinding,
3003
4339
  });
3004
4340
  return {
3005
4341
  acceptHandoff: acceptance.accept,
@@ -3029,7 +4365,7 @@ export function evaluateTelegramInputCustodyActivationReadiness(input) {
3029
4365
  return { enabled: false, blocker: "migration-incomplete" };
3030
4366
  if (input.sourceStatus !== "absent" && input.sourceStatus !== "v3")
3031
4367
  return { enabled: false, blocker: "source-unready" };
3032
- if (input.peerReadiness.some(readiness => readiness !== "ready"))
4368
+ if (input.peerReadiness.some((readiness) => readiness !== "ready"))
3033
4369
  return { enabled: false, blocker: "peer-capability-mismatch" };
3034
4370
  return { enabled: true };
3035
4371
  }
@@ -3049,9 +4385,17 @@ export function createTelegramInputCustodyReadinessEvidenceStore(deps) {
3049
4385
  if (!value || typeof value !== "object" || Array.isArray(value))
3050
4386
  throw new Error("Telegram custody readiness evidence is malformed.");
3051
4387
  const snapshot = value;
3052
- if (snapshot.version !== 1 || !Number.isSafeInteger(snapshot.revision) ||
3053
- snapshot.revision < 0 || Object.keys(snapshot).some(key => !["version", "revision", "writerExclusion", "migration", "startupExclusion",
3054
- "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)))
3055
4399
  throw new Error("Telegram custody readiness evidence is malformed.");
3056
4400
  const validate = (evidence, kind) => {
3057
4401
  if (evidence === undefined)
@@ -3059,61 +4403,104 @@ export function createTelegramInputCustodyReadinessEvidenceStore(deps) {
3059
4403
  if (!evidence || typeof evidence !== "object" || Array.isArray(evidence))
3060
4404
  throw new Error("Telegram custody readiness evidence is malformed.");
3061
4405
  const record = evidence;
3062
- const statuses = kind === "writer" ? ["excluded", "present", "unknown"]
4406
+ const statuses = kind === "writer"
4407
+ ? ["excluded", "present", "unknown"]
3063
4408
  : ["complete", "incomplete", "unknown"];
3064
- const writerKeys = ["startupAuthorityId", "closureOperationId", "writerInventorySha256"];
3065
- const migrationKeys = ["migrationAuthorityId", "startupAuthorityId", "closureOperationId",
3066
- "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
+ ];
3067
4421
  const linkedKeys = kind === "writer" ? writerKeys : migrationKeys;
3068
- const suppliedLinkedKeys = linkedKeys.filter(key => record[key] !== undefined);
3069
- const linkedMalformed = suppliedLinkedKeys.length === linkedKeys.length && (typeof record.startupAuthorityId !== "string" || !record.startupAuthorityId ||
3070
- typeof record.closureOperationId !== "string" || !record.closureOperationId ||
3071
- (kind === "writer"
3072
- ? typeof record.writerInventorySha256 !== "string" ||
3073
- !/^[a-f0-9]{64}$/u.test(record.writerInventorySha256)
3074
- : typeof record.migrationAuthorityId !== "string" || !record.migrationAuthorityId ||
3075
- typeof record.migrationInventorySha256 !== "string" ||
3076
- !/^[a-f0-9]{64}$/u.test(record.migrationInventorySha256) ||
3077
- (record.resultingSourceFamily !== "absent" && record.resultingSourceFamily !== "v3")));
3078
- if (record.version !== 1 || typeof record.profileKey !== "string" || !record.profileKey ||
3079
- typeof record.recoveryKey !== "string" || !record.recoveryKey ||
3080
- !statuses.includes(record.status) || Object.keys(record).some(key => !["version", "profileKey", "recoveryKey", "status", ...linkedKeys].includes(key)) ||
3081
- (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) ||
3082
4452
  linkedMalformed)
3083
4453
  throw new Error("Telegram custody readiness evidence is malformed.");
3084
- return { version: 1, profileKey: record.profileKey,
3085
- recoveryKey: record.recoveryKey, status: record.status,
3086
- ...(suppliedLinkedKeys.length === linkedKeys.length ? kind === "writer" ? {
3087
- startupAuthorityId: record.startupAuthorityId,
3088
- closureOperationId: record.closureOperationId,
3089
- writerInventorySha256: record.writerInventorySha256,
3090
- } : {
3091
- migrationAuthorityId: record.migrationAuthorityId,
3092
- startupAuthorityId: record.startupAuthorityId,
3093
- closureOperationId: record.closureOperationId,
3094
- migrationInventorySha256: record.migrationInventorySha256,
3095
- resultingSourceFamily: record.resultingSourceFamily,
3096
- } : {}) };
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
+ };
3097
4475
  };
3098
4476
  const writerExclusion = validate(snapshot.writerExclusion, "writer");
3099
4477
  const migration = validate(snapshot.migration, "migration");
3100
4478
  let startupExclusion;
3101
4479
  if (snapshot.startupExclusion !== undefined) {
3102
4480
  const candidate = snapshot.startupExclusion;
3103
- if (!candidate || typeof candidate.profileKey !== "string" ||
4481
+ if (!candidate ||
4482
+ typeof candidate.profileKey !== "string" ||
3104
4483
  typeof candidate.recoveryKey !== "string")
3105
4484
  throw new Error("Telegram custody readiness evidence is malformed.");
3106
- startupExclusion = normalizeTelegramInputCustodyStartupExclusionAuthority(candidate, { profileKey: candidate.profileKey, recoveryKey: candidate.recoveryKey });
4485
+ startupExclusion = normalizeTelegramInputCustodyStartupExclusionAuthority(candidate, {
4486
+ profileKey: candidate.profileKey,
4487
+ recoveryKey: candidate.recoveryKey,
4488
+ });
3107
4489
  if (!startupExclusion)
3108
4490
  throw new Error("Telegram custody readiness evidence is malformed.");
3109
4491
  }
3110
4492
  let migrationCompletion;
3111
4493
  if (snapshot.migrationCompletion !== undefined) {
3112
4494
  const candidate = snapshot.migrationCompletion;
3113
- if (!candidate || typeof candidate.profileKey !== "string" ||
4495
+ if (!candidate ||
4496
+ typeof candidate.profileKey !== "string" ||
3114
4497
  typeof candidate.recoveryKey !== "string")
3115
4498
  throw new Error("Telegram custody readiness evidence is malformed.");
3116
- migrationCompletion = normalizeTelegramInputCustodyMigrationCompletionAuthority(candidate, { profileKey: candidate.profileKey, recoveryKey: candidate.recoveryKey });
4499
+ migrationCompletion =
4500
+ normalizeTelegramInputCustodyMigrationCompletionAuthority(candidate, {
4501
+ profileKey: candidate.profileKey,
4502
+ recoveryKey: candidate.recoveryKey,
4503
+ });
3117
4504
  if (!migrationCompletion)
3118
4505
  throw new Error("Telegram custody readiness evidence is malformed.");
3119
4506
  }
@@ -3126,48 +4513,72 @@ export function createTelegramInputCustodyReadinessEvidenceStore(deps) {
3126
4513
  identities.push(startupExclusion);
3127
4514
  if (migrationCompletion)
3128
4515
  identities.push(migrationCompletion);
3129
- if (identities.some(evidence => evidence.profileKey !== identities[0]?.profileKey ||
4516
+ if (identities.some((evidence) => evidence.profileKey !== identities[0]?.profileKey ||
3130
4517
  evidence.recoveryKey !== identities[0]?.recoveryKey))
3131
4518
  throw new Error("Telegram custody readiness evidence has conflicting identities.");
3132
- if (writerExclusion?.startupAuthorityId && startupExclusion &&
4519
+ if (writerExclusion?.startupAuthorityId &&
4520
+ startupExclusion &&
3133
4521
  (writerExclusion.startupAuthorityId !== startupExclusion.authorityId ||
3134
- writerExclusion.closureOperationId !== startupExclusion.closureOperationId ||
3135
- writerExclusion.writerInventorySha256 !== startupExclusion.writerInventorySha256))
4522
+ writerExclusion.closureOperationId !==
4523
+ startupExclusion.closureOperationId ||
4524
+ writerExclusion.writerInventorySha256 !==
4525
+ startupExclusion.writerInventorySha256))
3136
4526
  throw new Error("Telegram custody readiness evidence has conflicting authorities.");
3137
- if (migration?.migrationAuthorityId && migrationCompletion &&
4527
+ if (migration?.migrationAuthorityId &&
4528
+ migrationCompletion &&
3138
4529
  (migration.migrationAuthorityId !== migrationCompletion.authorityId ||
3139
- migration.startupAuthorityId !== migrationCompletion.startupAuthorityId ||
3140
- migration.closureOperationId !== migrationCompletion.closureOperationId ||
3141
- migration.migrationInventorySha256 !== migrationCompletion.migrationInventorySha256 ||
3142
- 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))
3143
4538
  throw new Error("Telegram custody readiness evidence has conflicting migration authorities.");
3144
- if (migrationCompletion && startupExclusion &&
3145
- (migrationCompletion.startupAuthorityId !== startupExclusion.authorityId ||
3146
- migrationCompletion.closureOperationId !== startupExclusion.closureOperationId))
4539
+ if (migrationCompletion &&
4540
+ startupExclusion &&
4541
+ (migrationCompletion.startupAuthorityId !==
4542
+ startupExclusion.authorityId ||
4543
+ migrationCompletion.closureOperationId !==
4544
+ startupExclusion.closureOperationId))
3147
4545
  throw new Error("Telegram custody readiness evidence has conflicting cutover authorities.");
3148
- return { version: 1, revision: snapshot.revision,
4546
+ return {
4547
+ version: 1,
4548
+ revision: snapshot.revision,
3149
4549
  ...(writerExclusion ? { writerExclusion } : {}),
3150
4550
  ...(migration ? { migration } : {}),
3151
4551
  ...(startupExclusion ? { startupExclusion } : {}),
3152
- ...(migrationCompletion ? { migrationCompletion } : {}) };
4552
+ ...(migrationCompletion ? { migrationCompletion } : {}),
4553
+ };
3153
4554
  };
3154
4555
  const read = () => decode(deps.readRetained());
3155
4556
  const publish = (input) => deps.withSerialization(() => {
3156
4557
  const current = read();
3157
4558
  if (current.revision !== input.expectedRevision)
3158
4559
  throw new Error("Telegram custody readiness evidence revision changed.");
3159
- const next = { ...current, revision: current.revision + 1,
3160
- ...(input.kind === "writer-exclusion" ? { writerExclusion: input.evidence } :
3161
- input.kind === "migration" ? { migration: input.evidence } :
3162
- input.kind === "startup-exclusion" ? { startupExclusion: input.evidence } :
3163
- { 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
+ };
3164
4571
  const serialized = `${JSON.stringify(next)}\n`;
3165
4572
  const validated = decode(serialized);
3166
4573
  const normalizedEvidence = input.kind === "writer-exclusion"
3167
- ? validated.writerExclusion : input.kind === "migration"
3168
- ? validated.migration : input.kind === "startup-exclusion"
3169
- ? validated.startupExclusion : validated.migrationCompletion;
3170
- 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))
3171
4582
  throw new Error("Telegram custody readiness evidence publication is unauthorized.");
3172
4583
  deps.publishRetained(serialized);
3173
4584
  return validated;
@@ -3178,23 +4589,45 @@ export function normalizeTelegramInputCustodyStartupExclusionAuthority(value, ex
3178
4589
  if (!value || typeof value !== "object" || Array.isArray(value))
3179
4590
  return undefined;
3180
4591
  const record = value;
3181
- const keys = ["version", "status", "authorityId", "closureOperationId", "profileKey",
3182
- "recoveryKey", "writerInventorySha256", "allowedWriterProtocol", "authorizedAtMs"];
3183
- 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 ||
3184
4605
  (record.status !== "enforced" && record.status !== "revoked") ||
3185
- record.profileKey !== expected.profileKey || record.recoveryKey !== expected.recoveryKey ||
3186
- typeof record.authorityId !== "string" || !record.authorityId || record.authorityId.length > 512 ||
3187
- 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 ||
3188
4613
  record.closureOperationId.length > 512 ||
3189
4614
  typeof record.writerInventorySha256 !== "string" ||
3190
4615
  !/^[a-f0-9]{64}$/u.test(record.writerInventorySha256) ||
3191
4616
  record.allowedWriterProtocol !== "custody-v3" ||
3192
- !Number.isSafeInteger(record.authorizedAtMs) || record.authorizedAtMs < 0)
4617
+ !Number.isSafeInteger(record.authorizedAtMs) ||
4618
+ record.authorizedAtMs < 0)
3193
4619
  return undefined;
3194
- return { version: 1, status: record.status, authorityId: record.authorityId,
3195
- closureOperationId: record.closureOperationId, profileKey: expected.profileKey,
3196
- recoveryKey: expected.recoveryKey, writerInventorySha256: record.writerInventorySha256,
3197
- 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
+ };
3198
4631
  }
3199
4632
  export function evaluateTelegramInputCustodyWriterExclusionEvidence(input) {
3200
4633
  const identity = { ...input.expected };
@@ -3203,14 +4636,17 @@ export function evaluateTelegramInputCustodyWriterExclusionEvidence(input) {
3203
4636
  input.inventory.profileKey !== input.expected.profileKey ||
3204
4637
  input.inventory.recoveryKey !== input.expected.recoveryKey ||
3205
4638
  !/^[a-f0-9]{64}$/u.test(input.inventory.writerInventorySha256) ||
3206
- !authority || authority.status !== "enforced" ||
4639
+ !authority ||
4640
+ authority.status !== "enforced" ||
3207
4641
  authority.profileKey !== input.expected.profileKey ||
3208
4642
  authority.recoveryKey !== input.expected.recoveryKey ||
3209
4643
  authority.writerInventorySha256 !== input.inventory.writerInventorySha256)
3210
4644
  return { version: 1, status: "unknown", ...identity };
3211
- const authorityBinding = { startupAuthorityId: authority.authorityId,
4645
+ const authorityBinding = {
4646
+ startupAuthorityId: authority.authorityId,
3212
4647
  closureOperationId: authority.closureOperationId,
3213
- writerInventorySha256: authority.writerInventorySha256 };
4648
+ writerInventorySha256: authority.writerInventorySha256,
4649
+ };
3214
4650
  let unknown = false;
3215
4651
  for (const writer of input.inventory.writers) {
3216
4652
  let liveness;
@@ -3221,49 +4657,82 @@ export function evaluateTelegramInputCustodyWriterExclusionEvidence(input) {
3221
4657
  liveness = "unverifiable";
3222
4658
  }
3223
4659
  if (liveness === "alive")
3224
- return { version: 1, status: "present", ...identity,
3225
- ...authorityBinding };
4660
+ return {
4661
+ version: 1,
4662
+ status: "present",
4663
+ ...identity,
4664
+ ...authorityBinding,
4665
+ };
3226
4666
  if (liveness !== "dead")
3227
4667
  unknown = true;
3228
4668
  }
3229
- return { version: 1, status: unknown ? "unknown" : "excluded", ...identity,
3230
- ...authorityBinding };
4669
+ return {
4670
+ version: 1,
4671
+ status: unknown ? "unknown" : "excluded",
4672
+ ...identity,
4673
+ ...authorityBinding,
4674
+ };
3231
4675
  }
3232
4676
  export function normalizeTelegramInputCustodyMigrationCompletionAuthority(value, expected) {
3233
4677
  if (!value || typeof value !== "object" || Array.isArray(value))
3234
4678
  return undefined;
3235
4679
  const record = value;
3236
- const keys = ["version", "status", "authorityId", "startupAuthorityId",
3237
- "closureOperationId", "profileKey", "recoveryKey", "migrationInventorySha256",
3238
- "resultingSourceFamily", "authorizedAtMs"];
3239
- 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 ||
3240
4694
  (record.status !== "authorized" && record.status !== "revoked") ||
3241
- record.profileKey !== expected.profileKey || record.recoveryKey !== expected.recoveryKey ||
3242
- typeof record.authorityId !== "string" || !record.authorityId || record.authorityId.length > 512 ||
3243
- 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 ||
3244
4702
  record.startupAuthorityId.length > 512 ||
3245
- typeof record.closureOperationId !== "string" || !record.closureOperationId ||
4703
+ typeof record.closureOperationId !== "string" ||
4704
+ !record.closureOperationId ||
3246
4705
  record.closureOperationId.length > 512 ||
3247
4706
  typeof record.migrationInventorySha256 !== "string" ||
3248
4707
  !/^[a-f0-9]{64}$/u.test(record.migrationInventorySha256) ||
3249
- (record.resultingSourceFamily !== "absent" && record.resultingSourceFamily !== "v3") ||
3250
- !Number.isSafeInteger(record.authorizedAtMs) || record.authorizedAtMs < 0)
4708
+ (record.resultingSourceFamily !== "absent" &&
4709
+ record.resultingSourceFamily !== "v3") ||
4710
+ !Number.isSafeInteger(record.authorizedAtMs) ||
4711
+ record.authorizedAtMs < 0)
3251
4712
  return undefined;
3252
- return { version: 1, status: record.status, authorityId: record.authorityId,
4713
+ return {
4714
+ version: 1,
4715
+ status: record.status,
4716
+ authorityId: record.authorityId,
3253
4717
  startupAuthorityId: record.startupAuthorityId,
3254
- closureOperationId: record.closureOperationId, profileKey: expected.profileKey,
4718
+ closureOperationId: record.closureOperationId,
4719
+ profileKey: expected.profileKey,
3255
4720
  recoveryKey: expected.recoveryKey,
3256
4721
  migrationInventorySha256: record.migrationInventorySha256,
3257
4722
  resultingSourceFamily: record.resultingSourceFamily,
3258
- authorizedAtMs: record.authorizedAtMs };
4723
+ authorizedAtMs: record.authorizedAtMs,
4724
+ };
3259
4725
  }
3260
4726
  export function executeTelegramInputCustodyWriterCutover(input) {
3261
- 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 &&
3262
4730
  candidate.profileKey === input.expected.profileKey &&
3263
4731
  candidate.recoveryKey === input.expected.recoveryKey &&
3264
4732
  candidate.authorityId === input.startupAuthority.authorityId &&
3265
4733
  candidate.closureOperationId === input.closure.operationId &&
3266
- candidate.writerInventorySha256 === input.startupAuthority.writerInventorySha256 &&
4734
+ candidate.writerInventorySha256 ===
4735
+ input.startupAuthority.writerInventorySha256 &&
3267
4736
  candidate.allowedWriterProtocol === "custody-v3" &&
3268
4737
  candidate.authorizedAtMs === input.startupAuthority.authorizedAtMs);
3269
4738
  if (input.closure.profileKey !== input.expected.profileKey ||
@@ -3271,8 +4740,10 @@ export function executeTelegramInputCustodyWriterCutover(input) {
3271
4740
  !matchesAuthority(input.evidenceStore.read().startupExclusion))
3272
4741
  return { kind: "blocked", blocker: "startup-authority" };
3273
4742
  const evidence = evaluateTelegramInputCustodyWriterExclusionEvidence({
3274
- expected: input.expected, inventory: input.inventory,
3275
- startupAuthority: input.startupAuthority, getProcessLiveness: input.getProcessLiveness
4743
+ expected: input.expected,
4744
+ inventory: input.inventory,
4745
+ startupAuthority: input.startupAuthority,
4746
+ getProcessLiveness: input.getProcessLiveness,
3276
4747
  });
3277
4748
  if (evidence.status !== "excluded")
3278
4749
  return { kind: "blocked", blocker: "writer-inventory", evidence };
@@ -3291,30 +4762,43 @@ export function executeTelegramInputCustodyWriterCutover(input) {
3291
4762
  if (!matchesAuthority(current.startupExclusion))
3292
4763
  return { kind: "blocked", blocker: "startup-authority", evidence };
3293
4764
  const existing = current.writerExclusion;
3294
- if (existing?.status === "excluded" && existing.profileKey === evidence.profileKey &&
4765
+ if (existing?.status === "excluded" &&
4766
+ existing.profileKey === evidence.profileKey &&
3295
4767
  existing.recoveryKey === evidence.recoveryKey &&
3296
4768
  existing.startupAuthorityId === evidence.startupAuthorityId &&
3297
4769
  existing.closureOperationId === evidence.closureOperationId &&
3298
4770
  existing.writerInventorySha256 === evidence.writerInventorySha256)
3299
- return { kind: "completed", mode: installed.mode, evidence: existing,
3300
- resumed: installed.resumed };
3301
- input.evidenceStore.publish({ expectedRevision: current.revision,
3302
- 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
+ });
3303
4782
  return { kind: "completed", mode: installed.mode, evidence, resumed: false };
3304
4783
  }
3305
4784
  function matchesTelegramInputCustodyActivationEvidence(evidence, expected) {
3306
- return Boolean(evidence && evidence.profileKey === expected.profileKey &&
4785
+ return Boolean(evidence &&
4786
+ evidence.profileKey === expected.profileKey &&
3307
4787
  evidence.recoveryKey === expected.recoveryKey);
3308
4788
  }
3309
4789
  export function executeTelegramInputCustodyMigrationCompletion(input) {
3310
- 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" &&
3311
4793
  candidate.profileKey === input.expected.profileKey &&
3312
4794
  candidate.recoveryKey === input.expected.recoveryKey &&
3313
4795
  candidate.authorityId === input.authority.authorityId &&
3314
4796
  candidate.startupAuthorityId === input.authority.startupAuthorityId &&
3315
4797
  candidate.closureOperationId === input.authority.closureOperationId &&
3316
- candidate.migrationInventorySha256 === input.authority.migrationInventorySha256 &&
3317
- candidate.resultingSourceFamily === input.authority.resultingSourceFamily &&
4798
+ candidate.migrationInventorySha256 ===
4799
+ input.authority.migrationInventorySha256 &&
4800
+ candidate.resultingSourceFamily ===
4801
+ input.authority.resultingSourceFamily &&
3318
4802
  candidate.authorizedAtMs === input.authority.authorizedAtMs);
3319
4803
  const current = input.evidenceStore.read();
3320
4804
  if (!matchesAuthority(current.migrationCompletion))
@@ -3330,15 +4814,19 @@ export function executeTelegramInputCustodyMigrationCompletion(input) {
3330
4814
  }
3331
4815
  if (source !== input.authority.resultingSourceFamily)
3332
4816
  return { kind: "blocked", blocker: "source-drift" };
3333
- const evidence = { version: 1,
3334
- status: "complete", ...input.expected,
4817
+ const evidence = {
4818
+ version: 1,
4819
+ status: "complete",
4820
+ ...input.expected,
3335
4821
  migrationAuthorityId: input.authority.authorityId,
3336
4822
  startupAuthorityId: input.authority.startupAuthorityId,
3337
4823
  closureOperationId: input.authority.closureOperationId,
3338
4824
  migrationInventorySha256: input.authority.migrationInventorySha256,
3339
- resultingSourceFamily: input.authority.resultingSourceFamily };
4825
+ resultingSourceFamily: input.authority.resultingSourceFamily,
4826
+ };
3340
4827
  const existing = current.migration;
3341
- if (existing?.status === "complete" && existing.profileKey === evidence.profileKey &&
4828
+ if (existing?.status === "complete" &&
4829
+ existing.profileKey === evidence.profileKey &&
3342
4830
  existing.recoveryKey === evidence.recoveryKey &&
3343
4831
  existing.migrationAuthorityId === evidence.migrationAuthorityId &&
3344
4832
  existing.startupAuthorityId === evidence.startupAuthorityId &&
@@ -3346,7 +4834,11 @@ export function executeTelegramInputCustodyMigrationCompletion(input) {
3346
4834
  existing.migrationInventorySha256 === evidence.migrationInventorySha256 &&
3347
4835
  existing.resultingSourceFamily === evidence.resultingSourceFamily)
3348
4836
  return { kind: "completed", evidence: existing, resumed: true };
3349
- input.evidenceStore.publish({ expectedRevision: current.revision, kind: "migration", evidence });
4837
+ input.evidenceStore.publish({
4838
+ expectedRevision: current.revision,
4839
+ kind: "migration",
4840
+ evidence,
4841
+ });
3350
4842
  return { kind: "completed", evidence, resumed: false };
3351
4843
  }
3352
4844
  export function createTelegramInputCustodyProvenReadinessResolver(deps) {
@@ -3363,26 +4855,30 @@ export function createTelegramInputCustodyProvenReadinessResolver(deps) {
3363
4855
  const expected = deps.expectedIdentity();
3364
4856
  const authority = deps.readMigrationCompletionAuthority();
3365
4857
  return matchesTelegramInputCustodyActivationEvidence(authority, expected) &&
3366
- authority?.status === "authorized" && authority.resultingSourceFamily === source
3367
- ? source : "ambiguous";
4858
+ authority?.status === "authorized" &&
4859
+ authority.resultingSourceFamily === source
4860
+ ? source
4861
+ : "ambiguous";
3368
4862
  },
3369
4863
  areLegacyWritersExcluded() {
3370
4864
  const expected = deps.expectedIdentity();
3371
4865
  const evidence = deps.readWriterExclusionEvidence();
3372
4866
  const authority = deps.readStartupExclusionAuthority();
3373
4867
  const mode = deps.readWriterProtocolMode();
3374
- return matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
4868
+ return (matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
3375
4869
  matchesTelegramInputCustodyActivationEvidence(authority, expected) &&
3376
4870
  matchesTelegramInputCustodyActivationEvidence(mode, expected) &&
3377
- evidence?.version === 1 && evidence.status === "excluded" &&
3378
- authority?.version === 1 && authority.status === "enforced" &&
4871
+ evidence?.version === 1 &&
4872
+ evidence.status === "excluded" &&
4873
+ authority?.version === 1 &&
4874
+ authority.status === "enforced" &&
3379
4875
  mode?.protocol === "custody-v3" &&
3380
4876
  evidence.startupAuthorityId === authority.authorityId &&
3381
4877
  evidence.startupAuthorityId === mode.startupAuthorityId &&
3382
4878
  evidence.closureOperationId === authority.closureOperationId &&
3383
4879
  evidence.closureOperationId === mode.closureOperationId &&
3384
4880
  evidence.writerInventorySha256 === authority.writerInventorySha256 &&
3385
- evidence.writerInventorySha256 === mode.writerInventorySha256;
4881
+ evidence.writerInventorySha256 === mode.writerInventorySha256);
3386
4882
  },
3387
4883
  isHistoricalMigrationComplete() {
3388
4884
  const expected = deps.expectedIdentity();
@@ -3397,10 +4893,12 @@ export function createTelegramInputCustodyProvenReadinessResolver(deps) {
3397
4893
  catch {
3398
4894
  source = "ambiguous";
3399
4895
  }
3400
- return matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
4896
+ return (matchesTelegramInputCustodyActivationEvidence(evidence, expected) &&
3401
4897
  matchesTelegramInputCustodyActivationEvidence(authority, expected) &&
3402
- evidence?.version === 1 && evidence.status === "complete" &&
3403
- authority?.version === 1 && authority.status === "authorized" &&
4898
+ evidence?.version === 1 &&
4899
+ evidence.status === "complete" &&
4900
+ authority?.version === 1 &&
4901
+ authority.status === "authorized" &&
3404
4902
  authority.resultingSourceFamily === source &&
3405
4903
  evidence.migrationAuthorityId === authority.authorityId &&
3406
4904
  evidence.startupAuthorityId === authority.startupAuthorityId &&
@@ -3408,8 +4906,9 @@ export function createTelegramInputCustodyProvenReadinessResolver(deps) {
3408
4906
  evidence.startupAuthorityId === mode?.startupAuthorityId &&
3409
4907
  evidence.closureOperationId === authority.closureOperationId &&
3410
4908
  evidence.closureOperationId === mode?.closureOperationId &&
3411
- evidence.migrationInventorySha256 === authority.migrationInventorySha256 &&
3412
- evidence.resultingSourceFamily === authority.resultingSourceFamily;
4909
+ evidence.migrationInventorySha256 ===
4910
+ authority.migrationInventorySha256 &&
4911
+ evidence.resultingSourceFamily === authority.resultingSourceFamily);
3413
4912
  },
3414
4913
  listPeerReadiness: deps.listPeerReadiness,
3415
4914
  });
@@ -3423,14 +4922,18 @@ export function createTelegramInputCustodyActivationReadinessResolver(deps) {
3423
4922
  try {
3424
4923
  legacyWritersExcluded = deps.areLegacyWritersExcluded();
3425
4924
  }
3426
- catch { /* blocked */ }
4925
+ catch {
4926
+ /* blocked */
4927
+ }
3427
4928
  if (!legacyWritersExcluded)
3428
4929
  return { enabled: false, blocker: "legacy-writers-present" };
3429
4930
  let historicalMigrationComplete = false;
3430
4931
  try {
3431
4932
  historicalMigrationComplete = deps.isHistoricalMigrationComplete();
3432
4933
  }
3433
- catch { /* blocked */ }
4934
+ catch {
4935
+ /* blocked */
4936
+ }
3434
4937
  if (!historicalMigrationComplete)
3435
4938
  return { enabled: false, blocker: "migration-incomplete" };
3436
4939
  let sourceStatus;
@@ -3449,8 +4952,13 @@ export function createTelegramInputCustodyActivationReadinessResolver(deps) {
3449
4952
  catch {
3450
4953
  peerReadiness = ["unknown"];
3451
4954
  }
3452
- return evaluateTelegramInputCustodyActivationReadiness({ requested,
3453
- sourceStatus, legacyWritersExcluded, historicalMigrationComplete, peerReadiness });
4955
+ return evaluateTelegramInputCustodyActivationReadiness({
4956
+ requested,
4957
+ sourceStatus,
4958
+ legacyWritersExcluded,
4959
+ historicalMigrationComplete,
4960
+ peerReadiness,
4961
+ });
3454
4962
  };
3455
4963
  }
3456
4964
  export function createTelegramInputCustodyLifecycleBindingResolver(deps) {
@@ -3461,21 +4969,33 @@ export function createTelegramInputCustodyLifecycleBindingResolver(deps) {
3461
4969
  return undefined;
3462
4970
  const source = deps.resolveInputJournal();
3463
4971
  const recipientBindingKey = deps.getRecipientBindingKey()?.trim();
3464
- if (!source || !source.runtimeKey || !source.recoveryKey || !recipientBindingKey)
4972
+ if (!source ||
4973
+ !source.runtimeKey ||
4974
+ !source.recoveryKey ||
4975
+ !recipientBindingKey)
3465
4976
  return undefined;
3466
4977
  let port = ports.get(source.journal);
3467
4978
  if (!port) {
3468
4979
  port = createTelegramInputCustodyWorkerJournalPort(source.journal);
3469
4980
  ports.set(source.journal, port);
3470
4981
  }
3471
- return { runtimeKey: JSON.stringify({ source: source.runtimeKey, recipientBindingKey }),
4982
+ return {
4983
+ runtimeKey: JSON.stringify({
4984
+ source: source.runtimeKey,
4985
+ recipientBindingKey,
4986
+ }),
3472
4987
  recoveryKey: source.recoveryKey,
3473
- recipientBindingKey, journal: { ...port, appendBatch: source.journal.appendBatch,
4988
+ recipientBindingKey,
4989
+ journal: {
4990
+ ...port,
4991
+ appendBatch: source.journal.appendBatch,
3474
4992
  discardQueued: source.journal.discardQueued,
3475
4993
  recoverDeadQueueOwner: source.journal.recoverDeadQueueOwner,
3476
4994
  offerQueuedHandoff: source.journal.offerQueuedHandoff,
3477
4995
  acceptQueuedHandoff: source.journal.acceptQueuedHandoff,
3478
- cancelQueuedHandoff: source.journal.cancelQueuedHandoff } };
4996
+ cancelQueuedHandoff: source.journal.cancelQueuedHandoff,
4997
+ },
4998
+ };
3479
4999
  };
3480
5000
  }
3481
5001
  export function createTelegramCustodiedExecutionSession(input) {
@@ -3485,10 +5005,12 @@ export function createTelegramCustodiedExecutionSession(input) {
3485
5005
  const receipt = receipts.get(updateId);
3486
5006
  if (!receipt) {
3487
5007
  const duplicate = settledQueues.get(updateId);
3488
- if (duplicate && outcome.kind === "queued" &&
5008
+ if (duplicate &&
5009
+ outcome.kind === "queued" &&
3489
5010
  duplicate.queueReceipt.queueKind === outcome.queueKind &&
3490
5011
  duplicate.queueReceipt.receiptId === outcome.receiptId &&
3491
- duplicate.queueReceipt.sourceUpdateIds.length === outcome.sourceUpdateIds.length &&
5012
+ duplicate.queueReceipt.sourceUpdateIds.length ===
5013
+ outcome.sourceUpdateIds.length &&
3492
5014
  duplicate.queueReceipt.sourceUpdateIds.every((id, index) => id === outcome.sourceUpdateIds[index])) {
3493
5015
  settledQueues.delete(updateId);
3494
5016
  return duplicate;
@@ -3500,12 +5022,18 @@ export function createTelegramCustodiedExecutionSession(input) {
3500
5022
  if (outcome.kind === "queued") {
3501
5023
  if (!outcome.sourceUpdateIds.includes(updateId))
3502
5024
  throw new TelegramUpdateAdmissionOutcomeError("Telegram grouped queue custody omitted the settling input.");
3503
- const grouped = outcome.sourceUpdateIds.map(sourceUpdateId => receipts.get(sourceUpdateId));
3504
- if (grouped.some(candidate => !candidate))
5025
+ const grouped = outcome.sourceUpdateIds.map((sourceUpdateId) => receipts.get(sourceUpdateId));
5026
+ if (grouped.some((candidate) => !candidate))
3505
5027
  throw new TelegramUpdateAdmissionOutcomeError("Telegram grouped queue custody omitted an exact running source receipt.");
3506
- const queued = input.journal.queueInputs({ queueKind: outcome.queueKind,
3507
- receiptId: outcome.receiptId, receipts: grouped });
3508
- 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
+ };
3509
5037
  for (const sourceUpdateId of outcome.sourceUpdateIds) {
3510
5038
  receipts.delete(sourceUpdateId);
3511
5039
  if (sourceUpdateId !== updateId)
@@ -3520,8 +5048,11 @@ export function createTelegramCustodiedExecutionSession(input) {
3520
5048
  return {
3521
5049
  settle,
3522
5050
  async execute(update, handler) {
3523
- const acquired = input.journal.acquireInput({ updateId: update.update_id,
3524
- recipientBindingKey: input.recipientBindingKey, executionUpdate: update });
5051
+ const acquired = input.journal.acquireInput({
5052
+ updateId: update.update_id,
5053
+ recipientBindingKey: input.recipientBindingKey,
5054
+ executionUpdate: update,
5055
+ });
3525
5056
  const started = input.journal.startInput(acquired.receipt);
3526
5057
  if (!started.started)
3527
5058
  return { status: "outcome-unknown", receipt: acquired.receipt };
@@ -3549,6 +5080,10 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3549
5080
  if (deps.onLateOutcome && !deps.onLateOutcomeError) {
3550
5081
  throw new Error("Telegram late admission outcomes require a diagnostic error sink.");
3551
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) {
3552
5087
  const registry = deps.registry ?? getOrCreateUpdateHandlerRegistry();
3553
5088
  let nextExecutionGeneration = 0;
3554
5089
  return async (update, ctx, signal) => {
@@ -3565,8 +5100,15 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3565
5100
  generation,
3566
5101
  updateId: update.update_id,
3567
5102
  signal,
3568
- isCurrent: () => !signal.aborted && !suspended && ((!routingLifetime && !routingClockUnknown) || immediate || settlementReported ||
3569
- deps.isRoutingInputCurrent?.({ updateId: update.update_id, signal }) !== false),
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),
3570
5112
  assertCurrent() {
3571
5113
  if (!execution.isCurrent()) {
3572
5114
  throw signal.reason ?? new DOMException("Aborted", "AbortError");
@@ -3574,18 +5116,25 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3574
5116
  },
3575
5117
  };
3576
5118
  execution.assertCurrent();
3577
- const verdict = await registry.dispatch(update, execution);
3578
- execution.assertCurrent();
3579
- if (verdict === "consume")
3580
- 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
+ }
3581
5125
  let immediate = true;
3582
5126
  let settlementReported = false;
3583
5127
  let outcome;
5128
+ let completionAttempted = false;
5129
+ let completedSource;
3584
5130
  const boundUpdate = bindTelegramUpdateExecutionFence(bindTelegramUpdateAdmissionSource(update, (next) => {
3585
5131
  if (!execution.isCurrent())
3586
5132
  return;
3587
5133
  if (next.kind === "deferred" && next.routingReview) {
3588
- 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 })) {
3589
5138
  throw new TelegramUpdateAdmissionOutcomeError("Historical review cannot override accepted or selected work.");
3590
5139
  }
3591
5140
  suspended = true;
@@ -3599,15 +5148,34 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3599
5148
  if (!deps.onLateOutcome) {
3600
5149
  throw new TelegramUpdateAdmissionOutcomeError(`Telegram update ${update.update_id} reported a late outcome without an owner.`);
3601
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;
3602
5161
  void Promise.resolve()
3603
- .then(() => {
5162
+ .then(async () => {
3604
5163
  if (!execution.isCurrent())
3605
5164
  return;
3606
- return deps.onLateOutcome(next, {
5165
+ const completion = await deps.onLateOutcome(reported, {
3607
5166
  updateId: update.update_id,
3608
5167
  ctx,
3609
5168
  signal,
3610
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
+ };
3611
5179
  })
3612
5180
  .catch((error) => {
3613
5181
  try {
@@ -3617,9 +5185,16 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3617
5185
  // Diagnostic sinks must not create an unhandled late Promise.
3618
5186
  }
3619
5187
  });
3620
- }, { abandon: (authority) => {
3621
- const isCurrent = () => !signal.aborted && routingClaims === 0 && !settlementReported && authority.isCurrent();
3622
- 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())
3623
5198
  return undefined;
3624
5199
  if (abandoned)
3625
5200
  return { ...abandoned, duplicate: true };
@@ -3627,23 +5202,91 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3627
5202
  suspended = true;
3628
5203
  // Failed/unknown publication keeps dispatch suspended for exact cancellation retry.
3629
5204
  // An ineligible request that attempted no publication remains inert.
3630
- 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
+ });
3631
5211
  if (result)
3632
5212
  abandoned = result;
3633
5213
  else
3634
5214
  suspended = wasSuspended;
3635
5215
  return result;
3636
- }, inspectAbandoning: request => execution.isCurrent()
3637
- ? deps.inspectAbandoning?.({ ...request, signal }) : undefined,
3638
- 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,
3639
5223
  inspectSource: () => execution.isCurrent() && !immediate && !settlementReported
3640
- ? deps.inspectDeferredSource?.({ updateId: update.update_id, signal }) : undefined,
3641
- isHistorical: matchesOriginal => !signal.aborted && deps.isHistoricalSource?.({ updateId: update.update_id, signal,
3642
- ...(matchesOriginal ? { matchesOriginal } : {}) }) === true,
3643
- isHistoricalReviewHeld: () => !signal.aborted && suspended && outcome?.kind === "deferred" && outcome.routingReview === true,
3644
- supportsAbandonment: journalBindingKey => !signal.aborted && !settlementReported &&
3645
- deps.supportsDeferredAbandonment?.({ updateId: update.update_id, signal, journalBindingKey }) === true,
3646
- 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) => {
3647
5290
  execution.assertCurrent();
3648
5291
  if (settlementReported || routingClaims || suspended)
3649
5292
  return undefined;
@@ -3653,7 +5296,13 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3653
5296
  if (routingSourceIds && !isDeepStrictEqual(ids, routingSourceIds))
3654
5297
  throw new TelegramUpdateAdmissionOutcomeError("Telegram routing input source membership changed.");
3655
5298
  try {
3656
- 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
+ });
3657
5306
  }
3658
5307
  catch (error) {
3659
5308
  routingClockUnknown = true;
@@ -3673,8 +5322,13 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3673
5322
  if (!isDeepStrictEqual(sourceUpdateIds ?? [update.update_id], routingSourceIds))
3674
5323
  throw new TelegramUpdateAdmissionOutcomeError("Telegram routing input selection changed its source membership.");
3675
5324
  routingSelectionAttempted = true;
3676
- routingSelectionConfirmed = deps.selectRoutingInput?.({ updateId: update.update_id, signal,
3677
- 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;
3678
5332
  if (!routingSelectionConfirmed)
3679
5333
  throw new TelegramUpdateAdmissionOutcomeError("Telegram routing input choice is expired or unavailable.");
3680
5334
  }
@@ -3686,11 +5340,19 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3686
5340
  released = true;
3687
5341
  routingClaims -= 1;
3688
5342
  };
3689
- } }), execution);
5343
+ },
5344
+ }), execution);
3690
5345
  try {
3691
5346
  execution.assertCurrent();
3692
- await deps.defaultHandle(boundUpdate, ctx, execution);
3693
- 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)
3694
5356
  execution.assertCurrent();
3695
5357
  }
3696
5358
  finally {
@@ -3700,47 +5362,63 @@ export function createTelegramUpdateAdmissionHandle(deps) {
3700
5362
  };
3701
5363
  }
3702
5364
  export function createTelegramCustodiedUpdateAdmissionHandle(deps) {
3703
- const session = createTelegramCustodiedExecutionSession({ journal: deps.journal,
3704
- recipientBindingKey: deps.recipientBindingKey });
5365
+ const session = createTelegramCustodiedExecutionSession({
5366
+ journal: deps.journal,
5367
+ recipientBindingKey: deps.recipientBindingKey,
5368
+ });
3705
5369
  const admission = createTelegramUpdateAdmissionHandle({
3706
5370
  defaultHandle: deps.defaultHandle,
3707
5371
  registry: deps.registry,
3708
5372
  async onLateOutcome(outcome, details) {
3709
5373
  const result = await session.settle(details.updateId, outcome);
3710
- deps.onCustodiedLateSettlement?.(result, { updateId: details.updateId, signal: details.signal });
5374
+ deps.onCustodiedLateSettlement?.(result, {
5375
+ updateId: details.updateId,
5376
+ signal: details.signal,
5377
+ });
3711
5378
  },
3712
5379
  onLateOutcomeError: deps.onLateOutcomeError,
3713
5380
  });
3714
- 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));
3715
5382
  }
3716
5383
  // A dispatched prompt can remain in memory until agent_start. Remember only acknowledged
3717
5384
  // removal of these exact receipt objects for later local discard, never for dispatch/replay.
3718
5385
  const acknowledgedQueueReceiptCompletions = new WeakMap();
3719
5386
  const isQueueReceiptCompletionAcknowledged = (receipt) => {
3720
5387
  const acknowledged = acknowledgedQueueReceiptCompletions.get(receipt);
3721
- return acknowledged !== undefined && areTelegramQueueAdmissionReceiptsEqual(acknowledged, receipt);
5388
+ return (acknowledged !== undefined &&
5389
+ areTelegramQueueAdmissionReceiptsEqual(acknowledged, receipt));
3722
5390
  };
3723
5391
  export function createTelegramQueueAdmissionSettlementMuxRuntime(runtimes) {
3724
5392
  // Plan the whole request before mutation; unready is not an acknowledgement of completion.
3725
5393
  const complete = (items, ctx, kind) => {
3726
5394
  const plan = new Map();
3727
- const reason = kind === "prompt" ? "prompt-handoff" : kind === "control" ? "control-settlement" : "discard";
3728
- const settlementOwner = (runtime, receipt) => runtime.getQueueReceiptSettlementOwner ? runtime.getQueueReceiptSettlementOwner(receipt, ctx, reason) :
3729
- runtime.isItemReady({ admissionReceipts: [receipt] }) ? runtime.getQueueReceiptOwner(receipt) : undefined;
3730
- 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 ?? [])) {
3731
5406
  if (isQueueReceiptCompletionAcknowledged(receipt))
3732
5407
  continue;
3733
- const candidates = runtimes.filter(runtime => runtime.getQueueReceiptSettlementOwner ? !!settlementOwner(runtime, receipt)
5408
+ const candidates = runtimes.filter((runtime) => runtime.getQueueReceiptSettlementOwner
5409
+ ? !!settlementOwner(runtime, receipt)
3734
5410
  : runtime.isItemReady({ admissionReceipts: [receipt] }));
3735
5411
  const selected = candidates[0];
3736
5412
  if (!selected)
3737
5413
  return false;
3738
5414
  if (candidates.length > 1) {
3739
5415
  const owner = settlementOwner(selected, receipt);
3740
- if (!owner || candidates.some(runtime => {
3741
- const candidate = settlementOwner(runtime, receipt);
3742
- return !candidate || !areTelegramUpdateJournalQueueOwnersEqual(owner, candidate);
3743
- }))
5416
+ if (!owner ||
5417
+ candidates.some((runtime) => {
5418
+ const candidate = settlementOwner(runtime, receipt);
5419
+ return (!candidate ||
5420
+ !areTelegramUpdateJournalQueueOwnersEqual(owner, candidate));
5421
+ }))
3744
5422
  return false;
3745
5423
  }
3746
5424
  const receipts = plan.get(selected) ?? [];
@@ -3749,8 +5427,10 @@ export function createTelegramQueueAdmissionSettlementMuxRuntime(runtimes) {
3749
5427
  }
3750
5428
  for (const [runtime, receipts] of plan) {
3751
5429
  const item = { admissionReceipts: receipts };
3752
- const completed = kind === "discard" ? runtime.onItemsDiscarded([item], ctx)
3753
- : kind === "prompt" ? runtime.onPromptHandedOff(item, ctx)
5430
+ const completed = kind === "discard"
5431
+ ? runtime.onItemsDiscarded([item], ctx)
5432
+ : kind === "prompt"
5433
+ ? runtime.onPromptHandedOff(item, ctx)
3754
5434
  : runtime.onControlSettled(item, ctx);
3755
5435
  if (completed !== true)
3756
5436
  return false;
@@ -3787,12 +5467,17 @@ export function createTelegramQueueAdmissionSettlementRuntime(worker) {
3787
5467
  receipts.push(receipt);
3788
5468
  }
3789
5469
  }
3790
- return receipts.length === 0 || worker.completeQueueReceipts({ receipts, ctx, reason });
5470
+ return (receipts.length === 0 ||
5471
+ worker.completeQueueReceipts({ receipts, ctx, reason }));
3791
5472
  };
3792
5473
  return {
3793
5474
  isItemReady: (item) => (item.admissionReceipts ?? []).every(worker.isQueueReceiptCommitted),
3794
5475
  getQueueReceiptOwner: worker.getQueueReceiptOwner,
3795
- ...(worker.getQueueReceiptSettlementOwner ? { getQueueReceiptSettlementOwner: worker.getQueueReceiptSettlementOwner } : {}),
5476
+ ...(worker.getQueueReceiptSettlementOwner
5477
+ ? {
5478
+ getQueueReceiptSettlementOwner: worker.getQueueReceiptSettlementOwner,
5479
+ }
5480
+ : {}),
3796
5481
  onPromptHandedOff: (item, ctx) => complete([item], ctx, "prompt-handoff"),
3797
5482
  onControlSettled: (item, ctx) => complete([item], ctx, "control-settlement"),
3798
5483
  onItemsDiscarded: (items, ctx) => complete(items, ctx, "discard"),
@@ -3966,8 +5651,7 @@ export function createTelegramQueueHandoffReconciliationRuntimeAssembly(deps) {
3966
5651
  createRecipientJournalBindingKey(recipient) {
3967
5652
  if (!recipient.profileKey || !recipient.sessionId)
3968
5653
  return undefined;
3969
- return deps.createRecipientJournalResolver(recipient.profileKey, recipient.sessionId)()
3970
- ?.recoveryKey;
5654
+ return deps.createRecipientJournalResolver(recipient.profileKey, recipient.sessionId)()?.recoveryKey;
3971
5655
  },
3972
5656
  getQueuedItems: deps.queueStore.getQueuedItems,
3973
5657
  getReceiptOwner(receipt) {
@@ -4021,7 +5705,9 @@ export function createTelegramQueueHandoffReconciler(deps) {
4021
5705
  (followerRegistered && deps.canHandoffWithLeader?.() === false)) {
4022
5706
  return;
4023
5707
  }
4024
- const followers = deps.listFollowers().filter((follower) => follower.instanceId !== deps.donorInstanceId);
5708
+ const followers = deps
5709
+ .listFollowers()
5710
+ .filter((follower) => follower.instanceId !== deps.donorInstanceId);
4025
5711
  if (followers.length === 0)
4026
5712
  return;
4027
5713
  for (const item of [...deps.getQueuedItems()]) {
@@ -4067,7 +5753,12 @@ export function createTelegramQueueHandoffReconciler(deps) {
4067
5753
  stageRemote: async ({ payload: sourcePayload }) => {
4068
5754
  const payload = {
4069
5755
  ...sourcePayload,
4070
- admissionReceipts: [{ ...sourcePayload.admissionReceipts[0], journalBindingKey: recipientJournalBindingKey }],
5756
+ admissionReceipts: [
5757
+ {
5758
+ ...sourcePayload.admissionReceipts[0],
5759
+ journalBindingKey: recipientJournalBindingKey,
5760
+ },
5761
+ ],
4071
5762
  };
4072
5763
  if (followerRegistered) {
4073
5764
  return deps.stageThroughFollower({
@@ -4103,7 +5794,7 @@ export function createTelegramQueueHandoffReconciler(deps) {
4103
5794
  typeof response.result !== "object" ||
4104
5795
  !queueOwner) {
4105
5796
  throw new Error(response.kind === "bus.ack"
4106
- ? response.message ?? "Telegram queue handoff was rejected."
5797
+ ? (response.message ?? "Telegram queue handoff was rejected.")
4107
5798
  : "Telegram queue handoff returned no acknowledgement.");
4108
5799
  }
4109
5800
  return {
@@ -4164,7 +5855,7 @@ export function createTelegramUpdateAdmissionRuntimeBinding(deps) {
4164
5855
  let follower;
4165
5856
  let settlement;
4166
5857
  let inputCustodyBus;
4167
- const getActive = () => deps.isFollowerRegistered() ? follower : leader;
5858
+ const getActive = () => (deps.isFollowerRegistered() ? follower : leader);
4168
5859
  return {
4169
5860
  bind(input) {
4170
5861
  leader = input.leader;
@@ -4226,12 +5917,14 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4226
5917
  let foreignQueueOwnerLiveness;
4227
5918
  let releaseSourceReference;
4228
5919
  let sourceReferenceBinding;
5920
+ let activeContext;
4229
5921
  const withRetainedSourceReference = (operation) => {
4230
5922
  if (!journal || !worker)
4231
5923
  throw new Error("Telegram update admission worker is not active.");
4232
5924
  // Synchronous retained-source access needs a reference, not renewed execution authority.
4233
5925
  const release = !releaseSourceReference && sourceReferenceBinding
4234
- ? deps.acquireSourceReference?.(sourceReferenceBinding) : undefined;
5926
+ ? deps.acquireSourceReference?.(sourceReferenceBinding)
5927
+ : undefined;
4235
5928
  try {
4236
5929
  return operation(journal);
4237
5930
  }
@@ -4239,8 +5932,9 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4239
5932
  release?.();
4240
5933
  }
4241
5934
  };
4242
- const readObservedJournal = () => withRetainedSourceReference(source => source.read());
5935
+ const readObservedJournal = () => withRetainedSourceReference((source) => source.read());
4243
5936
  const stopCurrent = async (forget) => {
5937
+ activeContext = undefined;
4244
5938
  await worker?.stop();
4245
5939
  const release = releaseSourceReference;
4246
5940
  releaseSourceReference = undefined;
@@ -4248,7 +5942,9 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4248
5942
  release?.();
4249
5943
  }
4250
5944
  catch (error) {
4251
- deps.recordRuntimeEvent?.("inbound-worker", error, { phase: "source-reference-release" });
5945
+ deps.recordRuntimeEvent?.("inbound-worker", error, {
5946
+ phase: "source-reference-release",
5947
+ });
4252
5948
  }
4253
5949
  if (!forget)
4254
5950
  return;
@@ -4267,12 +5963,15 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4267
5963
  await stopCurrent(true);
4268
5964
  return;
4269
5965
  }
4270
- if (replace || !worker || activeRuntimeKey !== binding.runtimeKey ||
5966
+ if (replace ||
5967
+ !worker ||
5968
+ activeRuntimeKey !== binding.runtimeKey ||
4271
5969
  workerInputCustody !== binding.journal.inputCustody) {
4272
5970
  const { runtimeKey, recoveryKey } = binding;
4273
5971
  await stopCurrent(true);
4274
5972
  binding = deps.resolveBinding();
4275
- if (!binding || binding.runtimeKey !== runtimeKey ||
5973
+ if (!binding ||
5974
+ binding.runtimeKey !== runtimeKey ||
4276
5975
  binding.recoveryKey !== recoveryKey) {
4277
5976
  throw new Error("Telegram update source binding changed during startup.");
4278
5977
  }
@@ -4371,6 +6070,7 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4371
6070
  }
4372
6071
  }
4373
6072
  worker.start(ctx);
6073
+ activeContext = ctx;
4374
6074
  };
4375
6075
  const runExclusive = (task) => {
4376
6076
  const next = operation.then(task, task);
@@ -4428,7 +6128,7 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4428
6128
  if (!journal || !worker || !journal.cancelQueuedHandoff) {
4429
6129
  throw new Error("Telegram update journal queue handoff cancellation is not available.");
4430
6130
  }
4431
- const result = withRetainedSourceReference(source => source.cancelQueuedHandoff(input));
6131
+ const result = withRetainedSourceReference((source) => source.cancelQueuedHandoff(input));
4432
6132
  worker.signal();
4433
6133
  return result;
4434
6134
  },
@@ -4475,6 +6175,31 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4475
6175
  isTelegramReactionDependencyForQueueItem(entry.update, item)));
4476
6176
  },
4477
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
+ },
4478
6203
  getState: () => {
4479
6204
  const state = worker?.getState();
4480
6205
  if (!state)
@@ -4496,20 +6221,25 @@ export function createTelegramUpdateAdmissionLifecycleRuntime(deps) {
4496
6221
  /** Compose source-bound routing and late grouped settlement under one worker. */
4497
6222
  export function createTelegramUpdateAdmissionWorkerRuntime(deps) {
4498
6223
  let worker;
4499
- const executeUpdate = createTelegramUpdateAdmissionHandle({
6224
+ const admissionDeps = {
4500
6225
  defaultHandle: deps.defaultHandle,
4501
6226
  registry: deps.registry,
4502
- abandonDeferred: input => worker?.abandonDeferred?.(input),
4503
- armRoutingInput: input => worker?.armRoutingInput?.(input),
4504
- selectRoutingInput: input => worker?.selectRoutingInput?.(input) === true,
4505
- isRoutingInputCurrent: input => worker?.isRoutingInputCurrent?.(input) !== false,
4506
- supportsDeferredAbandonment: input => worker?.supportsDeferredAbandonment?.(input) === true,
4507
- inspectAbandoning: input => worker?.inspectAbandoning?.(input),
4508
- inspectHistorical: input => worker?.inspectHistorical?.(input),
4509
- inspectDeferredSource: input => worker?.inspectDeferredSource?.(input),
4510
- 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,
4511
6241
  onLateOutcome(outcome, details) {
4512
- worker?.settleDeferred({
6242
+ return worker?.settleDeferred({
4513
6243
  updateId: details.updateId,
4514
6244
  outcome,
4515
6245
  signal: details.signal,
@@ -4521,24 +6251,54 @@ export function createTelegramUpdateAdmissionWorkerRuntime(deps) {
4521
6251
  updateId,
4522
6252
  });
4523
6253
  },
4524
- });
4525
- 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;
4526
6270
  if (deps.inputCustody && !custodyBindingKey)
4527
6271
  throw new Error("Telegram custodied admission worker requires one recipient binding key.");
4528
6272
  const executeCustodiedUpdate = deps.inputCustody && custodyBindingKey
4529
6273
  ? createTelegramCustodiedUpdateAdmissionHandle({
4530
- journal: deps.inputCustody, recipientBindingKey: custodyBindingKey,
4531
- defaultHandle: deps.defaultHandle, registry: deps.registry,
6274
+ journal: deps.inputCustody,
6275
+ recipientBindingKey: custodyBindingKey,
6276
+ defaultHandle: deps.defaultHandle,
6277
+ registry: deps.registry,
4532
6278
  onLateOutcomeError(error, updateId) {
4533
- 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
+ });
4534
6283
  },
4535
6284
  onCustodiedLateSettlement(result, details) {
4536
- worker?.settleCustodied({ updateId: details.updateId, result, signal: details.signal });
6285
+ worker?.settleCustodied({
6286
+ updateId: details.updateId,
6287
+ result,
6288
+ signal: details.signal,
6289
+ });
4537
6290
  },
4538
- }) : undefined;
6291
+ })
6292
+ : undefined;
4539
6293
  worker = createTelegramUpdateWorkerRuntime({
4540
6294
  ...deps,
4541
- ...(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
+ : {}),
4542
6302
  async executeUpdate(update, ctx, signal) {
4543
6303
  const typedUpdate = update;
4544
6304
  try {
@@ -4558,13 +6318,19 @@ export function createTelegramUpdateAdmissionWorkerRuntime(deps) {
4558
6318
  export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4559
6319
  const leader = createTelegramUpdateAdmissionLifecycleRuntime({
4560
6320
  resolveBinding: deps.leader.resolveBinding,
4561
- ...(deps.acquireSourceReference ? { acquireSourceReference: binding => deps.acquireSourceReference("leader", binding) } : {}),
6321
+ ...(deps.acquireSourceReference
6322
+ ? {
6323
+ acquireSourceReference: (binding) => deps.acquireSourceReference("leader", binding),
6324
+ }
6325
+ : {}),
4562
6326
  getQueueOwnerIdentity: deps.worker.getQueueOwnerIdentity,
4563
6327
  createWorker(journal, binding) {
4564
6328
  return createTelegramUpdateAdmissionWorkerRuntime({
4565
6329
  ...deps.worker,
4566
6330
  journal,
4567
- ...(binding.journal.inputCustody ? { inputCustody: binding.journal.inputCustody } : {}),
6331
+ ...(binding.journal.inputCustody
6332
+ ? { inputCustody: binding.journal.inputCustody }
6333
+ : {}),
4568
6334
  getJournalBindingKey: () => binding.recoveryKey,
4569
6335
  getRecipientBindingKey: () => binding.recipientBindingKey,
4570
6336
  hasAuthority: deps.leader.hasAuthority,
@@ -4574,8 +6340,14 @@ export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4574
6340
  });
4575
6341
  const follower = createTelegramUpdateAdmissionLifecycleRuntime({
4576
6342
  getQueueOwnerIdentity: deps.worker.getQueueOwnerIdentity,
4577
- ...(deps.acquireSourceReference ? { acquireSourceReference: binding => deps.acquireSourceReference("follower", binding) } : {}),
4578
- ...(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
+ : {}),
4579
6351
  resolveBinding() {
4580
6352
  const generation = deps.follower.getGeneration();
4581
6353
  if (!deps.follower.isRegistered() || !generation)
@@ -4596,7 +6368,9 @@ export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4596
6368
  return createTelegramUpdateAdmissionWorkerRuntime({
4597
6369
  ...deps.worker,
4598
6370
  journal,
4599
- ...(binding.journal.inputCustody ? { inputCustody: binding.journal.inputCustody } : {}),
6371
+ ...(binding.journal.inputCustody
6372
+ ? { inputCustody: binding.journal.inputCustody }
6373
+ : {}),
4600
6374
  getJournalBindingKey: () => binding.recoveryKey,
4601
6375
  getRecipientBindingKey: () => binding.recipientBindingKey,
4602
6376
  hasAuthority: () => binding.hasAuthority?.() ?? false,
@@ -4610,8 +6384,11 @@ export function createTelegramUpdateAdmissionLifecycleAssembly(deps) {
4610
6384
  },
4611
6385
  recordRuntimeEvent: deps.recordRuntimeEvent,
4612
6386
  });
4613
- deps.runtimeBinding.bind({ leader, follower,
4614
- ...(deps.inputCustodyBus ? { inputCustodyBus: deps.inputCustodyBus } : {}) });
6387
+ deps.runtimeBinding.bind({
6388
+ leader,
6389
+ follower,
6390
+ ...(deps.inputCustodyBus ? { inputCustodyBus: deps.inputCustodyBus } : {}),
6391
+ });
4615
6392
  return { leader, follower };
4616
6393
  }
4617
6394
  /** Own queue-owner projection and shared leader/follower worker composition. */