@llblab/pi-kit 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/BACKLOG.md +3 -1
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +4 -4
  4. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +12 -0
  5. package/node_modules/@llblab/pi-state-flow/README.md +2 -2
  6. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +2 -2
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +17 -8
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +7 -3
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +2 -1
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +3 -2
  11. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  12. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -3
  13. package/node_modules/@llblab/pi-state-flow/docs/usage.md +3 -2
  14. package/node_modules/@llblab/pi-state-flow/lib/context.ts +17 -8
  15. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +6 -3
  16. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +4 -2
  17. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  18. package/node_modules/@llblab/pi-telegram/AGENTS.md +13 -10
  19. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -1
  20. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +23 -0
  21. package/node_modules/@llblab/pi-telegram/README.md +10 -11
  22. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  23. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  24. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  25. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  26. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  28. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  29. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  30. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +164 -20
  31. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  32. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +181 -77
  33. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  34. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  35. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  36. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  37. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  38. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  39. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  40. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  42. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  43. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  44. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  45. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  46. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  47. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  48. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  49. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  50. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +642 -170
  51. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  52. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  53. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  54. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  55. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  56. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +40 -4
  57. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2102 -760
  58. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  59. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  60. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +34 -3
  61. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +318 -119
  62. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  63. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  65. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  66. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  67. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  68. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  69. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  70. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  71. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  72. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  73. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  74. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  75. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  76. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  77. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  79. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  80. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  81. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  82. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  83. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  84. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  85. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  86. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +61 -51
  87. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  88. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  89. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  90. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  91. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  92. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +23 -37
  93. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +71 -52
  94. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  95. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  96. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  97. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  98. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +16 -16
  99. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +261 -116
  100. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  101. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  102. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  103. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  104. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +121 -7
  105. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4750 -1161
  106. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  107. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  108. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  109. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  110. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  111. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +6 -3
  112. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +112 -45
  113. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  114. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  115. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -11
  116. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +165 -49
  117. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  118. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  119. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  120. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  121. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  122. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  123. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  124. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  125. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  126. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +88 -24
  127. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2276 -842
  128. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  129. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  130. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +178 -82
  131. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2438 -645
  132. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  133. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  134. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  135. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  136. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  137. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  138. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  139. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  140. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  141. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  142. package/node_modules/@llblab/pi-telegram/docs/architecture.md +43 -35
  143. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  144. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  145. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  146. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  147. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  148. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  149. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  150. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  151. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +390 -82
  152. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +262 -99
  153. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  154. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  155. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  156. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  157. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  158. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  159. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  160. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  161. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  162. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1209 -431
  163. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  164. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  165. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  166. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5329 -1712
  167. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  168. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  169. package/node_modules/@llblab/pi-telegram/lib/locks.ts +703 -212
  170. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  171. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  172. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  173. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  174. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  175. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  176. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  177. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  178. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  179. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  180. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  181. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  182. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  183. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +125 -125
  184. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  185. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  186. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  187. package/node_modules/@llblab/pi-telegram/lib/polling.ts +165 -149
  188. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  189. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  190. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  191. package/node_modules/@llblab/pi-telegram/lib/queue.ts +523 -289
  192. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  193. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  194. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  195. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8527 -2149
  196. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  197. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  198. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  199. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  200. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  201. package/node_modules/@llblab/pi-telegram/lib/sync.ts +488 -359
  202. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  203. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +322 -122
  204. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  205. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  206. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  207. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  208. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  209. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5329 -1817
  210. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  211. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  212. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5194 -1600
  213. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  214. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  215. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  216. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  217. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  218. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  219. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  220. package/package.json +3 -3
  221. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  222. package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
@@ -14,7 +14,8 @@ export const TELEGRAM_GET_UPDATES_CONFLICT_STOP_LIMIT = 10;
14
14
  const TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_LIMIT = 3;
15
15
  const TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_MS = 1_000;
16
16
  const TELEGRAM_GET_UPDATES_CONFLICT_SLOW_RETRY_MS = 3_000;
17
- const TELEGRAM_POLLING_RETRY_MS = 3_000;
17
+ const TELEGRAM_POLLING_RETRY_BASE_MS = 1_000;
18
+ const TELEGRAM_POLLING_RETRY_MAX_MS = 30_000;
18
19
  const TELEGRAM_GET_UPDATES_GRACE_MS = 10_000;
19
20
  // Standard Telegram DM polling does not expose ordinary message-deletion events.
20
21
  // Business deletions belong to a separate namespace and default routing ignores them.
@@ -51,6 +52,11 @@ export class TelegramPersistentGetUpdatesConflictError extends Error {
51
52
  this.count = count;
52
53
  }
53
54
  }
55
+ /** Exponential backoff for non-conflict polling/admission failures. */
56
+ export function getTelegramPollingRetryDelayMs(consecutiveFailures) {
57
+ const exponent = Math.max(0, Math.min(consecutiveFailures - 1, 16));
58
+ return Math.min(TELEGRAM_POLLING_RETRY_BASE_MS * 2 ** exponent, TELEGRAM_POLLING_RETRY_MAX_MS);
59
+ }
54
60
  export class TelegramGetUpdatesTimeoutError extends Error {
55
61
  timeoutMs;
56
62
  constructor(timeoutMs) {
@@ -152,7 +158,10 @@ export function createTelegramDurablePollingRuntimeAssembly(deps) {
152
158
  }
153
159
  catch (error) {
154
160
  try {
155
- deps.recordRuntimeEvent?.("polling", error, { phase: "batch-preparation", updateCount: updates.length });
161
+ deps.recordRuntimeEvent?.("polling", error, {
162
+ phase: "batch-preparation",
163
+ updateCount: updates.length,
164
+ });
156
165
  }
157
166
  catch {
158
167
  // Already-published input must still reach the worker if diagnostics fail.
@@ -356,8 +365,13 @@ export function startTelegramPollingRuntime(ctx, deps) {
356
365
  deps.setPollingController(undefined);
357
366
  if (!ownsPromise && !ownsController)
358
367
  return;
359
- deps.onPollingStopped?.(controller.signal.aborted ? "requested" : persistentConflict
360
- ? "persistent-conflict" : failed ? "failed" : "completed");
368
+ deps.onPollingStopped?.(controller.signal.aborted
369
+ ? "requested"
370
+ : persistentConflict
371
+ ? "persistent-conflict"
372
+ : failed
373
+ ? "failed"
374
+ : "completed");
361
375
  // Detach the inner promise before outer teardown calls polling.stop().
362
376
  if (persistentConflict && !controller.signal.aborted) {
363
377
  try {
@@ -367,12 +381,15 @@ export function startTelegramPollingRuntime(ctx, deps) {
367
381
  else {
368
382
  deps.stopTypingLoop();
369
383
  deps.recordRuntimeEvent?.("polling", persistentConflict, {
370
- phase: "persistent-conflict", count: persistentConflict.count,
384
+ phase: "persistent-conflict",
385
+ count: persistentConflict.count,
371
386
  });
372
387
  }
373
388
  }
374
389
  catch (error) {
375
- deps.recordRuntimeEvent?.("polling", error, { phase: "conflict-stand-down" });
390
+ deps.recordRuntimeEvent?.("polling", error, {
391
+ phase: "conflict-stand-down",
392
+ });
376
393
  }
377
394
  if (deps.getPollingController() || deps.getPollingPromise())
378
395
  return;
@@ -429,7 +446,9 @@ export function createTelegramThreadCapabilityOrchestration(deps) {
429
446
  const expected = generation;
430
447
  return () => expected === generation;
431
448
  },
432
- invalidate() { generation++; },
449
+ invalidate() {
450
+ generation++;
451
+ },
433
452
  };
434
453
  const capabilityDeps = {
435
454
  lifecycle,
@@ -537,6 +556,29 @@ function hasTelegramThreadCapabilityBindings(store) {
537
556
  record.status !== "stale");
538
557
  }) ?? false);
539
558
  }
559
+ /** Falls back to classic polling, recording a failed restore durably; false once the attempt is stale. */
560
+ async function restoreTelegramClassicPolling(ctx, phase, deps, isCurrent) {
561
+ try {
562
+ await deps.startClassicPolling(ctx);
563
+ return isCurrent();
564
+ }
565
+ catch (classicError) {
566
+ if (!isCurrent())
567
+ return false;
568
+ deps.topicTargetStore.setBotState({
569
+ threadMode: "disabled",
570
+ updatedAtMs: (deps.getNowMs ?? Date.now)(),
571
+ lastReconcileAction: `${phase}-classic-restore-failed`,
572
+ });
573
+ await deps.topicTargetStore.persist();
574
+ if (!isCurrent())
575
+ return false;
576
+ deps.recordEvent("bus", classicError, {
577
+ phase: `${phase}-classic-restore`,
578
+ });
579
+ return true;
580
+ }
581
+ }
540
582
  export async function applyTelegramThreadCapability(ctx, threadModeEnabled, phase, deps, isCurrent = deps.lifecycle?.capture() ?? (() => true)) {
541
583
  if (!isCurrent())
542
584
  return;
@@ -574,26 +616,8 @@ export async function applyTelegramThreadCapability(ctx, threadModeEnabled, phas
574
616
  deps.setPollingStartedWithTelegramBus(false);
575
617
  if (hadLiveThreadTransport)
576
618
  deps.suspendLiveThreadTarget?.();
577
- try {
578
- await deps.startClassicPolling(ctx);
579
- if (!isCurrent())
580
- return;
581
- }
582
- catch (classicError) {
583
- if (!isCurrent())
584
- return;
585
- deps.topicTargetStore.setBotState({
586
- threadMode: "disabled",
587
- updatedAtMs: (deps.getNowMs ?? Date.now)(),
588
- lastReconcileAction: `${phase}-classic-restore-failed`,
589
- });
590
- await deps.topicTargetStore.persist();
591
- if (!isCurrent())
592
- return;
593
- deps.recordEvent("bus", classicError, {
594
- phase: `${phase}-classic-restore`,
595
- });
596
- }
619
+ if (!(await restoreTelegramClassicPolling(ctx, phase, deps, isCurrent)))
620
+ return;
597
621
  }
598
622
  deps.updateStatus(ctx);
599
623
  return;
@@ -635,26 +659,8 @@ export async function applyTelegramThreadCapability(ctx, threadModeEnabled, phas
635
659
  return;
636
660
  deps.setTopicModeUnavailable(true);
637
661
  }
638
- try {
639
- await deps.startClassicPolling(ctx);
640
- if (!isCurrent())
641
- return;
642
- }
643
- catch (classicError) {
644
- if (!isCurrent())
645
- return;
646
- deps.topicTargetStore.setBotState({
647
- threadMode: "disabled",
648
- updatedAtMs: (deps.getNowMs ?? Date.now)(),
649
- lastReconcileAction: `${phase}-classic-restore-failed`,
650
- });
651
- await deps.topicTargetStore.persist();
652
- if (!isCurrent())
653
- return;
654
- deps.recordEvent("bus", classicError, {
655
- phase: `${phase}-classic-restore`,
656
- });
657
- }
662
+ if (!(await restoreTelegramClassicPolling(ctx, phase, deps, isCurrent)))
663
+ return;
658
664
  deps.updateStatus(ctx);
659
665
  if (threadModeUnavailable)
660
666
  return;
@@ -753,11 +759,17 @@ export function createTelegramThreadAwarePollingPorts(deps) {
753
759
  };
754
760
  const restoreFollowerWithOwner = async (ctx, owner) => {
755
761
  if (!(await refreshFollowerState())) {
756
- deps.recordEvent("bus", "Telegram follower auto-connect skipped: Threaded Mode is not enabled.", { phase: "follower-auto-connect-skip", reason: "thread-mode-unavailable" });
762
+ deps.recordEvent("bus", "Telegram follower auto-connect skipped: Threaded Mode is not enabled.", {
763
+ phase: "follower-auto-connect-skip",
764
+ reason: "thread-mode-unavailable",
765
+ });
757
766
  return undefined;
758
767
  }
759
768
  if (!deps.hasRememberedWorkspaceBinding?.(ctx)) {
760
- deps.recordEvent("bus", "Telegram follower auto-connect skipped: no binding for this session.", { phase: "follower-auto-connect-skip", reason: "session-binding-unavailable" });
769
+ deps.recordEvent("bus", "Telegram follower auto-connect skipped: no binding for this session.", {
770
+ phase: "follower-auto-connect-skip",
771
+ reason: "session-binding-unavailable",
772
+ });
761
773
  return undefined;
762
774
  }
763
775
  return deps.restoreFollowerWithLeader?.(ctx, owner);
@@ -892,7 +904,9 @@ export function createTelegramThreadCapabilityMonitor(deps) {
892
904
  try {
893
905
  stop();
894
906
  }
895
- catch { /* Timer shutdown must not escape the callback. */ }
907
+ catch {
908
+ /* Timer shutdown must not escape the callback. */
909
+ }
896
910
  try {
897
911
  deps.recordEvent("bus", error, { phase: "capability-monitor" });
898
912
  }
@@ -1103,12 +1117,14 @@ export async function runTelegramPollLoop(deps) {
1103
1117
  if (!deps.config.botToken)
1104
1118
  return;
1105
1119
  let consecutiveGetUpdatesConflicts = 0;
1120
+ let consecutiveFailures = 0;
1106
1121
  const retryConflict = async () => {
1107
1122
  consecutiveGetUpdatesConflicts += 1;
1108
1123
  if (consecutiveGetUpdatesConflicts >= TELEGRAM_GET_UPDATES_CONFLICT_STOP_LIMIT) {
1109
1124
  throw new TelegramPersistentGetUpdatesConflictError(consecutiveGetUpdatesConflicts);
1110
1125
  }
1111
- await deps.sleep(consecutiveGetUpdatesConflicts < TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_LIMIT
1126
+ await deps.sleep(consecutiveGetUpdatesConflicts <
1127
+ TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_LIMIT
1112
1128
  ? TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_MS
1113
1129
  : TELEGRAM_GET_UPDATES_CONFLICT_SLOW_RETRY_MS, deps.signal);
1114
1130
  };
@@ -1171,6 +1187,7 @@ export async function runTelegramPollLoop(deps) {
1171
1187
  onPhaseChange: deps.onPhaseChange,
1172
1188
  recordRuntimeEvent: deps.recordRuntimeEvent,
1173
1189
  });
1190
+ consecutiveFailures = 0;
1174
1191
  currentUpdateId = undefined;
1175
1192
  }
1176
1193
  catch (error) {
@@ -1190,8 +1207,10 @@ export async function runTelegramPollLoop(deps) {
1190
1207
  : {}),
1191
1208
  });
1192
1209
  consecutiveGetUpdatesConflicts = 0;
1210
+ consecutiveFailures += 1;
1193
1211
  deps.onErrorStatus(getTelegramPollingErrorMessage(error));
1194
- await deps.sleep(TELEGRAM_POLLING_RETRY_MS, deps.signal);
1212
+ // Cap the pause, not retry count: outages must recover without manual reconnect.
1213
+ await deps.sleep(getTelegramPollingRetryDelayMs(consecutiveFailures), deps.signal);
1195
1214
  if (deps.signal.aborted)
1196
1215
  return;
1197
1216
  deps.onStatusReset();
@@ -3,8 +3,8 @@
3
3
  * Zones: telegram outbound, native rich markdown drafts
4
4
  * Owns safe draft preview selection, runtime updates, and preview finalization
5
5
  */
6
- import { normalizeTelegramNativeMarkdown } from "./replies.js";
7
6
  import { stripTelegramCommentMarkupForPreview } from "./outbound-markup.js";
7
+ import { isTelegramMarkdownFenceClosing, normalizeTelegramNativeMarkdown, parseTelegramMarkdownFenceOpening, } from "./replies.js";
8
8
  import { getTelegramTargetThreadParams, } from "./target.js";
9
9
  import { shouldSuppressPreviewForVoice } from "./voice.js";
10
10
  const TELEGRAM_DRAFT_ID_MAX = 2_147_483_647;
@@ -73,9 +73,15 @@ export function createTelegramAssistantPreviewRuntime(deps) {
73
73
  clearPreview: async (chatId, options) => {
74
74
  if (controller.getState() !== state || !isDeliveryActive())
75
75
  return;
76
- await controller.prepareClear(chatId, { ...options, isDeliveryActive })();
76
+ await controller.prepareClear(chatId, {
77
+ ...options,
78
+ isDeliveryActive,
79
+ })();
77
80
  },
78
- finalizeMarkdownPreview: prepareTelegramNativeMarkdownPreviewFinalizer({ ...finalizerDeps, isDeliveryActive }),
81
+ finalizeMarkdownPreview: prepareTelegramNativeMarkdownPreviewFinalizer({
82
+ ...finalizerDeps,
83
+ isDeliveryActive,
84
+ }),
79
85
  };
80
86
  },
81
87
  ...createTelegramAssistantMessagePreviewHooks({
@@ -130,7 +136,10 @@ export function createTelegramPreviewController(deps) {
130
136
  createState: () => createTelegramPreviewRuntimeState(),
131
137
  resetState: () => {
132
138
  generation += 1;
133
- setState({ ...createTelegramPreviewRuntimeState(), nextDraftAt: state?.nextDraftAt });
139
+ setState({
140
+ ...createTelegramPreviewRuntimeState(),
141
+ nextDraftAt: state?.nextDraftAt,
142
+ });
134
143
  },
135
144
  invalidate: () => {
136
145
  generation += 1;
@@ -143,8 +152,15 @@ export function createTelegramPreviewController(deps) {
143
152
  sealTelegramPreviewState(state);
144
153
  const prior = state.publicationPromise ?? state.flushPromise ?? state.precedingFlush;
145
154
  let settle;
146
- state.publicationPromise = new Promise((resolve) => { settle = resolve; });
147
- return { wait: async () => { await prior?.catch(() => { }); }, settle };
155
+ state.publicationPromise = new Promise((resolve) => {
156
+ settle = resolve;
157
+ });
158
+ return {
159
+ wait: async () => {
160
+ await prior?.catch(() => { });
161
+ },
162
+ settle,
163
+ };
148
164
  },
149
165
  prepareClear: (chatId, options) => {
150
166
  const admittedState = state;
@@ -154,7 +170,8 @@ export function createTelegramPreviewController(deps) {
154
170
  return;
155
171
  await clearTelegramPreview(chatId, runtime, {
156
172
  ...options,
157
- isDeliveryActive: () => runtime.canSend?.() !== false && options?.isDeliveryActive?.() !== false,
173
+ isDeliveryActive: () => runtime.canSend?.() !== false &&
174
+ options?.isDeliveryActive?.() !== false,
158
175
  });
159
176
  };
160
177
  },
@@ -208,7 +225,8 @@ async function handleTelegramAssistantMessagePreviewStart(message, deps) {
208
225
  // Carry the previous delivery boundary; permanent text remains with its publication owner.
209
226
  next.draftId = state?.draftId;
210
227
  next.nextDraftAt = state?.nextDraftAt;
211
- next.precedingFlush = state?.publicationPromise ?? state?.flushPromise ?? state?.precedingFlush;
228
+ next.precedingFlush =
229
+ state?.publicationPromise ?? state?.flushPromise ?? state?.precedingFlush;
212
230
  deps.setState(next);
213
231
  }
214
232
  async function handleTelegramAssistantMessagePreviewUpdate(message, deps) {
@@ -280,7 +298,9 @@ export async function clearTelegramPreview(chatId, deps, options = {}) {
280
298
  if (options.isDeliveryActive?.() === false)
281
299
  return;
282
300
  deps.setState(undefined);
283
- if (state.mode === "draft" && state.draftId !== undefined && deps.canSend?.() !== false) {
301
+ if (state.mode === "draft" &&
302
+ state.draftId !== undefined &&
303
+ deps.canSend?.() !== false) {
284
304
  try {
285
305
  await deps.sendDraft(chatId, state.draftId, undefined, {
286
306
  ...getTelegramTargetThreadParams(options.target ?? { chatId }),
@@ -423,9 +443,8 @@ function updateTelegramDraftInlineStateForLine(line, state) {
423
443
  }
424
444
  }
425
445
  function updateTelegramDraftBlockStateForLine(line, state) {
426
- const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
427
446
  if (state.fence) {
428
- if (new RegExp(`^ {0,3}${state.fence.marker}{${state.fence.length},}\\s*$`).test(line)) {
447
+ if (isTelegramMarkdownFenceClosing(line, state.fence)) {
429
448
  state.fence = undefined;
430
449
  }
431
450
  return true;
@@ -435,12 +454,9 @@ function updateTelegramDraftBlockStateForLine(line, state) {
435
454
  state.displayMath = false;
436
455
  return true;
437
456
  }
438
- if (fenceMatch) {
439
- const markerText = fenceMatch[1] ?? "```";
440
- state.fence = {
441
- marker: markerText[0],
442
- length: markerText.length,
443
- };
457
+ const openingFence = parseTelegramMarkdownFenceOpening(line);
458
+ if (openingFence) {
459
+ state.fence = openingFence;
444
460
  return true;
445
461
  }
446
462
  if (line.trim() === "$$") {
@@ -523,7 +539,9 @@ async function performTelegramPreviewFlush(chatId, state, deps, options = {}) {
523
539
  state.nextDraftAt = Date.now() + (deps.minDraftIntervalMs ?? 0);
524
540
  try {
525
541
  const delivered = await deps.sendDraft(chatId, draftId, normalizeTelegramNativeMarkdown(snapshot.text), { ...getTelegramTargetThreadParams(options.target ?? { chatId }) });
526
- if (delivered === false || deps.getState() !== state || deps.canSend?.() === false)
542
+ if (delivered === false ||
543
+ deps.getState() !== state ||
544
+ deps.canSend?.() === false)
527
545
  return;
528
546
  deps.setDraftSupport("supported");
529
547
  state.mode = "draft";
@@ -584,7 +602,9 @@ export async function flushTelegramPreview(chatId, deps, options = {}) {
584
602
  });
585
603
  break;
586
604
  }
587
- } while (deps.getState() === state && !state.sealed && state.flushRequested);
605
+ } while (deps.getState() === state &&
606
+ !state.sealed &&
607
+ state.flushRequested);
588
608
  })();
589
609
  try {
590
610
  await state.flushPromise;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Process identity and liveness proofs
3
+ * Zones: shared utils, multi-instance bus, durable admission
4
+ * Owns PID liveness probes and stable Linux/macOS/Windows process-birth identity. Only an absent PID or a
5
+ * mismatched birth proof establishes death; inaccessible metadata stays unverifiable.
6
+ */
7
+ export declare function isProcessAlive(pid: number): boolean;
8
+ interface TelegramProcessBirthIdentityOptions {
9
+ platform?: NodeJS.Platform;
10
+ readProcStat?: (pid: number) => string;
11
+ readDarwinProcessStart?: (pid: number) => string;
12
+ readWindowsProcessStart?: (pid: number) => string;
13
+ }
14
+ export type TelegramProcessLiveness = "alive" | "dead" | "unverifiable";
15
+ interface TelegramProcessLivenessOptions extends TelegramProcessBirthIdentityOptions {
16
+ isProcessAlive?: (pid: number) => boolean;
17
+ }
18
+ export declare function getTelegramProcessBirthIdentity(pid: number, fallbackGeneration: number | string, options?: TelegramProcessBirthIdentityOptions): string;
19
+ export declare function getTelegramProcessLiveness(owner: {
20
+ processId: number;
21
+ processBirthId: string;
22
+ }, options?: TelegramProcessLivenessOptions): TelegramProcessLiveness;
23
+ export declare function getTelegramProcessBirthIdentityLiveness(processBirthId: string, options?: TelegramProcessLivenessOptions): TelegramProcessLiveness;
24
+ export {};
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Process identity and liveness proofs
3
+ * Zones: shared utils, multi-instance bus, durable admission
4
+ * Owns PID liveness probes and stable Linux/macOS/Windows process-birth identity. Only an absent PID or a
5
+ * mismatched birth proof establishes death; inaccessible metadata stays unverifiable.
6
+ */
7
+ import { execFileSync } from "node:child_process";
8
+ import { createHash } from "node:crypto";
9
+ import { readFileSync } from "node:fs";
10
+ import { platform as getPlatform } from "node:os";
11
+ export function isProcessAlive(pid) {
12
+ if (!Number.isInteger(pid) || pid <= 0)
13
+ return false;
14
+ try {
15
+ process.kill(pid, 0);
16
+ return true;
17
+ }
18
+ catch (error) {
19
+ // Only an absent PID proves death; permission and unexpected failures do not.
20
+ return error.code !== "ESRCH";
21
+ }
22
+ }
23
+ function readDarwinProcessStart(pid) {
24
+ return execFileSync("/bin/ps", ["-o", "lstart=", "-p", String(pid)], {
25
+ encoding: "utf8",
26
+ stdio: ["ignore", "pipe", "ignore"],
27
+ }).trim();
28
+ }
29
+ /** Creation time in UTC 100 ns ticks; PowerShell ships with every supported Windows and needs no native addon. */
30
+ function readWindowsProcessStart(pid) {
31
+ const root = process.env.SystemRoot ?? "C:\\Windows";
32
+ return execFileSync(`${root}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, [
33
+ "-NoProfile",
34
+ "-NonInteractive",
35
+ "-Command",
36
+ `(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,
37
+ ], {
38
+ encoding: "utf8",
39
+ stdio: ["ignore", "pipe", "ignore"],
40
+ timeout: 10_000,
41
+ windowsHide: true,
42
+ }).trim();
43
+ }
44
+ /** The current process never changes birth, so its slow Windows proof is read once; other PIDs are never cached. */
45
+ let ownWindowsBirthProof;
46
+ function getTelegramProcessBirthProof(pid, options = {}) {
47
+ if (!Number.isSafeInteger(pid) || pid <= 0)
48
+ return { status: "unverifiable" };
49
+ const platform = options.platform ?? getPlatform();
50
+ if (platform === "linux") {
51
+ try {
52
+ const stat = (options.readProcStat ??
53
+ ((targetPid) => readFileSync(`/proc/${targetPid}/stat`, "utf8")))(pid);
54
+ const closeParen = stat.lastIndexOf(")");
55
+ const fields = stat
56
+ .slice(closeParen + 2)
57
+ .trim()
58
+ .split(/\s+/u);
59
+ const startTicks = fields[19];
60
+ if (startTicks) {
61
+ return { status: "proven", identity: `${pid}:start:${startTicks}` };
62
+ }
63
+ }
64
+ catch {
65
+ /* inaccessible process metadata */
66
+ }
67
+ }
68
+ else if (platform === "darwin") {
69
+ try {
70
+ const startedAt = (options.readDarwinProcessStart ?? readDarwinProcessStart)(pid);
71
+ if (startedAt) {
72
+ const fingerprint = createHash("sha256")
73
+ .update(startedAt)
74
+ .digest("hex")
75
+ .slice(0, 16);
76
+ return { status: "proven", identity: `${pid}:start:${fingerprint}` };
77
+ }
78
+ }
79
+ catch {
80
+ /* inaccessible process metadata */
81
+ }
82
+ }
83
+ else if (platform === "win32") {
84
+ const injected = options.readWindowsProcessStart;
85
+ if (!injected && pid === process.pid && ownWindowsBirthProof)
86
+ return ownWindowsBirthProof;
87
+ let proof = { status: "unverifiable" };
88
+ try {
89
+ const ticks = (injected ?? readWindowsProcessStart)(pid);
90
+ if (/^\d+$/u.test(ticks))
91
+ proof = { status: "proven", identity: `${pid}:start:${ticks}` };
92
+ }
93
+ catch {
94
+ /* inaccessible process metadata, including another user's process */
95
+ }
96
+ if (!injected && pid === process.pid && proof.status === "proven")
97
+ ownWindowsBirthProof = proof;
98
+ return proof;
99
+ }
100
+ return { status: "unverifiable" };
101
+ }
102
+ export function getTelegramProcessBirthIdentity(pid, fallbackGeneration, options = {}) {
103
+ const proof = getTelegramProcessBirthProof(pid, options);
104
+ return proof.status === "proven"
105
+ ? proof.identity
106
+ : `${pid}:generation:${fallbackGeneration}`;
107
+ }
108
+ export function getTelegramProcessLiveness(owner, options = {}) {
109
+ const processAlive = options.isProcessAlive ?? isProcessAlive;
110
+ if (!processAlive(owner.processId))
111
+ return "dead";
112
+ const proof = getTelegramProcessBirthProof(owner.processId, options);
113
+ if (proof.status === "unverifiable")
114
+ return "unverifiable";
115
+ return proof.identity === owner.processBirthId ? "alive" : "dead";
116
+ }
117
+ export function getTelegramProcessBirthIdentityLiveness(processBirthId, options = {}) {
118
+ const match = /^(\d+):(start|generation):(.+)$/u.exec(processBirthId);
119
+ if (!match)
120
+ return "unverifiable";
121
+ const processId = Number(match[1]);
122
+ if (!Number.isSafeInteger(processId) || processId <= 0)
123
+ return "unverifiable";
124
+ const processAlive = options.isProcessAlive ?? isProcessAlive;
125
+ if (!processAlive(processId))
126
+ return "dead";
127
+ if (match[2] === "generation")
128
+ return "unverifiable";
129
+ const proof = getTelegramProcessBirthProof(processId, options);
130
+ if (proof.status === "unverifiable")
131
+ return "unverifiable";
132
+ return proof.identity === processBirthId ? "alive" : "dead";
133
+ }
@@ -121,13 +121,8 @@ export function buildTelegramBridgeSystemPrompt(options) {
121
121
  : "";
122
122
  return {
123
123
  systemPrompt: Array.isArray(basePrompt)
124
- ? [
125
- ...basePrompt,
126
- options.localSystemPromptSuffix + telegramSuffix,
127
- ]
128
- : basePrompt +
129
- options.localSystemPromptSuffix +
130
- telegramSuffix,
124
+ ? [...basePrompt, options.localSystemPromptSuffix + telegramSuffix]
125
+ : basePrompt + options.localSystemPromptSuffix + telegramSuffix,
131
126
  };
132
127
  }
133
128
  export function createTelegramBeforeAgentStartHook(options = {}) {
@@ -188,9 +188,7 @@ export declare function createTelegramQueueAdmissionReceipt(options: {
188
188
  scope: string;
189
189
  sourceUpdateIds: readonly number[];
190
190
  }): TelegramQueueAdmissionReceipt | undefined;
191
- export declare function isTelegramQueueItemDurablyAdmitted<TContext = unknown>(item: TelegramQueueItem<TContext>, isReceiptCommitted: (receipt: TelegramQueueAdmissionReceipt) => boolean): boolean;
192
191
  export declare function getTelegramQueueLaneContract(lane: TelegramQueueLane): TelegramQueueLaneContract;
193
- export declare function getTelegramQueueItemAdmissionMode(item: Pick<TelegramQueueItem, "queueLane">): TelegramQueueAdmissionMode;
194
192
  export declare function isTelegramQueueItemAdmissionValid(item: Pick<TelegramQueueItem, "kind" | "queueLane">): boolean;
195
193
  export declare function assertTelegramQueueItemAdmissionValid(item: Pick<TelegramQueueItem, "kind" | "queueLane" | "admissionReceipts">): void;
196
194
  export declare function createTelegramQueueStore<TContext = unknown>(initialItems?: TelegramQueueItem<TContext>[]): TelegramQueueStateStore<TContext>;
@@ -202,6 +200,13 @@ export declare function createTelegramTransportStampedQueueStore<TContext>(store
202
200
  export declare function countExecutableTelegramQueueItems<TContext = unknown>(items: readonly TelegramQueueItem<TContext>[]): number;
203
201
  export declare function createTelegramQueueItemCountGetter<TContext = unknown>(store: Pick<TelegramQueueStore<TContext>, "getQueuedItems">): () => number;
204
202
  export declare function createTelegramActiveTurnStore<TTurn extends PendingTelegramTurn = PendingTelegramTurn>(): TelegramActiveTurnStore<TTurn>;
203
+ /** Current captured targets only; skipped/receipt-held items remain work, never historical census. */
204
+ export declare function observeTelegramTargetQueueWork(target: TelegramQueueTarget & {
205
+ threadId: number;
206
+ }, activeTurn: Pick<TelegramQueueItemBase, "chatId" | "target"> | undefined, items: readonly Pick<TelegramQueueItemBase, "chatId" | "target">[]): {
207
+ protected: boolean;
208
+ unknown: boolean;
209
+ };
205
210
  export declare function partitionTelegramQueueItemsForHistory<TContext = unknown>(items: TelegramQueueItem<TContext>[]): {
206
211
  historyTurns: PendingTelegramTurn[];
207
212
  remainingItems: TelegramQueueItem<TContext>[];
@@ -586,7 +591,7 @@ export interface TelegramSessionShutdownRuntimeDeps<TQueueItem> {
586
591
  clearAbort: () => void;
587
592
  stopPolling: () => Promise<void>;
588
593
  }
589
- export interface TelegramSessionLifecycleHookRuntimeDeps<TContext, TQueueItem, TModel = unknown> extends TelegramRuntimeEventRecorderPort {
594
+ export interface TelegramSessionLifecycleHookRuntimeDeps<TContext, TQueueItem, TModel = unknown> extends TelegramRuntimeEventRecorderPort, Omit<TelegramSessionShutdownRuntimeDeps<TQueueItem>, "isSessionActive" | "discardQueuedItems" | "applyState"> {
590
595
  getCurrentModel: (ctx: TContext) => TModel | undefined;
591
596
  loadConfig: () => Promise<void>;
592
597
  applySessionStartState: (state: TelegramSessionStartState<TModel>) => void;
@@ -594,30 +599,20 @@ export interface TelegramSessionLifecycleHookRuntimeDeps<TContext, TQueueItem, T
594
599
  prepareTempDir: () => Promise<unknown>;
595
600
  updateStatus: (ctx: TContext) => void;
596
601
  isSessionActive?: (ctx: TContext) => boolean;
597
- unbindDeferredDispatchContext?: () => void;
598
602
  discardQueuedItems?: (ctx: TContext) => void;
599
603
  applySessionShutdownState: (state: TelegramSessionShutdownState<TQueueItem>) => void;
600
- clearPendingMediaGroups: () => void;
601
- clearModelMenuState: () => void;
602
- getActiveTurnChatId: () => number | undefined;
603
- getActiveTurnTarget?: () => TelegramQueueTarget | undefined;
604
- clearPreview: (chatId: number, options?: {
605
- target?: TelegramQueueTarget;
606
- }) => Promise<void>;
607
- previewShutdownTimeoutMs?: number;
608
- clearActiveTurn: () => void;
609
- clearAbort: () => void;
610
- stopPolling: () => Promise<void>;
611
604
  }
612
605
  export type TelegramSessionLifecycleHookEvent = unknown;
613
606
  export declare function createTelegramSessionStateApplier<TQueueItem, TModel>(deps: TelegramSessionStateApplierDeps<TQueueItem, TModel>): TelegramSessionStateApplier<TQueueItem, TModel>;
614
607
  export interface TelegramQueueMutationRuntimeDeps<TContext> extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
615
608
  ctx: TContext;
609
+ hasPendingDispatch?: () => boolean;
616
610
  allocateLaneOrder?: () => number;
617
611
  onItemsDiscarded?: (items: readonly TelegramQueueItem<TContext>[], ctx: TContext) => void;
618
612
  updateStatus: (ctx: TContext) => void;
619
613
  }
620
614
  export interface TelegramQueueMutationControllerDeps<TContext> extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
615
+ hasPendingDispatch?: () => boolean;
621
616
  allocateLaneOrder?: () => number;
622
617
  onItemsDiscarded?: (items: readonly TelegramQueueItem<TContext>[], ctx: TContext) => void;
623
618
  updateStatus: (ctx: TContext) => void;
@@ -646,6 +641,8 @@ export interface TelegramPromptEnqueueRuntimeDeps<TMessage, TContext = unknown>
646
641
  updateStatus: () => void;
647
642
  dispatchNextQueuedTelegramTurn: () => void;
648
643
  assertExecutionCurrent?: () => void;
644
+ /** Input-specific relocation must not fold or replace unrelated queued work. */
645
+ preserveQueued?: boolean;
649
646
  onQueued?: (turn: PendingTelegramTurn) => void;
650
647
  }
651
648
  export interface TelegramPromptEnqueueControllerDeps<TMessage, TContext = unknown> extends TelegramQueueStore<TContext> {
@@ -658,7 +655,10 @@ export interface TelegramPromptEnqueueControllerDeps<TMessage, TContext = unknow
658
655
  assertExecutionCurrent?: (messages: TMessage[]) => void;
659
656
  }
660
657
  export interface TelegramPromptEnqueueController<TMessage, TContext = unknown> {
661
- enqueue: (messages: TMessage[], ctx: TContext, onQueued?: (turn: PendingTelegramTurn) => void) => Promise<PendingTelegramTurn>;
658
+ enqueue: (messages: TMessage[], ctx: TContext, onQueued?: (turn: PendingTelegramTurn) => void, options?: {
659
+ assertCurrent?: () => void;
660
+ preserveQueued?: boolean;
661
+ }) => Promise<PendingTelegramTurn>;
662
662
  }
663
663
  export declare function buildTelegramSessionStartState<TModel = unknown>(currentModel: TModel | undefined): TelegramSessionStartState<TModel>;
664
664
  export declare function buildTelegramSessionShutdownState<TQueueItem>(): TelegramSessionShutdownState<TQueueItem>;