@llblab/pi-kit 0.29.2 → 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 (208) hide show
  1. package/BACKLOG.md +2 -1
  2. package/CHANGELOG.md +5 -0
  3. package/README.md +2 -2
  4. package/node_modules/@llblab/pi-telegram/AGENTS.md +9 -7
  5. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -18
  6. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +11 -0
  7. package/node_modules/@llblab/pi-telegram/README.md +7 -11
  8. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  9. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  10. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  11. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  12. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  13. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  14. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  15. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  16. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +163 -20
  17. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  18. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +182 -79
  19. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  20. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  21. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  22. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  23. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  24. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  25. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  26. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  28. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  29. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  30. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  31. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  32. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  33. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  34. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  35. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  36. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +639 -171
  37. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  38. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  39. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  40. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  42. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +33 -3
  43. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2063 -782
  44. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  45. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  46. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +29 -3
  47. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +289 -108
  48. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  49. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  50. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  51. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  52. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  53. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  54. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  55. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  56. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  57. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  58. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  59. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  60. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  61. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  62. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  63. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  65. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  66. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  67. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  68. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  69. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  70. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  71. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  72. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +61 -51
  73. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  74. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  75. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  76. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  77. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +21 -37
  79. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +59 -50
  80. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  81. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  82. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  83. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  84. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +14 -16
  85. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +249 -126
  86. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  87. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  88. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  89. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  90. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +120 -7
  91. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4667 -1152
  92. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  93. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  94. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  95. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  96. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  97. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +1 -3
  98. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +67 -43
  99. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  100. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  101. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -13
  102. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +148 -53
  103. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  104. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  105. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  106. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  107. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  108. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  109. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  110. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  111. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  112. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +82 -20
  113. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2247 -855
  114. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  115. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  116. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +169 -80
  117. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2418 -641
  118. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  119. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  120. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  121. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  122. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  123. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  124. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  125. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  126. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  127. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  128. package/node_modules/@llblab/pi-telegram/docs/architecture.md +31 -29
  129. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  130. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  131. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  132. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  133. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  134. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  135. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  136. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  137. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +389 -82
  138. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +263 -102
  139. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  140. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  141. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  142. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  143. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  144. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  145. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  146. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  147. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  148. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1206 -433
  149. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  150. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  151. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  152. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5311 -1763
  153. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  154. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  155. package/node_modules/@llblab/pi-telegram/lib/locks.ts +677 -203
  156. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  157. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  158. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  159. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  160. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  161. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  162. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  163. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  164. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  165. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  166. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  167. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  168. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  169. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +125 -125
  170. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  171. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  172. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  173. package/node_modules/@llblab/pi-telegram/lib/polling.ts +144 -147
  174. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  175. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  176. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  177. package/node_modules/@llblab/pi-telegram/lib/queue.ts +511 -300
  178. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  179. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  180. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  181. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8509 -2186
  182. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  183. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  184. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  185. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  186. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  187. package/node_modules/@llblab/pi-telegram/lib/sync.ts +448 -361
  188. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  189. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +302 -127
  190. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  191. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  192. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  193. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  194. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  195. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5318 -1845
  196. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  197. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  198. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5184 -1612
  199. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  200. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  201. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  202. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  203. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  204. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  205. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  206. package/package.json +2 -2
  207. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  208. package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
@@ -3,13 +3,13 @@
3
3
  * Zones: Telegram bot reality mirror, demand-driven reconciliation, status diagnostics
4
4
  * Owns pure contracts for deciding when local Telegram mirror state should be refreshed without querying Telegram on every action
5
5
  */
6
- import { getTelegramApiErrorRequestTarget, isTelegramStaleTargetHttpError } from "./telegram-api.js";
7
6
  import { getTelegramTargetKey } from "./target.js";
7
+ import { getTelegramApiErrorRequestTarget, isTelegramStaleTargetHttpError, } from "./telegram-api.js";
8
8
  import * as ThreadReconciler from "./thread-reconciler.js";
9
- import { TelegramWorkspaceSlotUnavailableError } from "./workspace-slots.js";
10
- import { normalizeTelegramWorkspacePath } from "./workspace-identity.js";
9
+ import { commitTelegramWorkspaceProvisionBinding, createTelegramCleanupTargetProtection, getTelegramTargetFromApiBody, getTelegramThreadOwnerKey, isSameTelegramProcessInstance, isTelegramTopicTargetStaleError, provisionOwnBusTopic, } from "./threads.js";
11
10
  import { createTelegramWorkspaceAdmissionOperationId, runWithTelegramWorkspaceAdmissionsAsync, } from "./workspace-admission.js";
12
- import { createTelegramCleanupTargetProtection, commitTelegramWorkspaceProvisionBinding, getTelegramTargetFromApiBody, getTelegramThreadOwnerKey, isSameTelegramProcessInstance, isTelegramTopicTargetStaleError, provisionOwnBusTopic, } from "./threads.js";
11
+ import { normalizeTelegramWorkspacePath } from "./workspace-identity.js";
12
+ import { TelegramWorkspaceSlotUnavailableError } from "./workspace-slots.js";
13
13
  function markTelegramConfigSyncChange(state, action, options) {
14
14
  const nowMs = options?.nowMs ?? Date.now();
15
15
  let nextState = markTelegramSyncSliceFresh(state, "pairing", {
@@ -32,10 +32,13 @@ export function createTelegramPreservedLeaderQuitHandler(deps) {
32
32
  const profile = deps.getProfileName();
33
33
  if (epoch === undefined || !isSessionCurrent())
34
34
  return undefined;
35
- const isCurrent = () => isSessionCurrent() && deps.getCurrentLeaderEpoch() === epoch &&
36
- deps.getProfileName() === profile && deps.isPollingSuspended();
35
+ const isCurrent = () => isSessionCurrent() &&
36
+ deps.getCurrentLeaderEpoch() === epoch &&
37
+ deps.getProfileName() === profile &&
38
+ deps.isPollingSuspended();
37
39
  return async () => {
38
- if (!isCurrent() || await deps.resolveAutomaticThreadCleanupEnabled() !== false)
40
+ if (!isCurrent() ||
41
+ (await deps.resolveAutomaticThreadCleanupEnabled()) !== false)
39
42
  return;
40
43
  if (!isCurrent())
41
44
  return;
@@ -49,13 +52,20 @@ export function createTelegramPreservedLeaderQuitHandler(deps) {
49
52
  await deps.topicTargetStore.load();
50
53
  if (!isCurrent())
51
54
  return;
52
- const records = deps.topicTargetStore.list().filter((record) => record.instanceId === deps.instanceId && (record.status === "active" || record.status === "starting"));
55
+ const records = deps.topicTargetStore
56
+ .list()
57
+ .filter((record) => record.instanceId === deps.instanceId &&
58
+ (record.status === "active" || record.status === "starting"));
53
59
  if (records.length !== 1)
54
60
  return;
55
61
  const record = records[0];
56
- if (deps.topicTargetStore.listPendingCleanups().some((intent) => intent.target.chatId === record.target.chatId && intent.target.threadId === record.target.threadId))
62
+ if (deps.topicTargetStore
63
+ .listPendingCleanups()
64
+ .some((intent) => intent.target.chatId === record.target.chatId &&
65
+ intent.target.threadId === record.target.threadId))
57
66
  return;
58
- if (!await deps.topicTargetStore.detachTargetOwner(record, isCurrent) && isCurrent()) {
67
+ if (!(await deps.topicTargetStore.detachTargetOwner(record, isCurrent)) &&
68
+ isCurrent()) {
59
69
  throw new Error("Telegram preserved leader detachment was not committed.");
60
70
  }
61
71
  });
@@ -93,16 +103,22 @@ export function createTelegramThreadDisconnectAssembly(deps) {
93
103
  catch (error) {
94
104
  // Never retry a stop whose teardown/release outcome may already have committed.
95
105
  if (stopAttempted) {
96
- deps.recordRuntimeEvent("connection", error, { phase: "disconnect-stop" });
106
+ deps.recordRuntimeEvent("connection", error, {
107
+ phase: "disconnect-stop",
108
+ });
97
109
  throw error;
98
110
  }
99
- deps.recordRuntimeEvent("connection", error, { phase: "disconnect-cleanup" });
111
+ deps.recordRuntimeEvent("connection", error, {
112
+ phase: "disconnect-cleanup",
113
+ });
100
114
  let stopped;
101
115
  try {
102
116
  stopped = await stop();
103
117
  }
104
118
  catch (stopError) {
105
- deps.recordRuntimeEvent("connection", stopError, { phase: "disconnect-stop" });
119
+ deps.recordRuntimeEvent("connection", stopError, {
120
+ phase: "disconnect-stop",
121
+ });
106
122
  throw stopError;
107
123
  }
108
124
  const separator = /[.!?]$/u.test(stopped) ? " " : ". ";
@@ -153,8 +169,11 @@ export function createTelegramManualThreadDisconnectHandler(deps) {
153
169
  target,
154
170
  requestedAtMs: (deps.getNowMs ?? Date.now)(),
155
171
  };
156
- const departingRecord = deps.topicTargetStore.list().find((record) => record.instanceId === currentRecord.instanceId &&
157
- record.target.chatId === target.chatId && record.target.threadId === target.threadId);
172
+ const departingRecord = deps.topicTargetStore
173
+ .list()
174
+ .find((record) => record.instanceId === currentRecord.instanceId &&
175
+ record.target.chatId === target.chatId &&
176
+ record.target.threadId === target.threadId);
158
177
  const isCleanupTargetProtected = createTelegramCleanupTargetProtection(deps.topicTargetStore, departingRecord);
159
178
  deps.topicTargetStore.upsertPendingCleanup(intent);
160
179
  await deps.topicTargetStore.persist();
@@ -312,23 +331,32 @@ export function captureTelegramStaleTargetRequestRecovery(body, deps) {
312
331
  if (!target || epoch === undefined)
313
332
  return undefined;
314
333
  const key = getTelegramTargetKey(target);
315
- const record = deps.topicTargetStore.list().find((candidate) => getTelegramTargetKey(candidate.target) === key);
334
+ const record = deps.topicTargetStore
335
+ .list()
336
+ .find((candidate) => getTelegramTargetKey(candidate.target) === key);
316
337
  if (!record)
317
338
  return undefined;
318
339
  const generation = deps.getSessionGeneration();
319
340
  const profile = deps.getProfileName();
320
341
  const isAuthorityCurrent = () => deps.getCurrentLeaderEpoch() === epoch &&
321
- deps.getSessionGeneration() === generation && deps.getProfileName() === profile;
342
+ deps.getSessionGeneration() === generation &&
343
+ deps.getProfileName() === profile;
322
344
  const isCurrent = () => {
323
- const current = deps.topicTargetStore.list().find((candidate) => getTelegramTargetKey(candidate.target) === key);
324
- return isAuthorityCurrent() &&
325
- current?.instanceId === record.instanceId && current?.profileKey === record.profileKey &&
326
- current?.updatedAtMs === record.updatedAtMs && current?.createdAtMs === record.createdAtMs;
345
+ const current = deps.topicTargetStore
346
+ .list()
347
+ .find((candidate) => getTelegramTargetKey(candidate.target) === key);
348
+ return (isAuthorityCurrent() &&
349
+ current?.instanceId === record.instanceId &&
350
+ current?.profileKey === record.profileKey &&
351
+ current?.updatedAtMs === record.updatedAtMs &&
352
+ current?.createdAtMs === record.createdAtMs);
327
353
  };
328
354
  return async (error) => {
329
355
  const requestTarget = getTelegramApiErrorRequestTarget(error);
330
- if (!isTelegramStaleTargetHttpError(error) || !requestTarget ||
331
- getTelegramTargetKey(requestTarget) !== key || !isCurrent())
356
+ if (!isTelegramStaleTargetHttpError(error) ||
357
+ !requestTarget ||
358
+ getTelegramTargetKey(requestTarget) !== key ||
359
+ !isCurrent())
332
360
  return;
333
361
  if (await recoverStaleTelegramTopicApiError({ chat_id: target.chatId, message_thread_id: target.threadId }, error, { ...deps, isCurrent, isAuthorityCurrent })) {
334
362
  deps.onRecovered();
@@ -354,7 +382,7 @@ export async function recoverStaleTelegramTopicApiError(apiBody, error, deps) {
354
382
  const recover = async () => {
355
383
  if (deps.isCurrent) {
356
384
  if (!deps.topicTargetStore.invalidateTarget ||
357
- !await deps.topicTargetStore.invalidateTarget(target, deps.isCurrent, String(error)))
385
+ !(await deps.topicTargetStore.invalidateTarget(target, deps.isCurrent, String(error))))
358
386
  return false;
359
387
  if (deps.isAuthorityCurrent?.() === false)
360
388
  return false;
@@ -436,7 +464,12 @@ export async function ensureTelegramLeaderThreadBinding(deps) {
436
464
  const legacyLeaderRecord = deps.topicTargetStore.getByProfileKey(leaderProfileKey);
437
465
  let capacityUnavailable = false;
438
466
  const workspaceIdentity = normalizedLeaderCwd
439
- ? deps.topicTargetStore.claimWorkspaceIdentity(normalizedLeaderCwd, deps.instanceId, legacyLeaderRecord?.instanceId, { sessionId: deps.sessionId, onCapacityUnavailable() { capacityUnavailable = true; } })
467
+ ? deps.topicTargetStore.claimWorkspaceIdentity(normalizedLeaderCwd, deps.instanceId, legacyLeaderRecord?.instanceId, {
468
+ sessionId: deps.sessionId,
469
+ onCapacityUnavailable() {
470
+ capacityUnavailable = true;
471
+ },
472
+ })
440
473
  : undefined;
441
474
  if (deps.cwd && !workspaceIdentity) {
442
475
  if (capacityUnavailable)
@@ -471,7 +504,9 @@ export async function ensureTelegramLeaderThreadBinding(deps) {
471
504
  target: { ...committed.target },
472
505
  slot: committed.slot ?? result.slot,
473
506
  ...(committed.threadName ? { threadName: committed.threadName } : {}),
474
- ...(committed.displayTitle ? { displayTitle: committed.displayTitle } : {}),
507
+ ...(committed.displayTitle
508
+ ? { displayTitle: committed.displayTitle }
509
+ : {}),
475
510
  };
476
511
  };
477
512
  try {
@@ -512,7 +547,9 @@ export async function ensureTelegramLeaderThreadBinding(deps) {
512
547
  ...(legacyLeaderRecord.threadName
513
548
  ? { threadName: legacyLeaderRecord.threadName }
514
549
  : {}),
515
- ...(legacyLeaderRecord.slot ? { slot: legacyLeaderRecord.slot } : {}),
550
+ ...(legacyLeaderRecord.slot
551
+ ? { slot: legacyLeaderRecord.slot }
552
+ : {}),
516
553
  updatedAtMs: legacyLeaderRecord.updatedAtMs,
517
554
  }
518
555
  : undefined;
@@ -600,7 +637,9 @@ export async function ensureTelegramLeaderThreadBinding(deps) {
600
637
  });
601
638
  // Legacy callers without session identity may reuse the active instance target.
602
639
  // Session-aware callers must resolve their exact Workspace binding instead.
603
- if (deps.sessionId === undefined && !deps.forceFreshUnnamed && priorTargets.length > 0) {
640
+ if (deps.sessionId === undefined &&
641
+ !deps.forceFreshUnnamed &&
642
+ priorTargets.length > 0) {
604
643
  const record = priorTargets[0];
605
644
  deps.recordEvent("telegram", "Leader thread preserved after session lifecycle change", {
606
645
  phase: "leader-thread-reused",
@@ -747,21 +786,6 @@ export function createTelegramProvisioningActivityRuntime() {
747
786
  },
748
787
  };
749
788
  }
750
- const RECONCILE_TRIGGERS = new Set([
751
- "startup",
752
- "reload",
753
- "topic-lifecycle",
754
- "stale-api-error",
755
- "setup-change",
756
- "pairing-change",
757
- "follower-register",
758
- "follower-prune",
759
- "status-request",
760
- "leader-health-tick",
761
- ]);
762
- export function shouldReconcileTelegramSync(trigger) {
763
- return RECONCILE_TRIGGERS.has(trigger);
764
- }
765
789
  export function markTelegramSyncSliceSuspect(state, slice, input) {
766
790
  return {
767
791
  ...state,
@@ -18,3 +18,7 @@ export declare function areTelegramTargetsEqual(left: TelegramTarget, right: Tel
18
18
  export declare function getTelegramTargetThreadParams(target: TelegramTarget): {
19
19
  message_thread_id?: number;
20
20
  };
21
+ /** Pure wire parser for `{ chatId, threadId? }`; unknown fields are dropped and malformed shapes refuse. */
22
+ export declare function parseTelegramTarget(value: unknown): TelegramTarget | undefined;
23
+ /** Pure Bot API integer id parser accepting integer numbers or non-blank integer strings. */
24
+ export declare function parseTelegramIntegerId(value: unknown): number | undefined;
@@ -25,3 +25,21 @@ export function getTelegramTargetThreadParams(target) {
25
25
  ? { message_thread_id: target.threadId }
26
26
  : {};
27
27
  }
28
+ /** Pure wire parser for `{ chatId, threadId? }`; unknown fields are dropped and malformed shapes refuse. */
29
+ export function parseTelegramTarget(value) {
30
+ if (typeof value !== "object" || value === null || Array.isArray(value))
31
+ return undefined;
32
+ const { chatId, threadId } = value;
33
+ if (typeof chatId !== "number")
34
+ return undefined;
35
+ return typeof threadId === "number" ? { chatId, threadId } : { chatId };
36
+ }
37
+ /** Pure Bot API integer id parser accepting integer numbers or non-blank integer strings. */
38
+ export function parseTelegramIntegerId(value) {
39
+ if (typeof value === "number" && Number.isInteger(value))
40
+ return value;
41
+ if (typeof value !== "string" || value.trim() === "")
42
+ return undefined;
43
+ const parsed = Number(value);
44
+ return Number.isInteger(parsed) ? parsed : undefined;
45
+ }
@@ -7,7 +7,8 @@
7
7
  */
8
8
  import type { TelegramAttachmentSource } from "./media.ts";
9
9
  export declare const TELEGRAM_FILE_MAX_BYTES: number;
10
- export declare function getTelegramInboundFileByteLimitFromEnv(env: NodeJS.ProcessEnv, names: string[], defaultValue?: number): number;
10
+ /** First positive safe-integer byte limit among ordered env names, else the caller's default. */
11
+ export declare function getTelegramByteLimitFromEnv(env: NodeJS.ProcessEnv, names: readonly string[], defaultValue: number): number;
11
12
  export type TelegramNetworkFamilyPolicy = "auto" | "ipv4" | "ipv6" | "ipv4-fallback";
12
13
  type TelegramNetworkFamily = 4 | 6;
13
14
  export interface TelegramUser {
@@ -288,6 +289,8 @@ export interface TelegramApiRetryWait {
288
289
  retryAfterSeconds?: number;
289
290
  }
290
291
  export interface TelegramApiCallOptions {
292
+ /** Local per-call effect authority; independent of source completion and transport ownership. */
293
+ assertAuthority?: () => void;
291
294
  signal?: AbortSignal;
292
295
  maxAttempts?: number;
293
296
  retryRateLimit?: boolean;
@@ -297,6 +300,12 @@ export interface TelegramApiCallOptions {
297
300
  /** Observability hook fired before a 429 retry wait, never for 5xx waits. */
298
301
  onRetryWait?: (wait: TelegramApiRetryWait) => void;
299
302
  }
303
+ export declare class TelegramApiAuthorityError extends Error {
304
+ readonly requestIssued: boolean;
305
+ constructor(requestIssued: boolean);
306
+ }
307
+ /** Recheck caller authority, classifying refusal by whether a Bot API request was already issued. */
308
+ export declare function assertTelegramApiCallAuthority(assertAuthority: (() => void) | undefined, requestIssued: boolean): void;
300
309
  export interface TelegramFileDownloadOptions {
301
310
  signal?: AbortSignal;
302
311
  maxFileSizeBytes?: number;
@@ -338,14 +347,14 @@ export interface TelegramEditGuestInlineMessageContent {
338
347
  richMessage?: TelegramInputRichMessage;
339
348
  parseMode?: "HTML";
340
349
  }
341
- export interface TelegramAnswerCallbackQueryOptions {
350
+ export interface TelegramAnswerCallbackQueryOptions extends Pick<TelegramApiCallOptions, "assertAuthority"> {
342
351
  recordRuntimeEvent?: (kind: "api", error: unknown, details?: Record<string, unknown>) => void;
343
352
  }
344
353
  export interface TelegramApiClient {
345
354
  call: <TResponse>(method: string, body: Record<string, unknown>, options?: TelegramApiCallOptions) => Promise<TResponse>;
346
355
  callMultipart: <TResponse>(method: string, fields: Record<string, string>, fileField: string, filePath: string, fileName: string, options?: TelegramApiCallOptions) => Promise<TResponse>;
347
356
  downloadFile: (fileId: string, suggestedName: string, tempDir: string, options?: TelegramFileDownloadOptions) => Promise<string>;
348
- answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
357
+ answerCallbackQuery: (callbackQueryId: string, text?: string, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
349
358
  answerGuestQuery?: (guestQueryId: string, text?: string, options?: TelegramAnswerGuestQueryOptions) => Promise<void>;
350
359
  }
351
360
  export interface TelegramApiTargetActivityRuntime {
@@ -422,16 +431,14 @@ export interface TelegramBridgeApiRuntimeDeps {
422
431
  chatActionMinIntervalMs?: number;
423
432
  chatActionMaxGates?: number;
424
433
  }
425
- export interface TelegramBridgeApiRuntime {
426
- call: <TResponse>(method: string, body: Record<string, unknown>, options?: TelegramApiCallOptions) => Promise<TResponse>;
427
- callMultipart: <TResponse>(method: string, fields: Record<string, string>, fileField: string, filePath: string, fileName: string, options?: TelegramApiCallOptions) => Promise<TResponse>;
434
+ export interface TelegramBridgeApiRuntime extends Pick<TelegramApiClient, "call" | "callMultipart"> {
428
435
  downloadFile: (fileId: string, suggestedName: string, source?: TelegramAttachmentSource) => Promise<string>;
429
436
  deleteWebhook: (signal?: AbortSignal) => Promise<boolean>;
430
437
  getUpdates: (body: Record<string, unknown>, signal?: AbortSignal) => Promise<TelegramUpdate[]>;
431
438
  setMyCommands: (commands: readonly {
432
439
  command: string;
433
440
  description: string;
434
- }[]) => Promise<boolean>;
441
+ }[], options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<boolean>;
435
442
  sendChatAction: (chatId: number, action: string, options?: {
436
443
  message_thread_id?: number;
437
444
  }) => Promise<boolean>;
@@ -446,12 +453,12 @@ export interface TelegramBridgeApiRuntime {
446
453
  entities?: unknown[];
447
454
  message_thread_id?: number;
448
455
  }) => Promise<boolean>;
449
- sendMessage: (body: TelegramSendMessageBody) => Promise<TelegramSentMessage>;
450
- sendRichMessage: (body: TelegramSendRichMessageBody) => Promise<TelegramSentMessage>;
456
+ sendMessage: (body: TelegramSendMessageBody, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<TelegramSentMessage>;
457
+ sendRichMessage: (body: TelegramSendRichMessageBody, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<TelegramSentMessage>;
451
458
  sendRichMessageDraft: (body: TelegramSendRichMessageDraftBody) => Promise<boolean>;
452
- editMessageText: (body: TelegramEditMessageTextBody) => Promise<"edited" | "unchanged">;
459
+ editMessageText: (body: TelegramEditMessageTextBody, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<"edited" | "unchanged">;
453
460
  editMessageReplyMarkup: (chatId: number, messageId: number, replyMarkup: unknown) => Promise<void>;
454
- answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
461
+ answerCallbackQuery: (callbackQueryId: string, text?: string, options?: Pick<TelegramApiCallOptions, "assertAuthority">) => Promise<void>;
455
462
  answerGuestQuery: (guestQueryId: string, text?: string, options?: TelegramAnswerGuestQueryOptions) => Promise<void>;
456
463
  /**
457
464
  * Temporary Guest Mode ACK experiment: answers the guest query and returns
@@ -514,8 +521,6 @@ export declare function fetchTelegramBotIdentity(botToken: string, fetchImpl?: t
514
521
  */
515
522
  export declare function callTelegramMultipart<TResponse>(botToken: string | undefined, method: string, fields: Record<string, string>, fileField: string, filePath: string, fileName: string, options?: TelegramApiCallOptions): Promise<TResponse>;
516
523
  export declare function downloadTelegramFile(botToken: string | undefined, fileId: string, suggestedName: string, tempDir: string, options?: TelegramFileDownloadOptions): Promise<string>;
517
- /** Tooltip-like callback answers omit a terminal sentence period; ellipses and other punctuation stay literal. */
518
- export declare function formatTelegramCallbackAnswerText(text: string | undefined): string | undefined;
519
524
  export declare function answerTelegramCallbackQuery(botToken: string | undefined, callbackQueryId: string, text?: string, options?: TelegramAnswerCallbackQueryOptions): Promise<void>;
520
525
  export declare function createTelegramChatActionSender<TAction extends string>(sendChatAction: (chatId: number, action: TAction, options?: {
521
526
  message_thread_id?: number;