@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
@@ -6,15 +6,15 @@
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { mkdir } from "node:fs/promises";
8
8
  import { join } from "node:path";
9
- import { resolveTelegramAttachmentsDir } from "./paths.js";
10
- import * as Replies from "./replies.js";
11
- import { isTelegramApiCommitUnknownError } from "./telegram-api.js";
12
9
  import { planTelegramButtonReply, } from "./outbound-buttons.js";
13
10
  import { planTelegramVoiceReply, stripTelegramCommentMarkupForDelivery, } from "./outbound-markup.js";
14
11
  import { createTelegramVoiceReplySender as createTelegramVoiceReplySenderWithPorts } from "./outbound-voice.js";
12
+ import { resolveTelegramAttachmentsDir } from "./paths.js";
13
+ import * as Replies from "./replies.js";
14
+ import { assertTelegramApiCallAuthority, isTelegramApiCommitUnknownError, } from "./telegram-api.js";
15
15
  const OUTBOUND_HANDLER_REGISTRY_KEY = "__piTelegramOutboundHandlers__";
16
16
  const VOICE_EVENT_RECORDER_KEY = "__piTelegramVoiceEventRecorder__";
17
- import { buildCommandTemplateInvocation, expandCommandTemplateConfigs, substituteCommandTemplateToken, } from "./command-templates.js";
17
+ import { buildCommandTemplateInvocation, createCommandTemplateRecovery, getCommandTemplateCompositionSteps, shouldRunCommandTemplateConfig, getCommandTemplateConfiguredTimeout, getCommandTemplateStepTimeout, resolveCommandTemplateNumericField, } from "./command-templates.js";
18
18
  const DEFAULT_VOICE_TIMEOUT_MS = 120_000;
19
19
  export function bindTelegramRuntimeEventRecorder(recorder) {
20
20
  globalThis[VOICE_EVENT_RECORDER_KEY] = recorder;
@@ -25,7 +25,6 @@ export function recordTelegramRuntimeEvent(category, error, details) {
25
25
  recorder(category, error, details);
26
26
  }
27
27
  }
28
- export { normalizeMarkdownAfterVoiceExtraction, planTelegramVoiceReply, stripTelegramCommentMarkupForDelivery, stripTelegramCommentMarkupForPreview, stripTelegramVoiceMarkupForPreview, } from "./outbound-markup.js";
29
28
  // --- Programmatic Outbound Handler Registry Runtime ---
30
29
  function getOrCreateOutboundHandlerRegistry() {
31
30
  const existing = globalThis[OUTBOUND_HANDLER_REGISTRY_KEY];
@@ -62,35 +61,8 @@ function getTelegramOutboundProgrammaticHandlers(kind) {
62
61
  return [...(registry.handlers.get(kind) ?? [])];
63
62
  }
64
63
  // --- Voice Reply Timeout Helpers ---
65
- function resolveOutboundNumericControlField(value, values, label) {
66
- if (value === undefined)
67
- return undefined;
68
- const resolved = typeof value === "string"
69
- ? substituteCommandTemplateToken(value, values, label)
70
- : value;
71
- if (resolved === "")
72
- return undefined;
73
- const numeric = Number(resolved);
74
- if (!Number.isFinite(numeric) || numeric < 0)
75
- throw new Error(`Command template ${label} must be a non-negative number.`);
76
- return numeric;
77
- }
78
- function getVoiceReplyConfiguredTimeout(config) {
79
- const timeout = typeof config === "string" ? undefined : config?.timeout;
80
- return resolveOutboundNumericControlField(timeout, {}, "timeout");
81
- }
82
64
  function getVoiceReplyTimeout(config) {
83
- return getVoiceReplyConfiguredTimeout(config) ?? DEFAULT_VOICE_TIMEOUT_MS;
84
- }
85
- function getRemainingVoiceReplyTimeout(timeout, startedAt) {
86
- return Math.max(1, timeout - (Date.now() - startedAt));
87
- }
88
- function getVoiceReplyCompositionStepTimeout(handlerTimeout, step, startedAt) {
89
- const remaining = getRemainingVoiceReplyTimeout(handlerTimeout, startedAt);
90
- const stepTimeout = getVoiceReplyConfiguredTimeout(step);
91
- return stepTimeout === undefined
92
- ? remaining
93
- : Math.min(stepTimeout, remaining);
65
+ return (getCommandTemplateConfiguredTimeout(config) ?? DEFAULT_VOICE_TIMEOUT_MS);
94
66
  }
95
67
  function formatVoiceReplyExecutionFailure(label, result) {
96
68
  const parts = [
@@ -102,6 +74,9 @@ function formatVoiceReplyExecutionFailure(label, result) {
102
74
  parts.push(`stdout:\n${result.stdout.trimEnd()}`);
103
75
  return parts.join("\n\n");
104
76
  }
77
+ function withCommandTemplateRecovery(recover) {
78
+ return recover ? { recover } : {};
79
+ }
105
80
  async function runVoiceReplyCommand(label, config, values, options) {
106
81
  if (!options.execCommand) {
107
82
  throw new Error("execCommand is required for command template execution");
@@ -115,7 +90,12 @@ async function runVoiceReplyCommand(label, config, values, options) {
115
90
  timeout: options.timeout,
116
91
  ...(typeof config === "object" && config.retry !== undefined
117
92
  ? {
118
- retry: resolveOutboundNumericControlField(config.retry, {}, "retry"),
93
+ retry: resolveCommandTemplateNumericField(config.retry, {}, "retry"),
94
+ ...withCommandTemplateRecovery(createCommandTemplateRecovery(config, values, {
95
+ cwd: options.cwd,
96
+ timeout: options.timeout,
97
+ execCommand: options.execCommand,
98
+ })),
119
99
  }
120
100
  : {}),
121
101
  ...(options.stdin !== undefined ? { stdin: options.stdin } : {}),
@@ -150,12 +130,6 @@ export function findTelegramOutboundHandlers(handlers, type) {
150
130
  typeof handler === "object" &&
151
131
  outboundHandlerMatchesType(handler, type));
152
132
  }
153
- function getTelegramVoiceHandlerCompositionSteps(handler) {
154
- if (Array.isArray(handler.template)) {
155
- return expandCommandTemplateConfigs(handler);
156
- }
157
- return [];
158
- }
159
133
  function extractVoiceReplyPath(stdout) {
160
134
  const path = stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
161
135
  if (!path)
@@ -194,15 +168,19 @@ async function generateTelegramVoiceReplyFileWithHandler(text, options) {
194
168
  mp3Path: join(options.tempDir, `${artifactId}-voice.mp3`),
195
169
  oggPath: join(options.tempDir, `${artifactId}-voice.ogg`),
196
170
  });
197
- const steps = getTelegramVoiceHandlerCompositionSteps(options.handler);
171
+ if (!shouldRunCommandTemplateConfig(options.handler, values))
172
+ return undefined;
173
+ const steps = getCommandTemplateCompositionSteps(options.handler);
198
174
  if (steps.length > 0) {
199
175
  const startedAt = Date.now();
200
176
  let stdout = text;
201
177
  for (const [index, step] of steps.entries()) {
178
+ if (!shouldRunCommandTemplateConfig(step, values))
179
+ continue;
202
180
  try {
203
181
  const result = await runVoiceReplyCommand(`Outbound voice template step ${index + 1}`, step, values, {
204
182
  cwd: options.cwd,
205
- timeout: getVoiceReplyCompositionStepTimeout(options.timeout, step, startedAt),
183
+ timeout: getCommandTemplateStepTimeout(options.timeout, step, Date.now() - startedAt),
206
184
  execCommand: options.execCommand,
207
185
  stdin: stdout,
208
186
  });
@@ -243,15 +221,19 @@ function getOutboundTextTemplateValues(text) {
243
221
  }
244
222
  async function transformTelegramOutboundTextWithHandler(text, options) {
245
223
  const values = getOutboundTextTemplateValues(text);
246
- const steps = getTelegramVoiceHandlerCompositionSteps(options.handler);
224
+ if (!shouldRunCommandTemplateConfig(options.handler, values))
225
+ return text;
226
+ const steps = getCommandTemplateCompositionSteps(options.handler);
247
227
  if (steps.length > 0) {
248
228
  const startedAt = Date.now();
249
229
  let stdout = text;
250
230
  for (const [index, step] of steps.entries()) {
231
+ if (!shouldRunCommandTemplateConfig(step, values))
232
+ continue;
251
233
  try {
252
234
  const result = await runVoiceReplyCommand(`Outbound text template step ${index + 1}`, step, values, {
253
235
  cwd: options.cwd,
254
- timeout: getVoiceReplyCompositionStepTimeout(getVoiceReplyTimeout(options.handler), step, startedAt),
236
+ timeout: getCommandTemplateStepTimeout(getVoiceReplyTimeout(options.handler), step, Date.now() - startedAt),
255
237
  execCommand: options.execCommand,
256
238
  stdin: stdout,
257
239
  });
@@ -328,17 +310,33 @@ async function transformTelegramOutboundTextReply(text, options) {
328
310
  return { text: transformedText, ...(replyMarkup ? { replyMarkup } : {}) };
329
311
  }
330
312
  export function createTelegramOutboundTextReplyRuntime(deps) {
313
+ const sendTextReply = deps.sendTextReply, sendMarkdownReply = deps.sendMarkdownReply;
331
314
  return {
332
315
  sendTextReply: async (chatId, replyToMessageId, text, options) => {
316
+ const capturedOptions = options && {
317
+ ...options,
318
+ ...(options.target ? { target: { ...options.target } } : {}),
319
+ };
320
+ const assertAuthority = capturedOptions?.assertAuthority;
321
+ assertTelegramApiCallAuthority(assertAuthority, false);
333
322
  const transformed = await transformTelegramOutboundText(text, {
334
323
  handlers: deps.getHandlers?.(),
335
324
  cwd: deps.cwd,
336
325
  execCommand: deps.execCommand,
337
326
  recordRuntimeEvent: deps.recordRuntimeEvent,
338
327
  });
339
- return deps.sendTextReply(chatId, replyToMessageId, transformed, options);
328
+ assertTelegramApiCallAuthority(assertAuthority, false);
329
+ const messageId = await sendTextReply(chatId, replyToMessageId, transformed, capturedOptions);
330
+ assertTelegramApiCallAuthority(assertAuthority, true);
331
+ return messageId;
340
332
  },
341
333
  sendMarkdownReply: async (chatId, replyToMessageId, markdown, options) => {
334
+ const capturedOptions = options && {
335
+ ...options,
336
+ ...(options.target ? { target: { ...options.target } } : {}),
337
+ };
338
+ const assertAuthority = capturedOptions?.assertAuthority;
339
+ assertTelegramApiCallAuthority(assertAuthority, false);
342
340
  const deliveryMarkdown = stripTelegramCommentMarkupForDelivery(markdown);
343
341
  if (!deliveryMarkdown)
344
342
  return undefined;
@@ -347,14 +345,17 @@ export function createTelegramOutboundTextReplyRuntime(deps) {
347
345
  cwd: deps.cwd,
348
346
  execCommand: deps.execCommand,
349
347
  recordRuntimeEvent: deps.recordRuntimeEvent,
350
- replyMarkup: options?.replyMarkup,
348
+ replyMarkup: capturedOptions?.replyMarkup,
351
349
  });
352
- return deps.sendMarkdownReply(chatId, replyToMessageId, transformed.text, {
353
- ...options,
350
+ assertTelegramApiCallAuthority(assertAuthority, false);
351
+ const messageId = await sendMarkdownReply(chatId, replyToMessageId, transformed.text, {
352
+ ...capturedOptions,
354
353
  ...(transformed.replyMarkup
355
354
  ? { replyMarkup: transformed.replyMarkup }
356
355
  : {}),
357
356
  });
357
+ assertTelegramApiCallAuthority(assertAuthority, true);
358
+ return messageId;
358
359
  },
359
360
  };
360
361
  }
@@ -378,7 +379,12 @@ export function createTelegramOutboundTextPreviewRuntime(deps) {
378
379
  finalizeMarkdownPreview: wrap(deps.finalizeMarkdownPreview),
379
380
  preparePreviewDelivery(isDeliveryActive) {
380
381
  const prepared = deps.preparePreviewDelivery?.(isDeliveryActive);
381
- return prepared ? { ...prepared, finalizeMarkdownPreview: wrap(prepared.finalizeMarkdownPreview) } : undefined;
382
+ return prepared
383
+ ? {
384
+ ...prepared,
385
+ finalizeMarkdownPreview: wrap(prepared.finalizeMarkdownPreview),
386
+ }
387
+ : undefined;
382
388
  },
383
389
  };
384
390
  }
@@ -431,8 +437,12 @@ export function createTelegramOutboundReplyPlanner(store, getRenderingMode = ()
431
437
  // --- Outbound Reply Artifacts ---
432
438
  export function createTelegramOutboundReplyArtifactSender(deps) {
433
439
  return async (turn, plan, options) => {
434
- const isDeliveryActive = () => deps.isDeliveryActive?.() !== false && options?.isDeliveryActive?.() !== false;
435
- const sendVoiceReply = createTelegramVoiceReplySender({ ...deps, isDeliveryActive });
440
+ const isDeliveryActive = () => deps.isDeliveryActive?.() !== false &&
441
+ options?.isDeliveryActive?.() !== false;
442
+ const sendVoiceReply = createTelegramVoiceReplySender({
443
+ ...deps,
444
+ isDeliveryActive,
445
+ });
436
446
  // Normalize voice replies: either use explicit voiceReplies array or fall back to voiceText
437
447
  const voiceReplies = plan.voiceReplies?.length
438
448
  ? plan.voiceReplies
@@ -52,12 +52,16 @@ export function createTelegramBusMessageOwnershipRuntime(deps) {
52
52
  const record = store.get(chatId, messageId);
53
53
  if (!record?.ownerGeneration || !record.recipientBindingKey)
54
54
  return record;
55
- const follower = deps.listFollowers().find((candidate) => candidate.instanceId === record.instanceId &&
55
+ const follower = deps
56
+ .listFollowers()
57
+ .find((candidate) => candidate.instanceId === record.instanceId &&
56
58
  candidate.registrationGeneration === record.ownerGeneration &&
57
59
  candidate.profileKey === record.recipientBindingKey);
58
60
  // Protocol is live registration authority, not message-cache history.
59
61
  // Keep an incomplete known owner foreign so the forwarder fails closed.
60
- return follower?.protocol?.capabilities.includes(TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION) ? { ...record, protocolIdentity: follower.protocol } : record;
62
+ return follower?.protocol?.capabilities.includes(TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION)
63
+ ? { ...record, protocolIdentity: follower.protocol }
64
+ : record;
61
65
  },
62
66
  recordLocal(input) {
63
67
  return store.record({ ...input, instanceId: deps.instanceId });
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Telegram bridge path resolution for Pi-compatible runtimes
3
+ * Zones: telemetry paths, filesystem, runtime identity
4
+ * Owns agent-dir detection and extension-local path derivation
5
+ */
1
6
  export declare const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
2
7
  export interface TelegramAgentDirResolutionInput {
3
8
  env?: Partial<Pick<NodeJS.ProcessEnv, "PI_CODING_AGENT_DIR">>;
@@ -2,16 +2,11 @@
2
2
  * Telegram bridge path resolution for Pi-compatible runtimes
3
3
  * Zones: telemetry paths, filesystem, runtime identity
4
4
  * Owns agent-dir detection and extension-local path derivation
5
- *
6
- * This domain is path-only: it resolves directories and file paths from
7
- * environment and runtime identity. Its only filesystem read canonicalizes the
8
- * existing agent-dir prefix; it does not read config, manage state, or import
9
- * broader Telegram domains.
10
5
  */
11
6
  import { createHash } from "node:crypto";
12
7
  import { realpathSync } from "node:fs";
13
8
  import { homedir } from "node:os";
14
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from "node:path";
15
10
  export const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
16
11
  /**
17
12
  * Resolve the agent data directory for the current Pi-compatible runtime.
@@ -41,9 +36,13 @@ export function resolveAgentDir(input = {}) {
41
36
  * Equality proves spelling only, not file identity, consumer closure or migration readiness.
42
37
  */
43
38
  export function requireTelegramStoragePathReference(path, expectedPath) {
44
- if (typeof path !== "string" || typeof expectedPath !== "string" ||
45
- !isAbsolute(path) || resolve(path) !== path ||
46
- !isAbsolute(expectedPath) || resolve(expectedPath) !== expectedPath || path !== expectedPath) {
39
+ if (typeof path !== "string" ||
40
+ typeof expectedPath !== "string" ||
41
+ !isAbsolute(path) ||
42
+ resolve(path) !== path ||
43
+ !isAbsolute(expectedPath) ||
44
+ resolve(expectedPath) !== expectedPath ||
45
+ path !== expectedPath) {
47
46
  throw new Error("Telegram storage reference does not match its approved absolute path.");
48
47
  }
49
48
  return path;
@@ -55,11 +54,12 @@ export function resolveTelegramConfigPath() {
55
54
  /**
56
55
  * Resolve symlinks in the longest existing prefix of `path` and keep the missing suffix. Strict journal reads require
57
56
  * canonical anchors, so a symlinked agent directory (for example macOS `/var` → `/private/var`) must not leak in.
58
- * Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. Windows keeps its
59
- * spelling: strict journal reads are POSIX-only there, and resolving a drive-less root would add a drive letter.
57
+ * Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. On Windows only
58
+ * drive-letter and UNC paths resolve; resolving a drive-less root would add the current drive.
60
59
  */
61
60
  function canonicalizeExistingPrefix(path) {
62
- if (!isAbsolute(path) || process.platform === "win32")
61
+ if (!isAbsolute(path) ||
62
+ (process.platform === "win32" && !/^(?:[a-zA-Z]:[\\/]|\\\\)/u.test(path)))
63
63
  return path;
64
64
  const absolute = resolve(path);
65
65
  const missing = [];
@@ -68,7 +68,8 @@ function canonicalizeExistingPrefix(path) {
68
68
  return join(realpathSync(current), ...missing.reverse());
69
69
  }
70
70
  catch (error) {
71
- if (error.code !== "ENOENT" || dirname(current) === current)
71
+ if (error.code !== "ENOENT" ||
72
+ dirname(current) === current)
72
73
  return absolute;
73
74
  missing.push(basename(current));
74
75
  }
@@ -140,7 +141,10 @@ export function getTelegramJournalPublicationPaths(path, runtimeDir) {
140
141
  requireTelegramStoragePathReference(runtimeDir, join(dirname(dirname(path)), "runtime"));
141
142
  if (basename(dirname(path)) !== "journals")
142
143
  throw new Error("Telegram service journal must use its journals namespace.");
143
- return { transactionPath: join(runtimeDir, `${basename(path)}.transaction`), temporaryBasePath: join(runtimeDir, basename(path)) };
144
+ return {
145
+ transactionPath: join(runtimeDir, `${basename(path)}.transaction`),
146
+ temporaryBasePath: join(runtimeDir, basename(path)),
147
+ };
144
148
  }
145
149
  /** Durable inactive Thread cleanup work-set journal. */
146
150
  export function resolveTelegramThreadCleanupWorkPath(agentDir = resolveAgentDir(), profileName) {
@@ -186,12 +190,17 @@ export function encodeTelegramSessionDirectoryName(sessionId) {
186
190
  let name = "";
187
191
  bytes.forEach((byte, index) => {
188
192
  const char = String.fromCharCode(byte);
189
- const safe = /[a-z0-9_-]/u.test(char) || (char === "." && index > 0 && index < bytes.length - 1);
190
- name += safe ? char : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
193
+ const safe = /[a-z0-9_-]/u.test(char) ||
194
+ (char === "." && index > 0 && index < bytes.length - 1);
195
+ name += safe
196
+ ? char
197
+ : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
191
198
  });
192
199
  if (WINDOWS_RESERVED_NAME.test(name))
193
200
  name = `%${name.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}${name.slice(1)}`;
194
- return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH ? name : undefined;
201
+ return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH
202
+ ? name
203
+ : undefined;
195
204
  }
196
205
  /** Inverse of `encodeTelegramSessionDirectoryName`; undefined unless the name is exactly canonical. */
197
206
  export function decodeTelegramSessionDirectoryName(name) {
@@ -210,7 +219,9 @@ export function decodeTelegramSessionDirectoryName(name) {
210
219
  bytes.push(name.charCodeAt(index));
211
220
  }
212
221
  const decoded = Buffer.from(bytes).toString("utf8");
213
- return encodeTelegramSessionDirectoryName(decoded) === name ? decoded : undefined;
222
+ return encodeTelegramSessionDirectoryName(decoded) === name
223
+ ? decoded
224
+ : undefined;
214
225
  }
215
226
  /** Root of every session folder (<agentDir>/tmp/pi-telegram/sessions). */
216
227
  export function resolveTelegramSessionsDir(agentDir = resolveAgentDir()) {
@@ -227,7 +238,10 @@ export function resolveTelegramSessionDir(sessionId, agentDir = resolveAgentDir(
227
238
  export function getTelegramRecipientJournalHash(recipientBindingKey) {
228
239
  if (!recipientBindingKey)
229
240
  throw new Error("Telegram session journal binding key is required.");
230
- return createHash("sha256").update(recipientBindingKey).digest("hex").slice(0, 16);
241
+ return createHash("sha256")
242
+ .update(recipientBindingKey)
243
+ .digest("hex")
244
+ .slice(0, 16);
231
245
  }
232
246
  /** Durable per-session journal (`sessions/<session id>/journal.<hash>[.<profile>].json`); the name never reveals a role. */
233
247
  export function resolveTelegramSessionJournalPath(sessionId, recipientBindingKey, agentDir = resolveAgentDir(), profileName) {
@@ -240,8 +254,10 @@ export function resolveTelegramSessionPollingJournalPath(sessionId, agentDir = r
240
254
  /** Accept only a canonical `sessions/<id>/inbox[.<profile>].json` beneath this agent's runtime. */
241
255
  export function isTelegramSessionPollingJournalPath(path, agentDir = resolveAgentDir()) {
242
256
  const parts = relative(resolveTelegramSessionsDir(agentDir), path).split(sep);
243
- return isAbsolute(path) && parts.length === 2 && decodeTelegramSessionDirectoryName(parts[0]) !== undefined &&
244
- /^inbox(?:\.[a-zA-Z0-9._-]+)?\.json$/u.test(parts[1]);
257
+ return (isAbsolute(path) &&
258
+ parts.length === 2 &&
259
+ decodeTelegramSessionDirectoryName(parts[0]) !== undefined &&
260
+ /^inbox(?:\.[a-zA-Z0-9._-]+)?\.json$/u.test(parts[1]));
245
261
  }
246
262
  /** Runtime event log (<agentDir>/tmp/pi-telegram/logs.jsonl). */
247
263
  export function resolveTelegramRuntimeLogPath(agentDir = resolveAgentDir()) {
@@ -42,6 +42,8 @@ export interface PiSettingsManager {
42
42
  }
43
43
  export type PiSlashCommandInfo = SlashCommandInfo;
44
44
  export type PiRunMode = "tui" | "rpc" | "json" | "print";
45
+ /** Pi rejects a context captured before session replacement with a stale-context error; this is not a domain failure. */
46
+ export declare function isPiStaleContextError(error: unknown): boolean;
45
47
  export declare function getExtensionContextMode(ctx: unknown): PiRunMode | undefined;
46
48
  export declare function isExtensionContextPassiveRunMode(ctx: unknown): boolean;
47
49
  export declare function canStartPollingInExtensionContext(ctx: unknown): boolean;
@@ -7,6 +7,12 @@ import { SettingsManager, } from "@earendil-works/pi-coding-agent";
7
7
  function isPiRunMode(value) {
8
8
  return (value === "tui" || value === "rpc" || value === "json" || value === "print");
9
9
  }
10
+ /** Pi rejects a context captured before session replacement with a stale-context error; this is not a domain failure. */
11
+ export function isPiStaleContextError(error) {
12
+ return (error instanceof Error &&
13
+ (error.message.includes("stale after session") ||
14
+ error.message.includes("stale ctx")));
15
+ }
10
16
  export function getExtensionContextMode(ctx) {
11
17
  const mode = typeof ctx === "object" && ctx !== null
12
18
  ? ctx.mode
@@ -50,7 +56,8 @@ export function createExtensionApiRuntimePorts(api) {
50
56
  function readEnabledModels(value) {
51
57
  if (value === undefined)
52
58
  return undefined;
53
- if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
59
+ if (Array.isArray(value) &&
60
+ value.every((entry) => typeof entry === "string")) {
54
61
  return [...value];
55
62
  }
56
63
  throw new TypeError("Host settings enabledModels must be a string array or undefined.");
@@ -28,6 +28,8 @@ export declare class TelegramPersistentGetUpdatesConflictError extends Error {
28
28
  readonly count: number;
29
29
  constructor(count: number);
30
30
  }
31
+ /** Exponential backoff for non-conflict polling/admission failures. */
32
+ export declare function getTelegramPollingRetryDelayMs(consecutiveFailures: number): number;
31
33
  export declare class TelegramGetUpdatesTimeoutError extends Error {
32
34
  readonly timeoutMs: number;
33
35
  constructor(timeoutMs: number);
@@ -223,13 +225,8 @@ export interface TelegramThreadAwarePollingPorts<TContext, TOwner> {
223
225
  restoreFollowerWithOwner: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
224
226
  stopFollowerRegistration: () => void;
225
227
  }
226
- export interface TelegramThreadAwarePollingDeps<TContext, TOwner> extends TelegramStartupThreadCapabilityProbeDeps {
227
- lifecycle?: TelegramThreadCapabilityLifecycle;
228
- isBusRuntimeEnabled: () => boolean;
229
- isTopicModeUnavailableError: (error: unknown) => boolean;
230
- getPollingStartedWithTelegramBus: () => boolean;
231
- setPollingStartedWithTelegramBus: (started: boolean) => void;
232
- setForceFreshLeaderThreadOnNextStart: (forceFresh: boolean) => void;
228
+ /** Classic/leader polling and follower registration controls that thread-aware polling switches between. */
229
+ export interface TelegramThreadAwarePollingControls<TContext, TOwner> {
233
230
  startClassicPolling: (ctx: TContext) => MaybePromise<void>;
234
231
  stopClassicPolling: () => Promise<void>;
235
232
  startBusLeaderPolling: (ctx: TContext) => Promise<void>;
@@ -240,25 +237,21 @@ export interface TelegramThreadAwarePollingDeps<TContext, TOwner> extends Telegr
240
237
  restoreFollowerWithLeader?: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
241
238
  hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
242
239
  stopFollowerRegistration: () => void;
240
+ isBusRuntimeEnabled: () => boolean;
241
+ isTopicModeUnavailableError: (error: unknown) => boolean;
243
242
  }
244
- export interface TelegramThreadCapabilityOrchestrationDeps<TContext, TOwner> extends TelegramThreadCapabilityReaderDeps {
243
+ export interface TelegramThreadAwarePollingDeps<TContext, TOwner> extends TelegramStartupThreadCapabilityProbeDeps, TelegramThreadAwarePollingControls<TContext, TOwner> {
244
+ lifecycle?: TelegramThreadCapabilityLifecycle;
245
+ getPollingStartedWithTelegramBus: () => boolean;
246
+ setPollingStartedWithTelegramBus: (started: boolean) => void;
247
+ setForceFreshLeaderThreadOnNextStart: (forceFresh: boolean) => void;
248
+ }
249
+ export interface TelegramThreadCapabilityOrchestrationDeps<TContext, TOwner> extends TelegramThreadCapabilityReaderDeps, TelegramThreadAwarePollingControls<TContext, TOwner> {
245
250
  state: TelegramThreadCapabilityStateRuntime;
246
251
  topicTargetStore: TelegramThreadCapabilityStore;
247
- isBusRuntimeEnabled: () => boolean;
248
252
  ownsLock: (ctx: TContext) => boolean;
249
253
  isFollowerRegistered?: () => boolean;
250
- startClassicPolling: (ctx: TContext) => MaybePromise<void>;
251
- stopClassicPolling: () => Promise<void>;
252
- startBusLeaderPolling: (ctx: TContext) => Promise<void>;
253
- stopBusLeaderPolling: () => Promise<void>;
254
- startLeaderHealth: () => void;
255
- stopLeaderHealth: () => void;
256
- registerFollowerWithLeader: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
257
- restoreFollowerWithLeader?: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
258
- hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
259
254
  suspendLiveThreadTarget?: () => void;
260
- stopFollowerRegistration: () => void;
261
- isTopicModeUnavailableError: (error: unknown) => boolean;
262
255
  updateStatus: (ctx: TContext) => void;
263
256
  recordEvent: (category: string, message: unknown, details?: Record<string, unknown>) => void;
264
257
  }
@@ -306,10 +299,8 @@ export interface TelegramPollingBatchAdmissionDeps<TUpdate extends TelegramUpdat
306
299
  onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
307
300
  }
308
301
  export declare function admitTelegramPollingUpdateBatch<TUpdate extends TelegramUpdate>(deps: TelegramPollingBatchAdmissionDeps<TUpdate>): Promise<TelegramPollingBatchAdmissionResult>;
309
- export interface TelegramPollLoopDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends TelegramRuntimeEventRecorderPort {
310
- ctx: TContext;
311
- signal: AbortSignal;
312
- config: TelegramPollingConfig;
302
+ /** Bot API, journal and progress ports shared by one poll loop and its restartable runner. */
303
+ export interface TelegramPollLoopTransportPorts<TUpdate extends TelegramUpdate> extends TelegramRuntimeEventRecorderPort {
313
304
  deleteWebhook: (signal: AbortSignal) => Promise<unknown>;
314
305
  getUpdates: (body: Record<string, unknown>, signal: AbortSignal) => Promise<TUpdate[]>;
315
306
  getUpdatesRequestBudgetMs?: (body: Record<string, unknown>) => number;
@@ -318,26 +309,21 @@ export interface TelegramPollLoopDeps<TUpdate extends TelegramUpdate, TContext =
318
309
  getAcceptedThroughUpdateId?: () => number | undefined;
319
310
  getJournalEntryCount: () => number;
320
311
  signalUpdateWorker: () => void;
312
+ onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
313
+ onSuccessfulResponse?: (updateCount: number) => void;
314
+ }
315
+ export interface TelegramPollLoopDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends TelegramPollLoopTransportPorts<TUpdate> {
316
+ ctx: TContext;
317
+ signal: AbortSignal;
318
+ config: TelegramPollingConfig;
321
319
  onErrorStatus: (message: string) => void;
322
320
  onStatusReset: () => void;
323
321
  sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
324
- onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
325
- onSuccessfulResponse?: (updateCount: number) => void;
326
322
  }
327
- export interface TelegramPollLoopRunnerDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends TelegramRuntimeEventRecorderPort {
323
+ export interface TelegramPollLoopRunnerDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends TelegramPollLoopTransportPorts<TUpdate> {
328
324
  getConfig: () => TelegramPollingConfig;
329
- deleteWebhook: (signal: AbortSignal) => Promise<unknown>;
330
- getUpdates: (body: Record<string, unknown>, signal: AbortSignal) => Promise<TUpdate[]>;
331
- getUpdatesRequestBudgetMs?: (body: Record<string, unknown>) => number;
332
- persistConfig: (config: TelegramPollingConfig) => Promise<void>;
333
- appendUpdateBatch: (updates: readonly TUpdate[], acceptedThroughUpdateId?: number) => MaybePromise<unknown>;
334
- getAcceptedThroughUpdateId?: () => number | undefined;
335
- getJournalEntryCount: () => number;
336
- signalUpdateWorker: () => void;
337
325
  updateStatus: (ctx: TContext, message?: string) => void;
338
326
  sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
339
- onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
340
- onSuccessfulResponse?: (updateCount: number) => void;
341
327
  }
342
328
  export declare function sleepTelegramPollingRetry(ms: number, signal?: AbortSignal): Promise<void>;
343
329
  export declare function createTelegramPollLoopRunner<TUpdate extends TelegramUpdate, TContext = unknown>(deps: TelegramPollLoopRunnerDeps<TUpdate, TContext>): (ctx: TContext, signal: AbortSignal) => Promise<void>;