@llblab/pi-telegram 0.52.2 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/AGENTS.md +9 -7
  2. package/BACKLOG.md +1 -18
  3. package/CHANGELOG.md +11 -0
  4. package/README.md +7 -11
  5. package/api/commands.ts +3 -0
  6. package/dist/api/commands.d.ts +1 -1
  7. package/dist/lib/activity-verbosity.d.ts +1 -5
  8. package/dist/lib/activity-verbosity.js +15 -42
  9. package/dist/lib/activity.d.ts +9 -0
  10. package/dist/lib/activity.js +39 -10
  11. package/dist/lib/agent-messages.js +7 -3
  12. package/dist/lib/bindings.d.ts +43 -5
  13. package/dist/lib/bindings.js +163 -20
  14. package/dist/lib/bus-api.d.ts +12 -0
  15. package/dist/lib/bus-api.js +182 -79
  16. package/dist/lib/bus-follower.d.ts +111 -31
  17. package/dist/lib/bus-follower.js +1324 -359
  18. package/dist/lib/bus-leader.d.ts +41 -91
  19. package/dist/lib/bus-leader.js +969 -637
  20. package/dist/lib/bus-transport.js +16 -9
  21. package/dist/lib/bus.d.ts +215 -18
  22. package/dist/lib/bus.js +1048 -327
  23. package/dist/lib/channel-posts.d.ts +5 -0
  24. package/dist/lib/channel-posts.js +434 -261
  25. package/dist/lib/command-templates.d.ts +26 -5
  26. package/dist/lib/command-templates.js +92 -176
  27. package/dist/lib/commands.d.ts +168 -82
  28. package/dist/lib/commands.js +1409 -291
  29. package/dist/lib/config.d.ts +1 -8
  30. package/dist/lib/config.js +34 -76
  31. package/dist/lib/delivery.d.ts +2 -2
  32. package/dist/lib/delivery.js +16 -16
  33. package/dist/lib/extension.js +639 -171
  34. package/dist/lib/generative-app-worker.mjs +19 -7
  35. package/dist/lib/generative-apps.d.ts +0 -5
  36. package/dist/lib/generative-apps.js +61 -49
  37. package/dist/lib/inbound.d.ts +5 -0
  38. package/dist/lib/inbound.js +35 -59
  39. package/dist/lib/journal.d.ts +33 -3
  40. package/dist/lib/journal.js +2063 -782
  41. package/dist/lib/lifecycle.d.ts +2 -6
  42. package/dist/lib/lifecycle.js +75 -58
  43. package/dist/lib/locks.d.ts +29 -3
  44. package/dist/lib/locks.js +289 -108
  45. package/dist/lib/logging.js +50 -14
  46. package/dist/lib/media.d.ts +22 -1
  47. package/dist/lib/media.js +46 -28
  48. package/dist/lib/menu-model.d.ts +19 -5
  49. package/dist/lib/menu-model.js +53 -11
  50. package/dist/lib/menu-queue.d.ts +5 -4
  51. package/dist/lib/menu-queue.js +45 -63
  52. package/dist/lib/menu-settings.d.ts +5 -4
  53. package/dist/lib/menu-settings.js +114 -78
  54. package/dist/lib/menu-status.d.ts +1 -1
  55. package/dist/lib/menu-status.js +8 -25
  56. package/dist/lib/menu-thinking.d.ts +6 -1
  57. package/dist/lib/menu-thinking.js +14 -21
  58. package/dist/lib/menu.d.ts +8 -61
  59. package/dist/lib/menu.js +92 -164
  60. package/dist/lib/model.d.ts +1 -1
  61. package/dist/lib/model.js +2 -16
  62. package/dist/lib/outbound-attachments.d.ts +6 -27
  63. package/dist/lib/outbound-attachments.js +45 -38
  64. package/dist/lib/outbound-buttons.js +22 -16
  65. package/dist/lib/outbound-markup.d.ts +0 -2
  66. package/dist/lib/outbound-markup.js +51 -64
  67. package/dist/lib/outbound-voice.js +6 -3
  68. package/dist/lib/outbound.d.ts +6 -6
  69. package/dist/lib/outbound.js +61 -51
  70. package/dist/lib/ownership.js +6 -2
  71. package/dist/lib/paths.d.ts +5 -0
  72. package/dist/lib/paths.js +37 -21
  73. package/dist/lib/pi.d.ts +2 -0
  74. package/dist/lib/pi.js +8 -1
  75. package/dist/lib/polling.d.ts +21 -37
  76. package/dist/lib/polling.js +59 -50
  77. package/dist/lib/preview.js +39 -19
  78. package/dist/lib/process-identity.d.ts +24 -0
  79. package/dist/lib/process-identity.js +133 -0
  80. package/dist/lib/prompts.js +2 -7
  81. package/dist/lib/queue.d.ts +14 -16
  82. package/dist/lib/queue.js +249 -126
  83. package/dist/lib/recovery.d.ts +2 -83
  84. package/dist/lib/recovery.js +20 -274
  85. package/dist/lib/replies.d.ts +16 -24
  86. package/dist/lib/replies.js +99 -116
  87. package/dist/lib/routing.d.ts +120 -7
  88. package/dist/lib/routing.js +4667 -1152
  89. package/dist/lib/runtime.d.ts +1 -11
  90. package/dist/lib/sections.d.ts +4 -29
  91. package/dist/lib/sections.js +7 -20
  92. package/dist/lib/skills.js +4 -3
  93. package/dist/lib/status.js +86 -57
  94. package/dist/lib/sync.d.ts +1 -3
  95. package/dist/lib/sync.js +67 -43
  96. package/dist/lib/target.d.ts +4 -0
  97. package/dist/lib/target.js +18 -0
  98. package/dist/lib/telegram-api.d.ts +18 -13
  99. package/dist/lib/telegram-api.js +148 -53
  100. package/dist/lib/text-groups.js +4 -39
  101. package/dist/lib/thread-cleanup-manager.d.ts +6 -27
  102. package/dist/lib/thread-cleanup-manager.js +369 -186
  103. package/dist/lib/thread-display.d.ts +1 -2
  104. package/dist/lib/thread-display.js +58 -38
  105. package/dist/lib/thread-naming.d.ts +28 -8
  106. package/dist/lib/thread-naming.js +175 -66
  107. package/dist/lib/thread-reconciler.d.ts +42 -16
  108. package/dist/lib/thread-reconciler.js +90 -50
  109. package/dist/lib/threads.d.ts +82 -20
  110. package/dist/lib/threads.js +2247 -855
  111. package/dist/lib/time-injection.js +1 -2
  112. package/dist/lib/turns.js +1 -19
  113. package/dist/lib/updates.d.ts +169 -80
  114. package/dist/lib/updates.js +2418 -641
  115. package/dist/lib/voice.d.ts +7 -22
  116. package/dist/lib/voice.js +7 -22
  117. package/dist/lib/wire.js +1 -1
  118. package/dist/lib/workspace-admission.d.ts +3 -1
  119. package/dist/lib/workspace-admission.js +425 -211
  120. package/dist/lib/workspace-identity.js +8 -13
  121. package/dist/lib/workspace-retirement.d.ts +1 -1
  122. package/dist/lib/workspace-retirement.js +328 -140
  123. package/dist/lib/workspace-slots.js +8 -4
  124. package/dist/package.json +1 -1
  125. package/docs/architecture.md +31 -29
  126. package/docs/inbound.md +1 -1
  127. package/docs/multi-instance-bus.md +15 -3
  128. package/docs/outbound.md +1 -1
  129. package/docs/public-api.md +29 -1
  130. package/docs/ui-style.md +25 -8
  131. package/lib/activity-verbosity.ts +59 -82
  132. package/lib/activity.ts +146 -102
  133. package/lib/agent-messages.ts +12 -5
  134. package/lib/bindings.ts +389 -82
  135. package/lib/bus-api.ts +263 -102
  136. package/lib/bus-follower.ts +2440 -601
  137. package/lib/bus-leader.ts +2209 -1290
  138. package/lib/bus-transport.ts +46 -12
  139. package/lib/bus.ts +1673 -444
  140. package/lib/channel-posts.ts +848 -327
  141. package/lib/command-templates.ts +152 -265
  142. package/lib/commands.ts +2398 -555
  143. package/lib/config.ts +177 -170
  144. package/lib/delivery.ts +41 -29
  145. package/lib/extension.ts +1206 -433
  146. package/lib/generative-app-worker.mjs +19 -7
  147. package/lib/generative-apps.ts +324 -211
  148. package/lib/inbound.ts +95 -97
  149. package/lib/journal.ts +5311 -1763
  150. package/lib/keyboard.ts +1 -3
  151. package/lib/lifecycle.ts +128 -78
  152. package/lib/locks.ts +677 -203
  153. package/lib/logging.ts +147 -58
  154. package/lib/media.ts +82 -43
  155. package/lib/menu-model.ts +85 -18
  156. package/lib/menu-queue.ts +88 -129
  157. package/lib/menu-settings.ts +260 -155
  158. package/lib/menu-status.ts +20 -60
  159. package/lib/menu-thinking.ts +41 -56
  160. package/lib/menu.ts +112 -285
  161. package/lib/model.ts +8 -22
  162. package/lib/outbound-attachments.ts +156 -125
  163. package/lib/outbound-buttons.ts +76 -49
  164. package/lib/outbound-markup.ts +96 -94
  165. package/lib/outbound-voice.ts +33 -27
  166. package/lib/outbound.ts +125 -125
  167. package/lib/ownership.ts +19 -10
  168. package/lib/paths.ts +148 -49
  169. package/lib/pi.ts +35 -15
  170. package/lib/polling.ts +144 -147
  171. package/lib/preview.ts +93 -40
  172. package/lib/process-identity.ts +164 -0
  173. package/lib/prompts.ts +4 -11
  174. package/lib/queue.ts +511 -300
  175. package/lib/recovery.ts +50 -454
  176. package/lib/rendering.ts +1 -3
  177. package/lib/replies.ts +248 -209
  178. package/lib/routing.ts +8509 -2186
  179. package/lib/runtime.ts +1 -12
  180. package/lib/sections.ts +24 -89
  181. package/lib/setup.ts +1 -3
  182. package/lib/skills.ts +9 -4
  183. package/lib/status.ts +215 -110
  184. package/lib/sync.ts +448 -361
  185. package/lib/target.ts +22 -3
  186. package/lib/telegram-api.ts +302 -127
  187. package/lib/text-groups.ts +5 -39
  188. package/lib/thread-cleanup-manager.ts +833 -331
  189. package/lib/thread-display.ts +213 -97
  190. package/lib/thread-naming.ts +232 -87
  191. package/lib/thread-reconciler.ts +155 -72
  192. package/lib/threads.ts +5318 -1845
  193. package/lib/time-injection.ts +1 -4
  194. package/lib/turns.ts +15 -28
  195. package/lib/updates.ts +5184 -1612
  196. package/lib/voice.ts +7 -35
  197. package/lib/wire.ts +1 -1
  198. package/lib/workspace-admission.ts +996 -416
  199. package/lib/workspace-identity.ts +14 -17
  200. package/lib/workspace-retirement.ts +878 -376
  201. package/lib/workspace-slots.ts +22 -13
  202. package/package.json +1 -1
  203. package/scripts/check-downgrade.mjs +0 -472
  204. package/scripts/measure-workspace.mjs +0 -108
@@ -11,7 +11,6 @@ import * as BusLeader from "./bus-leader.js";
11
11
  import * as BusTransport from "./bus-transport.js";
12
12
  import * as Bus from "./bus.js";
13
13
  import * as ChannelPosts from "./channel-posts.js";
14
- import * as ThreadCleanupManager from "./thread-cleanup-manager.js";
15
14
  import * as CommandTemplates from "./command-templates.js";
16
15
  import * as Commands from "./commands.js";
17
16
  import * as Config from "./config.js";
@@ -32,6 +31,7 @@ import * as Paths from "./paths.js";
32
31
  import * as Pi from "./pi.js";
33
32
  import * as Polling from "./polling.js";
34
33
  import * as Preview from "./preview.js";
34
+ import * as ProcessIdentity from "./process-identity.js";
35
35
  import * as PromptTemplates from "./prompt-templates.js";
36
36
  import * as Prompts from "./prompts.js";
37
37
  import * as Queue from "./queue.js";
@@ -43,12 +43,13 @@ import * as Sections from "./sections.js";
43
43
  import * as Skills from "./skills.js";
44
44
  import * as Status from "./status.js";
45
45
  import * as Sync from "./sync.js";
46
+ import * as Targets from "./target.js";
46
47
  import * as TelegramApi from "./telegram-api.js";
47
48
  import * as TextGroups from "./text-groups.js";
48
- import * as ThreadReconciler from "./thread-reconciler.js";
49
- import * as Targets from "./target.js";
49
+ import * as ThreadCleanupManager from "./thread-cleanup-manager.js";
50
50
  import * as ThreadDisplay from "./thread-display.js";
51
51
  import * as ThreadNaming from "./thread-naming.js";
52
+ import * as ThreadReconciler from "./thread-reconciler.js";
52
53
  import * as Threads from "./threads.js";
53
54
  import * as TimeInjection from "./time-injection.js";
54
55
  import * as Updates from "./updates.js";
@@ -64,6 +65,11 @@ const telegramBusProtocolIdentity = Bus.createTelegramCurrentBusProtocolIdentity
64
65
  Bus.TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT,
65
66
  Bus.TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT,
66
67
  Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE,
68
+ Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE,
69
+ Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY,
70
+ Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE,
71
+ Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
72
+ Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
67
73
  ]);
68
74
  // --- Extension Runtime ---
69
75
  export default function (pi) {
@@ -71,7 +77,9 @@ export default function (pi) {
71
77
  const piRuntime = Pi.createExtensionApiRuntimePorts(pi);
72
78
  const { getActiveTools, getCommands, getThinkingLevel, sendUserMessage, registerCommand, setActiveTools, setModel, setThinkingLevel, } = piRuntime;
73
79
  const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
74
- const runtimeDiagnostics = Logging.createTelegramRuntimeDiagnosticsRuntime({ sharedFile: true });
80
+ const runtimeDiagnostics = Logging.createTelegramRuntimeDiagnosticsRuntime({
81
+ sharedFile: true,
82
+ });
75
83
  const runtimeEvents = runtimeDiagnostics.events;
76
84
  const recordRuntimeEvent = runtimeDiagnostics.recordRuntimeEvent;
77
85
  const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
@@ -126,8 +134,12 @@ export default function (pi) {
126
134
  getBotId: getTelegramBotId,
127
135
  });
128
136
  const telegramLeaderJournalPath = Locks.createTelegramLeaderJournalPathResolver({
129
- getNamedJournalPath() { return lockRuntime.getJournalPath(); },
130
- getSessionId() { return Pi.getExtensionContextSessionId(telegramSessionContextStore.get()); },
137
+ getNamedJournalPath() {
138
+ return lockRuntime.getJournalPath();
139
+ },
140
+ getSessionId() {
141
+ return Pi.getExtensionContextSessionId(telegramSessionContextStore.get());
142
+ },
131
143
  getProfileName: configStore.getActiveProfileName,
132
144
  });
133
145
  const telegramJournalBindingRuntime = Journal.createTelegramUpdateJournalBindingRuntime({
@@ -175,7 +187,6 @@ export default function (pi) {
175
187
  const resolveTelegramFollowerJournalBinding = telegramJournalBindingRuntime.resolveFollower;
176
188
  const getTelegramQueueJournalBinding = telegramJournalBindingRuntime.getActiveRecoveryKey;
177
189
  const isTelegramBusRuntimeEnabled = telegramThreadCapabilityState.isBusRuntimeEnabled;
178
- Config.bindGlobalTelegramConfigRuntime(configStore);
179
190
  const configControls = Config.createTelegramConfigControls(configStore);
180
191
  const lockRuntime = Locks.createTelegramLockRuntime({
181
192
  key: Locks.createTelegramLockKeyResolver(configStore),
@@ -184,7 +195,9 @@ export default function (pi) {
184
195
  busSecret: telegramBusAuthSecret,
185
196
  staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
186
197
  legacyLocksPath: Paths.resolveLegacyTelegramOwnersPath(),
187
- createJournalPath() { return telegramLeaderJournalPath.createJournalPath(); },
198
+ createJournalPath() {
199
+ return telegramLeaderJournalPath.createJournalPath();
200
+ },
188
201
  });
189
202
  const telegramSessionContextStore = Lifecycle.createTelegramSessionContextStore({
190
203
  getIdentity(ctx) {
@@ -228,9 +241,9 @@ export default function (pi) {
228
241
  instanceId: telegramInstanceId,
229
242
  isRegisteredFor(target) {
230
243
  const registered = telegramBusFollowerRegistrationState.getTarget();
231
- return telegramBusFollowerRegistrationState.isRegistered() &&
244
+ return (telegramBusFollowerRegistrationState.isRegistered() &&
232
245
  registered?.chatId === target.chatId &&
233
- registered.threadId === target.threadId;
246
+ registered.threadId === target.threadId);
234
247
  },
235
248
  async requestSessionReplacement(operation, intent) {
236
249
  const request = telegramBusFollowerRegistration.requestSessionReplacement;
@@ -241,10 +254,14 @@ export default function (pi) {
241
254
  },
242
255
  async sendResult(target, html) {
243
256
  const delivery = await Delivery.sendTelegramView({ text: html, parseMode: "html", replyMarkup: { inline_keyboard: [] } }, { scope: { kind: "target", target } });
244
- return delivery.ok ? { ok: true } : { ok: false,
245
- retryable: delivery.reason === "runtime-unavailable" ||
246
- delivery.reason === "target-unavailable" ||
247
- delivery.reason === "transport-retryable" };
257
+ return delivery.ok
258
+ ? { ok: true }
259
+ : {
260
+ ok: false,
261
+ retryable: delivery.reason === "runtime-unavailable" ||
262
+ delivery.reason === "target-unavailable" ||
263
+ delivery.reason === "transport-retryable",
264
+ };
248
265
  },
249
266
  handoffTtlMs: Threads.TELEGRAM_LEADER_SESSION_HANDOFF_TTL_MS,
250
267
  recordRuntimeEvent,
@@ -302,13 +319,24 @@ export default function (pi) {
302
319
  });
303
320
  const telegramQueueStore = Queue.createTelegramTransportStampedQueueStore(rawTelegramQueueStore, telegramTransportStampRuntime.getStamp);
304
321
  const telegramApiTargetActivityRuntime = TelegramApi.createTelegramApiTargetActivityRuntime();
305
- const workspaceProtectionObserver = WorkspaceRetirement.createTelegramWorkspaceProtectionObserver({
306
- listFollowers: telegramBusFollowerRegistry.list,
322
+ // Retirement protection and dead-owner reclamation must inspect the same journal sources.
323
+ const workspaceRetirementJournalPorts = {
307
324
  getActiveTurnTarget: activeTurnRuntime.getTarget,
308
325
  getQueuedItems: telegramQueueStore.getQueuedItems,
309
326
  resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
310
327
  createFollowerJournalResolver: telegramJournalBindingRuntime.createLegacyRecipientResolver,
311
328
  createSessionJournalResolver: telegramJournalBindingRuntime.createRecipientResolver,
329
+ createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
330
+ discoverFollowerJournals() {
331
+ return Journal.discoverTelegramRecipientJournalPaths({
332
+ directory: Paths.resolveTelegramTempDir(),
333
+ profileName: configStore.getActiveProfileName(),
334
+ });
335
+ },
336
+ };
337
+ const workspaceProtectionObserver = WorkspaceRetirement.createTelegramWorkspaceProtectionObserver({
338
+ listFollowers: telegramBusFollowerRegistry.list,
339
+ ...workspaceRetirementJournalPorts,
312
340
  withJournalReference(binding, operation) {
313
341
  if (!binding.recoveryKey)
314
342
  throw new Error("Telegram workspace journal reference identity is unavailable.");
@@ -317,13 +345,6 @@ export default function (pi) {
317
345
  recoveryKey: binding.recoveryKey,
318
346
  }, operation);
319
347
  },
320
- discoverFollowerJournals() {
321
- return Journal.discoverTelegramRecipientJournalPaths({
322
- directory: Paths.resolveTelegramTempDir(),
323
- profileName: configStore.getActiveProfileName(),
324
- });
325
- },
326
- createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
327
348
  inspectJournalNamespace() {
328
349
  const botToken = configStore.getBotToken();
329
350
  if (!botToken)
@@ -332,27 +353,40 @@ export default function (pi) {
332
353
  directory: Paths.resolveTelegramTempDir(),
333
354
  profile: configStore.getActiveProfileName() ?? "default",
334
355
  pollingPath: telegramLeaderJournalPath.resolve(configStore.getActiveProfileName()),
335
- botIdentity: Journal.createTelegramUpdateJournalBotIdentity({ botToken, botId: getTelegramBotId() }),
336
- limits: { maxDirectoryEntries: 10_000, maxFiles: 4096, maxBytes: 64 * 1024 * 1024,
337
- maxEntries: 10_000, maxWork: 100_000 },
356
+ botIdentity: Journal.createTelegramUpdateJournalBotIdentity({
357
+ botToken,
358
+ botId: getTelegramBotId(),
359
+ }),
360
+ limits: {
361
+ maxDirectoryEntries: 10_000,
362
+ maxFiles: 4096,
363
+ maxBytes: 64 * 1024 * 1024,
364
+ maxEntries: 10_000,
365
+ maxWork: 100_000,
366
+ },
338
367
  });
339
368
  },
340
369
  getJournalWriterProtection(journalBindingKey) {
341
370
  const owner = Threads.getTelegramThreadOwnerFromProfileKey(journalBindingKey);
342
371
  if (owner.kind !== "manual-follower")
343
372
  return "unknown";
344
- const liveness = Bus.getTelegramProcessBirthIdentityLiveness(owner.instanceId);
373
+ const liveness = ProcessIdentity.getTelegramProcessBirthIdentityLiveness(owner.instanceId);
345
374
  return liveness === "alive"
346
375
  ? "protected"
347
- : liveness === "dead" ? "clear" : "unknown";
376
+ : liveness === "dead"
377
+ ? "clear"
378
+ : "unknown";
348
379
  },
349
380
  getDeliveryAuthorityProtection(binding) {
350
- const retainedRestore = resolveWorkspaceRestoreStore()?.list().some(function (intent) {
351
- return intent.request.binding.bindingKey === binding.bindingKey ||
381
+ const retainedRestore = resolveWorkspaceRestoreStore()
382
+ ?.list()
383
+ .some(function (intent) {
384
+ return (intent.request.binding.bindingKey === binding.bindingKey ||
352
385
  Targets.areTelegramTargetsEqual(intent.request.binding.target, binding.target) ||
353
- Targets.areTelegramTargetsEqual(intent.request.target, binding.target);
386
+ Targets.areTelegramTargetsEqual(intent.request.target, binding.target));
354
387
  });
355
- return retainedRestore || telegramApiTargetActivityRuntime.hasPendingTarget(binding.target)
388
+ return retainedRestore ||
389
+ telegramApiTargetActivityRuntime.hasPendingTarget(binding.target)
356
390
  ? "protected"
357
391
  : "clear";
358
392
  },
@@ -367,18 +401,7 @@ export default function (pi) {
367
401
  });
368
402
  const reclaimTelegramWorkspaceDeadOwnerQueue = WorkspaceRetirement.createTelegramWorkspaceDeadOwnerQueueReclaimer({
369
403
  getExternalProtection: captureWorkspaceExternalProtection,
370
- getActiveTurnTarget: activeTurnRuntime.getTarget,
371
- getQueuedItems: telegramQueueStore.getQueuedItems,
372
- resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
373
- createFollowerJournalResolver: telegramJournalBindingRuntime.createLegacyRecipientResolver,
374
- createSessionJournalResolver: telegramJournalBindingRuntime.createRecipientResolver,
375
- discoverFollowerJournals() {
376
- return Journal.discoverTelegramRecipientJournalPaths({
377
- directory: Paths.resolveTelegramTempDir(),
378
- profileName: configStore.getActiveProfileName(),
379
- });
380
- },
381
- createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
404
+ ...workspaceRetirementJournalPorts,
382
405
  withJournalReference(binding, operation) {
383
406
  if (!binding.recoveryKey)
384
407
  throw new Error("Telegram workspace journal reference identity is unavailable.");
@@ -395,7 +418,7 @@ export default function (pi) {
395
418
  sessionGeneration: telegramSessionContextStore.getGeneration(),
396
419
  };
397
420
  },
398
- getQueueOwnerLiveness: Bus.getTelegramProcessLiveness,
421
+ getQueueOwnerLiveness: ProcessIdentity.getTelegramProcessLiveness,
399
422
  onMutationError(error) {
400
423
  recordRuntimeEvent("Telegram dead-owner queue reclamation was refused.", {
401
424
  error: error instanceof Error ? error.message : String(error),
@@ -406,7 +429,9 @@ export default function (pi) {
406
429
  },
407
430
  });
408
431
  const inactiveThreadCleanupReviewRuntime = ThreadCleanupManager.createTelegramInactiveThreadCleanupReviewRuntime({
409
- getProfileName() { return configStore.getActiveProfileName() ?? "default"; },
432
+ getProfileName() {
433
+ return configStore.getActiveProfileName() ?? "default";
434
+ },
410
435
  listBindings: threadStore.listWorkspaceBindings,
411
436
  getProtection: captureWorkspaceExternalProtection,
412
437
  listReservations: threadStore.listReservations,
@@ -420,7 +445,9 @@ export default function (pi) {
420
445
  return ThreadCleanupManager.createTelegramThreadCleanupWorkStore({
421
446
  ...Paths.resolveTelegramServiceJournalStorage("thread-cleanup", undefined, profileName),
422
447
  profileName,
423
- tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
448
+ tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({
449
+ botToken,
450
+ }).tokenSha256,
424
451
  });
425
452
  },
426
453
  runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
@@ -484,7 +511,9 @@ export default function (pi) {
484
511
  registry: telegramJournalReferenceRegistry,
485
512
  resolveBinding: resolveTelegramUpdateJournalBinding,
486
513
  referenceClass: "polling-cursor",
487
- operation(binding) { return binding.journal.read().acceptedThroughUpdateId; },
514
+ operation(binding) {
515
+ return binding.journal.read().acceptedThroughUpdateId;
516
+ },
488
517
  });
489
518
  },
490
519
  getActiveSourceMessageIds: activeTurnRuntime.getSourceMessageIds,
@@ -534,7 +563,8 @@ export default function (pi) {
534
563
  getBotToken: configStore.getBotToken,
535
564
  getBotScope() {
536
565
  const config = configStore.get();
537
- return config.botUsername ?? (config.botId === undefined ? undefined : String(config.botId));
566
+ return (config.botUsername ??
567
+ (config.botId === undefined ? undefined : String(config.botId)));
538
568
  },
539
569
  recordRuntimeEvent,
540
570
  targetActivity: telegramApiTargetActivityRuntime,
@@ -762,15 +792,21 @@ export default function (pi) {
762
792
  getCommands,
763
793
  getReservedCommandNames: Commands.getTelegramReservedCommandNames,
764
794
  });
765
- const menuActions = Menu.createTelegramMenuActionRuntimeWithStateBuilder({
795
+ const getQueueMenuState = Menu.createTelegramModelMenuStateBuilder({
766
796
  runtime: modelMenuRuntime,
767
797
  createSettingsManager: Pi.createSettingsManager,
768
798
  getActiveModel: currentModelRuntime.get,
799
+ });
800
+ const menuActionPorts = {
801
+ getModelMenuState: getQueueMenuState,
802
+ getActiveModel: currentModelRuntime.get,
769
803
  getThinkingLevel,
770
804
  getQueueItemCount,
771
805
  getPendingCancellationCount() {
772
- return getCurrentLeaderEpoch() === undefined ? 0 :
773
- updateAdmissionRuntimeBinding.getActive()?.getState()?.abandoningClaimCount ?? 0;
806
+ return getCurrentLeaderEpoch() === undefined
807
+ ? 0
808
+ : (updateAdmissionRuntimeBinding.getActive()?.getState()
809
+ ?.abandoningClaimCount ?? 0);
774
810
  },
775
811
  buildStatusHtml: Commands.createTelegramAppMenuHtmlBuilder({
776
812
  buildStatusHtml: Status.createTelegramStatusHtmlBuilder({
@@ -783,22 +819,20 @@ export default function (pi) {
783
819
  storeModelMenuState: modelMenuRuntime.storeState,
784
820
  isIdle,
785
821
  canOfferInFlightModelSwitch: modelSwitchController.canOfferInFlightSwitch,
786
- sendTextReply,
787
- editInteractiveMessage,
788
- sendInteractiveMessage,
789
822
  sectionRegistry,
790
823
  // Menu/status UI uses this to reflect whether the active Telegram turn expects voice delivery.
791
824
  isVoiceReplyActive: function () {
792
825
  const turn = activeTurnRuntime.get();
793
826
  return Voice.isVoiceTurn(turn);
794
827
  },
828
+ };
829
+ const menuActions = Menu.createTelegramMenuActionRuntime({
830
+ ...menuActionPorts,
831
+ sendTextReply,
832
+ editInteractiveMessage,
833
+ sendInteractiveMessage,
795
834
  });
796
835
  // --- Queue And Settings Menus ---
797
- const getQueueMenuState = Menu.createTelegramModelMenuStateBuilder({
798
- runtime: modelMenuRuntime,
799
- createSettingsManager: Pi.createSettingsManager,
800
- getActiveModel: currentModelRuntime.get,
801
- });
802
836
  const queueMenuRuntime = MenuQueue.createTelegramQueueMenuRuntime({
803
837
  telegramQueueStore,
804
838
  queueMutationRuntime,
@@ -814,20 +848,30 @@ export default function (pi) {
814
848
  });
815
849
  const threadDisplaySettingsRuntime = ThreadDisplay.createTelegramThreadDisplaySettingsRuntime({
816
850
  getTarget() {
817
- return telegramBusFollowerRegistrationState.getTarget() ??
818
- telegramBusLeaderState.getTarget();
851
+ return (telegramBusFollowerRegistrationState.getTarget() ??
852
+ telegramBusLeaderState.getTarget());
853
+ },
854
+ getBinding(target) {
855
+ return threadStore.getWorkspaceBindingByTarget(target);
819
856
  },
820
- getBinding(target) { return threadStore.getWorkspaceBindingByTarget(target); },
821
857
  apply(mode) {
822
858
  return ThreadDisplay.applyTelegramThreadDisplaySetting(mode, {
823
859
  getProfileKey: configStore.getActiveProfileName,
824
- ownsLeader() { return getCurrentLeaderEpoch() !== undefined; },
825
- getLeaderSetter() { return telegramBusLeaderRuntime.setThreadDisplayMode; },
826
- getFollowerSetter() { return telegramBusFollowerRegistration.setThreadDisplayMode; },
860
+ ownsLeader() {
861
+ return getCurrentLeaderEpoch() !== undefined;
862
+ },
863
+ getLeaderSetter() {
864
+ return telegramBusLeaderRuntime.setThreadDisplayMode;
865
+ },
866
+ getFollowerSetter() {
867
+ return telegramBusFollowerRegistration.setThreadDisplayMode;
868
+ },
827
869
  reloadConfig: configStore.load,
828
870
  });
829
871
  },
830
- reset(target) { return telegramThreadDisplayNameResetBinding.reset(target); },
872
+ reset(target) {
873
+ return telegramThreadDisplayNameResetBinding.reset(target);
874
+ },
831
875
  });
832
876
  const settingsMenuRuntime = MenuSettings.createTelegramSettingsMenuRuntime({
833
877
  reloadConfig: configStore.load,
@@ -841,7 +885,8 @@ export default function (pi) {
841
885
  reviewInactiveThreads: inactiveThreadCleanupReviewRuntime.review,
842
886
  getThreadDisplayMode() {
843
887
  return threadStore.getBotState().threadMode === "enabled"
844
- ? Config.resolveTelegramThreadDisplayMode(configStore.get()) : undefined;
888
+ ? Config.resolveTelegramThreadDisplayMode(configStore.get())
889
+ : undefined;
845
890
  },
846
891
  isThreadDisplayCustom: threadDisplaySettingsRuntime.isCustom,
847
892
  async setThreadDisplayMode(mode) {
@@ -860,8 +905,42 @@ export default function (pi) {
860
905
  getFollower: telegramBusFollowerRegistry.get,
861
906
  localProtocolIdentity: telegramBusProtocolIdentity,
862
907
  createRequestId: telegramBusFollowerClients.createRequestId,
863
- getAuthSecret() { return telegramBusAuthSecret; },
908
+ getAuthSecret() {
909
+ return telegramBusAuthSecret;
910
+ },
864
911
  });
912
+ const liveRebindFollowerController = Bus.createTelegramBusLiveRebindController({
913
+ getFollower: telegramBusFollowerRegistry.get,
914
+ localProtocolIdentity: telegramBusProtocolIdentity,
915
+ createRequestId: telegramBusFollowerClients.createRequestId,
916
+ getAuthSecret() {
917
+ return telegramBusAuthSecret;
918
+ },
919
+ });
920
+ const liveFollowerPorts = {
921
+ getJournalBindingKey(follower) {
922
+ if (!follower.profileKey || !follower.sessionId)
923
+ return undefined;
924
+ return telegramJournalBindingRuntime.createRecipientResolver(follower.profileKey, follower.sessionId)()?.recoveryKey;
925
+ },
926
+ isSelectedCommandAvailable(follower, name) {
927
+ return (Bus.hasTelegramBusSharedCapabilities(telegramBusProtocolIdentity, follower.protocol, [
928
+ ...Bus.TELEGRAM_BUS_LIVE_REBIND_CAPABILITIES,
929
+ ...Bus.TELEGRAM_BUS_HELD_COMMAND_CAPABILITIES,
930
+ ]) &&
931
+ typeof selectedMenuDelivery === "function" &&
932
+ typeof inboundRouteRuntime.prepareHeldCommand === "function" &&
933
+ typeof inboundRouteRuntime.canPrepareHeldCommand === "function" &&
934
+ inboundRouteRuntime.canPrepareHeldCommand(name, {
935
+ showStatus: menuActions.sendStatusMessage,
936
+ sendTextReply,
937
+ }) &&
938
+ !!captureTelegramStateAuthority() &&
939
+ !!workspaceAdmissionRuntime.resolve() &&
940
+ !!resolveWorkspaceRestoreStore());
941
+ },
942
+ run: liveRebindFollowerController,
943
+ };
865
944
  const observedThreadTargetBinding = Polling.createTelegramThreadTargetObservationBinding();
866
945
  const topicLifecycleSync = Sync.createTelegramObservedTopicLifecycleSyncHandler({
867
946
  topicTargetStore: threadStore,
@@ -890,6 +969,18 @@ export default function (pi) {
890
969
  });
891
970
  const telegramThreadDisplayNameRenameBinding = Commands.createTelegramThreadDisplayNameRenameBinding();
892
971
  const telegramThreadDisplayNameResetBinding = Commands.createTelegramThreadDisplayNameResetBinding();
972
+ const observeLiveTargetWork = Bindings.createTelegramLiveTargetWorkObserver({
973
+ queue: telegramQueueStore,
974
+ activeTurn: activeTurnRuntime,
975
+ lifecycle: bridgeRuntime.lifecycle,
976
+ isIdle,
977
+ hasPendingMessages,
978
+ hasPendingControl: pendingModelSwitchStore.has,
979
+ publication: publicationRuntime,
980
+ activity: activityRuntime,
981
+ delivery: deliveryLifecycleRuntime,
982
+ api: telegramApiTargetActivityRuntime,
983
+ });
893
984
  const inboundRouteRuntime = Routing.createTelegramInboundRouteRuntime({
894
985
  configStore,
895
986
  callApi: callTelegramApi,
@@ -898,6 +989,7 @@ export default function (pi) {
898
989
  },
899
990
  getAdmissionScope: getTelegramUpdateAdmissionScope,
900
991
  getAdmissionJournalBinding: getTelegramQueueJournalBinding,
992
+ beginCommandEffectWork: publicationRuntime.beginWork,
901
993
  getMessageOwnership: messageOwnershipRuntime.getForwardOwnership,
902
994
  recordMessageOwnership: messageOwnershipRuntime.recordRouted,
903
995
  ...inboundBusProjectionRuntime,
@@ -915,24 +1007,40 @@ export default function (pi) {
915
1007
  getSessionId: Pi.getExtensionContextSessionId,
916
1008
  getCwd: Pi.getExtensionContextCwd,
917
1009
  getLeaderIdentity: telegramBusLeaderState.getIdentity,
1010
+ observeLeaderWork: observeLiveTargetWork,
918
1011
  followerRegistry: telegramBusFollowerRegistry,
919
1012
  runFollower: workspaceRestoreFollowerController,
1013
+ liveFollower: liveFollowerPorts,
920
1014
  },
921
1015
  getWorkspaceRestoreStore: resolveWorkspaceRestoreStore,
922
1016
  captureWorkspaceExternalProtection,
923
1017
  inspectRestoreSourceCompletion(expected) {
924
- return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition",
925
- recoveryKey: expected.journalBindingKey }, function () {
926
- const observed = telegramJournalBindingRuntime.inspectSourceCompletion(expected.journalBindingKey, { updateId: expected.updateId,
927
- sourceSha256: expected.sourceSha256, completionSha256: expected.completionSha256 });
928
- return observed && { journalBindingKey: expected.journalBindingKey, ...observed };
1018
+ return telegramJournalReferenceRegistry.withReference({
1019
+ referenceClass: "operator-disposition",
1020
+ recoveryKey: expected.journalBindingKey,
1021
+ }, function () {
1022
+ const observed = telegramJournalBindingRuntime.inspectSourceCompletion(expected.journalBindingKey, {
1023
+ updateId: expected.updateId,
1024
+ sourceSha256: expected.sourceSha256,
1025
+ completionSha256: expected.completionSha256,
1026
+ });
1027
+ return (observed && {
1028
+ journalBindingKey: expected.journalBindingKey,
1029
+ ...observed,
1030
+ });
929
1031
  });
930
1032
  },
931
1033
  inspectRestoreQueuedReceipt(expected) {
932
- return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition",
933
- recoveryKey: expected.journalBindingKey }, function () {
934
- return telegramJournalBindingRuntime.inspectQueuedReceipt(expected.journalBindingKey, { queueKind: expected.queueKind, receiptId: expected.receiptId,
935
- sourceUpdateIds: [...expected.sourceUpdateIds], queueOwner: { ...expected.queueOwner } });
1034
+ return telegramJournalReferenceRegistry.withReference({
1035
+ referenceClass: "operator-disposition",
1036
+ recoveryKey: expected.journalBindingKey,
1037
+ }, function () {
1038
+ return telegramJournalBindingRuntime.inspectQueuedReceipt(expected.journalBindingKey, {
1039
+ queueKind: expected.queueKind,
1040
+ receiptId: expected.receiptId,
1041
+ sourceUpdateIds: [...expected.sourceUpdateIds],
1042
+ queueOwner: { ...expected.queueOwner },
1043
+ });
936
1044
  });
937
1045
  },
938
1046
  inspectRestoreSourceAbandonment(updateId, journalBindingKey) {
@@ -944,31 +1052,72 @@ export default function (pi) {
944
1052
  });
945
1053
  },
946
1054
  inspectRoutingInputGroupExpiry(input) {
947
- return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition", recoveryKey: input.journalBindingKey }, function () { return telegramJournalBindingRuntime.inspectSourceGroupExpiry(input.journalBindingKey, input.updateIds); });
1055
+ return telegramJournalReferenceRegistry.withReference({
1056
+ referenceClass: "operator-disposition",
1057
+ recoveryKey: input.journalBindingKey,
1058
+ }, function () {
1059
+ return telegramJournalBindingRuntime.inspectSourceGroupExpiry(input.journalBindingKey, input.updateIds);
1060
+ });
948
1061
  },
949
- inspectTemporaryThreadSources(_target, requiredJournalBindingKeys) {
1062
+ inspectTemporaryThreadSources(target, requiredJournalBindingKeys, ownInputs) {
950
1063
  const binding = resolveTelegramUpdateJournalBinding();
951
1064
  const botToken = configStore.getBotToken();
952
1065
  if (!binding || !botToken)
953
1066
  return undefined;
954
1067
  const profile = configStore.getActiveProfileName() ?? "default";
955
- const botIdentity = Journal.createTelegramUpdateJournalBotIdentity({ botToken, botId: getTelegramBotId() });
1068
+ const botIdentity = Journal.createTelegramUpdateJournalBotIdentity({
1069
+ botToken,
1070
+ botId: getTelegramBotId(),
1071
+ });
956
1072
  return configStore.withSourceSerialization(function () {
957
1073
  return Journal.isTelegramThreadCleanupJournalNamespaceClear({
958
- directory: Paths.resolveTelegramTempDir(), profile, botIdentity,
1074
+ directory: Paths.resolveTelegramTempDir(),
1075
+ profile,
1076
+ botIdentity,
959
1077
  pollingPath: telegramLeaderJournalPath.resolve(configStore.getActiveProfileName()),
960
- requiredJournalBindingKeys: [binding.recoveryKey, ...requiredJournalBindingKeys],
961
- limits: { maxDirectoryEntries: 10_000, maxFiles: 4096, maxBytes: 64 * 1024 * 1024,
962
- maxEntries: 10_000, maxWork: 100_000 },
1078
+ requiredJournalBindingKeys: [
1079
+ binding.recoveryKey,
1080
+ ...requiredJournalBindingKeys,
1081
+ ],
1082
+ ...(target.threadId !== undefined && ownInputs
1083
+ ? {
1084
+ cleanup: {
1085
+ target: { chatId: target.chatId, threadId: target.threadId },
1086
+ ownInputs,
1087
+ },
1088
+ }
1089
+ : {}),
1090
+ limits: {
1091
+ maxDirectoryEntries: 10_000,
1092
+ maxFiles: 4096,
1093
+ maxBytes: 64 * 1024 * 1024,
1094
+ maxEntries: 10_000,
1095
+ maxWork: 100_000,
1096
+ },
963
1097
  withSourceReference(path, operation) {
964
- return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition",
965
- recoveryKey: Journal.createTelegramUpdateJournalBindingKey({ path, profileName: profile, botIdentity }) }, operation);
1098
+ return telegramJournalReferenceRegistry.withReference({
1099
+ referenceClass: "operator-disposition",
1100
+ recoveryKey: Journal.createTelegramUpdateJournalBindingKey({
1101
+ path,
1102
+ profileName: profile,
1103
+ botIdentity,
1104
+ }),
1105
+ }, operation);
966
1106
  },
967
- }) ? [] : undefined;
1107
+ })
1108
+ ? []
1109
+ : undefined;
968
1110
  });
969
1111
  },
970
1112
  hasWorkspaceRestoreAuthority() {
971
- return lockRuntime.owns() && Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE);
1113
+ return (lockRuntime.owns() &&
1114
+ Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE));
1115
+ },
1116
+ hasWorkspaceLiveRebindAuthority() {
1117
+ return (lockRuntime.owns() &&
1118
+ Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE) &&
1119
+ Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY) &&
1120
+ Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE));
972
1121
  },
973
1122
  getSessionGeneration: telegramSessionContextStore.getGeneration,
974
1123
  bridgeRuntime,
@@ -1007,6 +1156,7 @@ export default function (pi) {
1007
1156
  currentModelRuntime,
1008
1157
  modelSwitchController,
1009
1158
  menuActions,
1159
+ isVoiceReplyActive: menuActionPorts.isVoiceReplyActive,
1010
1160
  updateSettingsMenuMessage: settingsMenuRuntime.updateSettingsMenuMessage,
1011
1161
  openQueueMenu: queueMenuRuntime.openQueueMenu,
1012
1162
  queueMenuCallbackHandler: queueMenuRuntime.handleCallbackQuery,
@@ -1019,6 +1169,7 @@ export default function (pi) {
1019
1169
  inboundHandlerRuntime,
1020
1170
  threadStore,
1021
1171
  runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
1172
+ liveRebindCleanupSchedule: Routing.TELEGRAM_LIVE_REBIND_CLEANUP_SCHEDULE,
1022
1173
  updateStatus,
1023
1174
  isContextActive: telegramSessionContextStore.isCurrent,
1024
1175
  dispatchNextQueuedTelegramTurn,
@@ -1038,6 +1189,22 @@ export default function (pi) {
1038
1189
  startGuestPlaceholder: guestPlaceholderRuntime.start,
1039
1190
  sendTextReply,
1040
1191
  setMyCommands,
1192
+ captureThreadNameRecipientAuthority(target, ctx) {
1193
+ // Ordinary follower/classic dialogs keep their unguarded policy; closures are leader-local only.
1194
+ if (telegramBusFollowerRegistrationState.isRegistered() ||
1195
+ target.threadId === undefined)
1196
+ return undefined;
1197
+ return Threads.createTelegramWorkspaceThreadRenameRecipient({
1198
+ store: threadStore,
1199
+ instanceId: telegramInstanceId,
1200
+ target,
1201
+ assertAuthority() {
1202
+ if (telegramSessionContextStore.get() !== ctx)
1203
+ throw new Error("Telegram Thread name dialog lost Pi context.");
1204
+ },
1205
+ getAuthority: threadNameRecipientAuthority.getAuthority,
1206
+ }).assertAuthority;
1207
+ },
1041
1208
  validateThreadName(threadName) {
1042
1209
  return ThreadNaming.getTelegramManualThreadDisplayNameValidationError(threadName);
1043
1210
  },
@@ -1110,16 +1277,21 @@ export default function (pi) {
1110
1277
  if (verdict === "retain")
1111
1278
  return verdict;
1112
1279
  const journalBindingKey = getTelegramQueueJournalBinding();
1113
- if (journalBindingKey && resolveWorkspaceRestoreStore()?.list().some(function (intent) {
1114
- return intent.request.source.journalBindingKey === journalBindingKey &&
1115
- intent.request.source.updateIds.includes(entry.updateId);
1116
- }))
1280
+ if (journalBindingKey &&
1281
+ resolveWorkspaceRestoreStore()
1282
+ ?.list()
1283
+ .some(function (intent) {
1284
+ return (intent.request.source.journalBindingKey === journalBindingKey &&
1285
+ intent.request.source.updateIds.includes(entry.updateId));
1286
+ }))
1117
1287
  return true;
1118
1288
  return verdict;
1119
1289
  },
1120
1290
  spendHistoricalInput: true,
1121
1291
  onHeldSourcesPrepared(input) {
1122
- return inboundRouteRuntime.forgetPreviousWorld(input, lockedPollingRuntime.captureTransportAuthority).then(function () { });
1292
+ return inboundRouteRuntime
1293
+ .forgetPreviousWorld(input, lockedPollingRuntime.captureTransportAuthority)
1294
+ .then(function () { });
1123
1295
  },
1124
1296
  async shouldHoldPendingInput(entry, ctx, signal) {
1125
1297
  if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
@@ -1141,7 +1313,7 @@ export default function (pi) {
1141
1313
  isRegistered: telegramBusFollowerRegistrationState.isRegistered,
1142
1314
  getGeneration: telegramBusFollowerRegistrationState.getGeneration,
1143
1315
  prepareBinding(binding) {
1144
- telegramJournalBindingRuntime.prepareActiveFollowerSuccession(function isCurrent() {
1316
+ telegramJournalBindingRuntime.prepareActiveFollowerSuccession(function () {
1145
1317
  return binding.hasAuthority?.() === true;
1146
1318
  });
1147
1319
  },
@@ -1214,25 +1386,38 @@ export default function (pi) {
1214
1386
  local: agentMessageRuntime,
1215
1387
  follower: telegramBusFollowerClients.agentMessages,
1216
1388
  });
1217
- const followerWorkspaceRestore = BusFollower.createTelegramBusFollowerWorkspaceRestoreHandler({
1389
+ const followerWorkspaceTargetContextPorts = {
1390
+ isContextCurrent: telegramSessionContextStore.isCurrent,
1391
+ getSessionId: Pi.getExtensionContextSessionId,
1392
+ getCwd: Pi.getExtensionContextCwd,
1393
+ getGeneration: telegramSessionContextStore.getGeneration,
1394
+ getProfileBindingKey() {
1395
+ return workspaceAdmissionRuntime.resolve()?.getProfileKey();
1396
+ },
1397
+ getOperatorUserId: configStore.getAllowedUserId,
1398
+ getLeaderState: lockRuntime.getState,
1399
+ getAuthenticatedSecret: telegramBusFollowerControlState.getActiveAuthSecret,
1400
+ getLeaderProtocol: telegramBusFollowerRegistrationState.getLeaderProtocol,
1401
+ };
1402
+ const followerWorkspaceTargetOwnerPorts = {
1218
1403
  instanceId: telegramInstanceId,
1219
- getContextAuthority: BusFollower.createTelegramBusFollowerRestoreContextGetter({
1220
- isContextCurrent: telegramSessionContextStore.isCurrent,
1221
- getSessionId: Pi.getExtensionContextSessionId,
1222
- getCwd: Pi.getExtensionContextCwd,
1223
- getGeneration: telegramSessionContextStore.getGeneration,
1224
- getProfileBindingKey() {
1225
- return workspaceAdmissionRuntime.resolve()?.getProfileKey();
1226
- },
1227
- getOperatorUserId: configStore.getAllowedUserId,
1228
- getLeaderState: lockRuntime.getState,
1229
- getAuthenticatedSecret: telegramBusFollowerControlState.getActiveAuthSecret,
1230
- getLeaderProtocol: telegramBusFollowerRegistrationState.getLeaderProtocol,
1231
- }),
1232
1404
  readRestoreIntent(operationId, profileBindingKey) {
1233
- if (profileBindingKey !== workspaceAdmissionRuntime.resolve()?.getProfileKey())
1405
+ if (profileBindingKey !==
1406
+ workspaceAdmissionRuntime.resolve()?.getProfileKey())
1234
1407
  return undefined;
1235
- return resolveWorkspaceRestoreStore()?.list().find(function (intent) {
1408
+ return resolveWorkspaceRestoreStore()
1409
+ ?.list()
1410
+ .find(function (intent) {
1411
+ return intent.request.operationId === operationId;
1412
+ });
1413
+ },
1414
+ readLiveRebindIntent(operationId, profileBindingKey) {
1415
+ if (profileBindingKey !==
1416
+ workspaceAdmissionRuntime.resolve()?.getProfileKey())
1417
+ return undefined;
1418
+ return resolveWorkspaceRestoreStore()
1419
+ ?.listLiveRebindings()
1420
+ .find(function (intent) {
1236
1421
  return intent.request.operationId === operationId;
1237
1422
  });
1238
1423
  },
@@ -1240,6 +1425,91 @@ export default function (pi) {
1240
1425
  registrationState: telegramBusFollowerRegistrationState,
1241
1426
  getWorkspaceAdmission: workspaceAdmissionRuntime.resolve,
1242
1427
  recordRuntimeEvent,
1428
+ };
1429
+ const followerWorkspaceRestore = BusFollower.createTelegramBusFollowerWorkspaceRestoreHandler({
1430
+ ...followerWorkspaceTargetOwnerPorts,
1431
+ getContextAuthority: BusFollower.createTelegramBusFollowerRestoreContextGetter(followerWorkspaceTargetContextPorts),
1432
+ });
1433
+ const followerLiveRebindTarget = BusFollower.createTelegramBusFollowerWorkspaceRestoreHandler({
1434
+ ...followerWorkspaceTargetOwnerPorts,
1435
+ getContextAuthority: BusFollower.createTelegramBusFollowerRestoreContextGetter({
1436
+ ...followerWorkspaceTargetContextPorts,
1437
+ capability: Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY,
1438
+ }),
1439
+ });
1440
+ const selectedMenuCaller = BusFollower.createTelegramBusFollowerSelectedMenuCaller({
1441
+ client: {
1442
+ socketPath: getTelegramBusSocketPath,
1443
+ instanceId: telegramInstanceId,
1444
+ createRequestId: telegramBusFollowerClients.createRequestId,
1445
+ getAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
1446
+ getRegistrationGeneration: telegramBusFollowerRegistrationState.getGeneration,
1447
+ },
1448
+ protocolIdentity: telegramBusProtocolIdentity,
1449
+ recipient: {
1450
+ getContextAuthority: BusFollower.createTelegramBusFollowerRestoreContextGetter({
1451
+ ...followerWorkspaceTargetContextPorts,
1452
+ // Wire recipient identity is the registered profile key, not the admission namespace.
1453
+ getProfileBindingKey: getTelegramManualFollowerProfileKey,
1454
+ capability: Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
1455
+ }),
1456
+ getJournalBindingKey: followerAdmissionLifecycleRuntime.getJournalBindingKey,
1457
+ getProcessIdentity() {
1458
+ return {
1459
+ processId: telegramProcessId,
1460
+ processBirthId: telegramQueueProcessBirthId,
1461
+ };
1462
+ },
1463
+ registrationState: telegramBusFollowerRegistrationState,
1464
+ },
1465
+ });
1466
+ const followerLiveRebindRuntime = BusFollower.createTelegramBusFollowerLiveRebindRuntime({
1467
+ getAdmission(ctx) {
1468
+ return telegramSessionContextStore.isCurrent(ctx)
1469
+ ? followerAdmissionLifecycleRuntime
1470
+ : undefined;
1471
+ },
1472
+ applyTarget: followerLiveRebindTarget,
1473
+ getCommandOwner(input, ctx) {
1474
+ if (!Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET) ||
1475
+ !Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY) ||
1476
+ !input.selectedCommand ||
1477
+ !telegramSessionContextStore.isCurrent(ctx) ||
1478
+ followerAdmissionLifecycleRuntime.getJournalBindingKey() !==
1479
+ input.recipientBindingKey)
1480
+ return undefined;
1481
+ const recipient = followerLiveRebindTarget.prepareLiveCommandRecipient({
1482
+ operationId: input.operationId,
1483
+ registrationGeneration: input.recipientRegistrationGeneration,
1484
+ sessionId: input.recipientSessionId,
1485
+ sourceUpdateIds: input.updates.map(function sourceUpdateId(update) {
1486
+ return update.update_id;
1487
+ }),
1488
+ target: input.selectedCommand.target,
1489
+ }, ctx);
1490
+ return (recipient &&
1491
+ Bindings.createTelegramFollowerSelectedCommandBinding({
1492
+ ctx,
1493
+ operationId: input.operationId,
1494
+ registrationGeneration: input.recipientRegistrationGeneration,
1495
+ name: input.selectedCommand.name,
1496
+ target: input.selectedCommand.target,
1497
+ recipient,
1498
+ command: inboundRouteRuntime,
1499
+ menu: menuActionPorts,
1500
+ recordOwnership: messageOwnershipRuntime.recordLocal,
1501
+ deliver(effect, assertAuthority) {
1502
+ return selectedMenuCaller({
1503
+ ctx,
1504
+ operationId: input.operationId,
1505
+ registrationGeneration: input.recipientRegistrationGeneration,
1506
+ effect,
1507
+ assertAuthority,
1508
+ });
1509
+ },
1510
+ }));
1511
+ },
1512
+ observeWork: observeLiveTargetWork,
1243
1513
  });
1244
1514
  const telegramBusFollowerAssembly = BusFollower.createTelegramBusFollowerRuntimeAssembly({
1245
1515
  instanceId: telegramInstanceId,
@@ -1250,12 +1520,36 @@ export default function (pi) {
1250
1520
  getContext: telegramSessionContextStore.get,
1251
1521
  getAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
1252
1522
  getRecipientBindingKey: getTelegramManualFollowerProfileKey,
1523
+ getLiveRebindJournalBindingKey: followerAdmissionLifecycleRuntime.getJournalBindingKey,
1524
+ getSessionId() {
1525
+ return telegramBusFollowerAssembly.getReadySessionId();
1526
+ },
1527
+ getLeaderProtocol: telegramBusFollowerRegistrationState.getLeaderProtocol,
1528
+ getLocalProtocol() {
1529
+ return telegramBusProtocolIdentity;
1530
+ },
1531
+ isLiveRebindSaveEnabled() {
1532
+ return Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE);
1533
+ },
1534
+ isLiveRebindApplyEnabled() {
1535
+ return Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY);
1536
+ },
1537
+ isLiveRebindSettleEnabled() {
1538
+ return Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE);
1539
+ },
1540
+ isLiveRebindCommandSetEnabled() {
1541
+ return (Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET) &&
1542
+ Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY));
1543
+ },
1544
+ handleLiveRebindSave: followerLiveRebindRuntime.save,
1545
+ handleLiveRebindApply: followerLiveRebindRuntime.apply,
1546
+ handleLiveRebindSettle: followerLiveRebindRuntime.settle,
1253
1547
  durableAdmission: followerDurableAdmissionRuntime,
1254
1548
  handleQueueHandoff: acceptStagedQueueHandoff,
1255
1549
  handleWorkspaceRestore: followerWorkspaceRestore,
1256
1550
  isWorkspaceRestoreEnabled() {
1257
- return Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
1258
- Bus.hasTelegramBusCapability(telegramBusFollowerRegistrationState.getLeaderProtocol(), Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE);
1551
+ return (Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
1552
+ Bus.hasTelegramBusCapability(telegramBusFollowerRegistrationState.getLeaderProtocol(), Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE));
1259
1553
  },
1260
1554
  },
1261
1555
  recovery: {
@@ -1292,7 +1586,7 @@ export default function (pi) {
1292
1586
  const { admission: pollingAdmissionRuntime } = Polling.createTelegramDurablePollingRuntimeAssembly({
1293
1587
  state: pollingControllerState,
1294
1588
  canStart(ctx) {
1295
- return telegramSessionContextStore.isCurrent(ctx) && lockRuntime.owns(ctx);
1589
+ return (telegramSessionContextStore.isCurrent(ctx) && lockRuntime.owns(ctx));
1296
1590
  },
1297
1591
  onPersistentConflict(ctx, count) {
1298
1592
  return lockedPollingRuntime.onPersistentConflict(ctx, count);
@@ -1312,7 +1606,9 @@ export default function (pi) {
1312
1606
  registry: telegramJournalReferenceRegistry,
1313
1607
  resolveBinding: resolveTelegramUpdateJournalBinding,
1314
1608
  referenceClass: "polling-cursor",
1315
- operation(binding) { return binding.journal.read().acceptedThroughUpdateId; },
1609
+ operation(binding) {
1610
+ return binding.journal.read().acceptedThroughUpdateId;
1611
+ },
1316
1612
  });
1317
1613
  },
1318
1614
  async prepareCursorCutover() {
@@ -1341,12 +1637,14 @@ export default function (pi) {
1341
1637
  getEntryCount: updateAdmissionLifecycleRuntime.getJournalEntryCount,
1342
1638
  signalWorker: updateAdmissionLifecycleRuntime.signal,
1343
1639
  getBootstrapEntryCount() {
1344
- return Journal.withTelegramResolvedUpdateJournalReference({
1640
+ return (Journal.withTelegramResolvedUpdateJournalReference({
1345
1641
  registry: telegramJournalReferenceRegistry,
1346
1642
  resolveBinding: resolveTelegramUpdateJournalBinding,
1347
1643
  referenceClass: "polling-bootstrap",
1348
- operation(binding) { return binding.journal.read().entries.length; },
1349
- }) ?? 0;
1644
+ operation(binding) {
1645
+ return binding.journal.read().entries.length;
1646
+ },
1647
+ }) ?? 0);
1350
1648
  },
1351
1649
  onSessionStart: updateAdmissionLifecycleRuntime.onSessionStart,
1352
1650
  },
@@ -1358,13 +1656,59 @@ export default function (pi) {
1358
1656
  const authorizeFollowerApiCall = Bus.createTelegramFollowerApiCallAuthorizer({
1359
1657
  isMessageOwned: messageOwnershipRuntime.isOwnedByFollower,
1360
1658
  });
1659
+ const selectedMenuDelivery = BusLeader.createTelegramBusSelectedMenuDeliveryHandler({
1660
+ followerRegistry: telegramBusFollowerRegistry,
1661
+ protocolIdentity: telegramBusProtocolIdentity,
1662
+ workspace: {
1663
+ getScopeKey() {
1664
+ return workspaceAdmissionRuntime.resolve()?.getProfileKey();
1665
+ },
1666
+ captureAuthority() {
1667
+ const isCurrent = captureTelegramStateAuthority(), epoch = lockRuntime.getOwnedLeaderEpoch(), operatorUserId = configStore.getAllowedUserId();
1668
+ return isCurrent &&
1669
+ epoch !== undefined &&
1670
+ operatorUserId !== undefined
1671
+ ? {
1672
+ executor: {
1673
+ instanceId: telegramInstanceId,
1674
+ leaderEpoch: String(epoch),
1675
+ },
1676
+ operatorUserId,
1677
+ isCurrent,
1678
+ }
1679
+ : undefined;
1680
+ },
1681
+ getStore: resolveWorkspaceRestoreStore,
1682
+ threadStore,
1683
+ getJournalBindingKey: liveFollowerPorts.getJournalBindingKey,
1684
+ run: telegramWorkspaceOperationRuntime.run,
1685
+ },
1686
+ api: {
1687
+ runtime: directTelegramApiRuntime,
1688
+ authorize: authorizeFollowerApiCall,
1689
+ record(record, assertCurrent) {
1690
+ assertCurrent();
1691
+ messageOwnershipRuntime.recordFollower(record);
1692
+ assertCurrent();
1693
+ return messageOwnershipRuntime.isOwnedByFollower(record);
1694
+ },
1695
+ },
1696
+ });
1361
1697
  const telegramSessionFolderSweeper = Recovery.createTelegramSessionFolderSweeper({
1362
1698
  getSessionsDir: Paths.resolveTelegramSessionsDir,
1363
1699
  getProfileName: configStore.getActiveProfileName,
1364
1700
  getKeptSessionIds() {
1365
1701
  return [
1366
- ...threadStore.listWorkspaceBindings().map(function bindingSession(binding) { return binding.sessionId; }),
1367
- ...telegramBusFollowerRegistry.list().map(function followerSession(follower) { return follower.sessionId; }),
1702
+ ...threadStore
1703
+ .listWorkspaceBindings()
1704
+ .map(function bindingSession(binding) {
1705
+ return binding.sessionId;
1706
+ }),
1707
+ ...telegramBusFollowerRegistry
1708
+ .list()
1709
+ .map(function followerSession(follower) {
1710
+ return follower.sessionId;
1711
+ }),
1368
1712
  Pi.getExtensionContextSessionId(telegramSessionContextStore.get()),
1369
1713
  ];
1370
1714
  },
@@ -1378,6 +1722,7 @@ export default function (pi) {
1378
1722
  followerRegistry: telegramBusFollowerRegistry,
1379
1723
  authSecret: telegramBusAuthSecret,
1380
1724
  protocolIdentity: telegramBusProtocolIdentity,
1725
+ selectedMenuDelivery,
1381
1726
  startPolling: pollingAdmissionRuntime.start,
1382
1727
  stopPolling: pollingAdmissionRuntime.stop,
1383
1728
  authorizeFollowerApiCall,
@@ -1391,8 +1736,10 @@ export default function (pi) {
1391
1736
  message,
1392
1737
  });
1393
1738
  },
1394
- isFollowerProcessAlive: Locks.isProcessAlive,
1395
- afterFollowerPrune() { telegramSessionFolderSweeper.sweep(); },
1739
+ isFollowerProcessAlive: ProcessIdentity.isProcessAlive,
1740
+ afterFollowerPrune() {
1741
+ telegramSessionFolderSweeper.sweep();
1742
+ },
1396
1743
  shouldCleanupConfirmedDeadFollower: configControls.resolveAutomaticThreadCleanupEnabled,
1397
1744
  recordFollowerMessageOwnership(record) {
1398
1745
  messageOwnershipRuntime.recordFollower(record);
@@ -1572,13 +1919,14 @@ export default function (pi) {
1572
1919
  isCurrent: telegramSessionContextStore.isCurrent,
1573
1920
  getProfileName: configStore.getActiveProfileName,
1574
1921
  isConnected() {
1575
- return lockRuntime.owns() || telegramBusFollowerRegistrationState.isRegistered();
1922
+ return (lockRuntime.owns() ||
1923
+ telegramBusFollowerRegistrationState.isRegistered());
1576
1924
  },
1577
1925
  async activateProfile(profileName, isCurrent) {
1578
1926
  await configStore.load();
1579
1927
  if (!isCurrent())
1580
1928
  return false;
1581
- return configStore.activateProfile(profileName) && configStore.hasBotToken();
1929
+ return (configStore.activateProfile(profileName) && configStore.hasBotToken());
1582
1930
  },
1583
1931
  start(ctx) {
1584
1932
  return lockedPollingRuntime.start(ctx);
@@ -1649,14 +1997,46 @@ export default function (pi) {
1649
1997
  });
1650
1998
  const sessionLifecycleRuntime = Lifecycle.createTelegramBridgeSessionLifecycleAssembly(telegramBridgeSessionLifecycleDeps);
1651
1999
  // --- Extension API Bindings ---
2000
+ const threadNameRecipientAuthority = {
2001
+ getAuthority() {
2002
+ const ctx = telegramSessionContextStore.get();
2003
+ return {
2004
+ context: ctx,
2005
+ sessionId: Pi.getExtensionContextSessionId(ctx),
2006
+ sessionGeneration: telegramSessionContextStore.getGeneration(),
2007
+ cwd: ctx && Pi.getExtensionContextCwd(ctx),
2008
+ profileName: configStore.getActiveProfileName(),
2009
+ botToken: configStore.getBotToken(),
2010
+ operatorUserId: configStore.getAllowedUserId(),
2011
+ leaderEpoch: getCurrentLeaderEpoch(),
2012
+ ownsDirectDelivery: ownsTelegramDirectDelivery(),
2013
+ followerRegistered: telegramBusFollowerRegistrationState.isRegistered(),
2014
+ localTarget: telegramBusLeaderState.getTarget(),
2015
+ localSlot: telegramBusLeaderState.getIdentity()?.slot,
2016
+ };
2017
+ },
2018
+ };
1652
2019
  telegramThreadDisplayNameRenameBinding.bind({
1653
- async rename(expectedTarget, threadName) {
2020
+ async rename(expectedTarget, threadName, options) {
1654
2021
  try {
2022
+ const assertCaller = options?.assertAuthority;
2023
+ assertCaller?.();
1655
2024
  if (telegramBusFollowerRegistrationState.isRegistered()) {
2025
+ if (assertCaller)
2026
+ return {
2027
+ ok: false,
2028
+ message: "Guarded follower Workspace Thread rename is unavailable.",
2029
+ };
1656
2030
  if (typeof expectedTarget.threadId !== "number") {
1657
- return { ok: false, message: "Telegram Workspace Thread target is unavailable." };
2031
+ return {
2032
+ ok: false,
2033
+ message: "Telegram Workspace Thread target is unavailable.",
2034
+ };
1658
2035
  }
1659
- const renamedThreadName = await telegramBusFollowerRegistration.renameThread?.({ chatId: expectedTarget.chatId, threadId: expectedTarget.threadId }, threadName);
2036
+ const renamedThreadName = await telegramBusFollowerRegistration.renameThread?.({
2037
+ chatId: expectedTarget.chatId,
2038
+ threadId: expectedTarget.threadId,
2039
+ }, threadName);
1660
2040
  if (!renamedThreadName) {
1661
2041
  return {
1662
2042
  ok: false,
@@ -1675,14 +2055,32 @@ export default function (pi) {
1675
2055
  };
1676
2056
  }
1677
2057
  if (typeof expectedTarget.threadId !== "number") {
1678
- return { ok: false, message: "Telegram Workspace Thread target is unavailable." };
2058
+ return {
2059
+ ok: false,
2060
+ message: "Telegram Workspace Thread target is unavailable.",
2061
+ };
1679
2062
  }
1680
- const renamed = await telegramBusLeaderRuntime.renameLeaderThreadAdmitted(threadName, { chatId: expectedTarget.chatId, threadId: expectedTarget.threadId });
2063
+ const target = {
2064
+ chatId: expectedTarget.chatId,
2065
+ threadId: expectedTarget.threadId,
2066
+ };
2067
+ const recipient = assertCaller
2068
+ ? Threads.createTelegramWorkspaceThreadRenameRecipient({
2069
+ store: threadStore,
2070
+ instanceId: telegramInstanceId,
2071
+ target,
2072
+ assertAuthority: assertCaller,
2073
+ getAuthority: threadNameRecipientAuthority.getAuthority,
2074
+ })
2075
+ : undefined;
2076
+ const renamed = await telegramBusLeaderRuntime.renameLeaderThreadAdmitted(threadName, target, recipient?.assertAuthority);
2077
+ recipient?.assertResult(renamed);
1681
2078
  telegramBusLeaderState.set({
1682
2079
  target: renamed.target,
1683
2080
  slot: renamed.slot,
1684
2081
  threadName: renamed.manualThreadName ?? renamed.threadName,
1685
2082
  });
2083
+ recipient?.assertResult(renamed);
1686
2084
  return {
1687
2085
  ok: true,
1688
2086
  threadName: renamed.manualThreadName,
@@ -1699,26 +2097,58 @@ export default function (pi) {
1699
2097
  },
1700
2098
  }.rename);
1701
2099
  telegramThreadDisplayNameResetBinding.bind({
1702
- async reset(expectedTarget) {
2100
+ async reset(expectedTarget, options) {
1703
2101
  try {
2102
+ const assertCaller = options?.assertAuthority;
2103
+ assertCaller?.();
2104
+ const wasFollower = telegramBusFollowerRegistrationState.isRegistered();
2105
+ if (wasFollower && assertCaller)
2106
+ return {
2107
+ ok: false,
2108
+ message: "Guarded follower Workspace Thread reset is unavailable.",
2109
+ };
1704
2110
  if (typeof expectedTarget.threadId !== "number") {
1705
- return { ok: false, message: "Telegram Workspace Thread target is unavailable." };
2111
+ return {
2112
+ ok: false,
2113
+ message: "Telegram Workspace Thread target is unavailable.",
2114
+ };
1706
2115
  }
1707
2116
  const target = {
1708
2117
  chatId: expectedTarget.chatId,
1709
2118
  threadId: expectedTarget.threadId,
1710
2119
  };
1711
- const wasFollower = telegramBusFollowerRegistrationState.isRegistered();
2120
+ const recipient = assertCaller
2121
+ ? Threads.createTelegramWorkspaceThreadResetRecipient({
2122
+ store: threadStore,
2123
+ instanceId: telegramInstanceId,
2124
+ target,
2125
+ assertAuthority: assertCaller,
2126
+ getAuthority: threadNameRecipientAuthority.getAuthority,
2127
+ })
2128
+ : undefined;
1712
2129
  const result = wasFollower
1713
2130
  ? await telegramBusFollowerRegistration.resetThreadName?.(target)
1714
- : (await telegramBusLeaderRuntime.resetLeaderThreadName(target)).threadName;
2131
+ : (await (recipient
2132
+ ? telegramBusLeaderRuntime.resetLeaderThreadName(target, recipient.assertAuthority)
2133
+ : telegramBusLeaderRuntime.resetLeaderThreadName(target))).threadName;
1715
2134
  if (!result) {
1716
- return { ok: false, message: "Thread display name reset is unavailable." };
2135
+ return {
2136
+ ok: false,
2137
+ message: "Thread display name reset is unavailable.",
2138
+ };
1717
2139
  }
2140
+ recipient?.assertResult(result);
1718
2141
  const leaderTarget = telegramBusLeaderState.getTarget();
1719
2142
  if (!wasFollower && leaderTarget) {
1720
- telegramBusLeaderState.set({ target: leaderTarget, threadName: result });
2143
+ telegramBusLeaderState.set({
2144
+ target: leaderTarget,
2145
+ threadName: result,
2146
+ ...(recipient
2147
+ ? { slot: telegramBusLeaderState.getIdentity()?.slot }
2148
+ : {}),
2149
+ });
1721
2150
  }
2151
+ recipient?.assertResult(result);
1722
2152
  return {
1723
2153
  ok: true,
1724
2154
  threadName: result,
@@ -1728,7 +2158,8 @@ export default function (pi) {
1728
2158
  return {
1729
2159
  ok: false,
1730
2160
  message: error instanceof Error
1731
- ? error.message : "Thread display name reset failed.",
2161
+ ? error.message
2162
+ : "Thread display name reset failed.",
1732
2163
  };
1733
2164
  }
1734
2165
  },
@@ -1769,20 +2200,28 @@ export default function (pi) {
1769
2200
  const botToken = configStore.getBotToken();
1770
2201
  if (!botToken)
1771
2202
  throw new Error("Telegram channel delivery requires an active bot token.");
1772
- const store = ChannelPosts.createTelegramChannelPostJournalStore({
1773
- ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
2203
+ const store = ChannelPosts.openTelegramChannelPostJournalStore({
1774
2204
  profileName,
1775
- tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
2205
+ botToken,
1776
2206
  });
1777
2207
  const record = await ChannelPosts.publishTelegramChannelPost({
1778
- store, operationId: options.operationId,
1779
- channel: channel, markdown,
2208
+ store,
2209
+ operationId: options.operationId,
2210
+ channel: channel,
2211
+ markdown,
1780
2212
  async observeChannel(channelAddress) {
1781
- return telegramApiRuntime.call("getChat", { chat_id: channelAddress });
2213
+ return telegramApiRuntime.call("getChat", {
2214
+ chat_id: channelAddress,
2215
+ });
1782
2216
  },
1783
2217
  async send(channelAddress, body) {
1784
- const sent = await telegramApiRuntime.call("sendRichMessage", { chat_id: channelAddress, rich_message: { markdown: body },
1785
- ...(options.replyMarkup ? { reply_markup: options.replyMarkup } : {}) });
2218
+ const sent = await telegramApiRuntime.call("sendRichMessage", {
2219
+ chat_id: channelAddress,
2220
+ rich_message: { markdown: body },
2221
+ ...(options.replyMarkup
2222
+ ? { reply_markup: options.replyMarkup }
2223
+ : {}),
2224
+ });
1786
2225
  return { messageId: sent.message_id, chat: sent.chat };
1787
2226
  },
1788
2227
  });
@@ -1799,23 +2238,29 @@ export default function (pi) {
1799
2238
  const media = await ChannelPosts.inspectTelegramChannelPostMedia(mediaPath);
1800
2239
  const caption = Replies.renderTelegramMarkdownToHtmlDraft(markdown);
1801
2240
  ChannelPosts.assertTelegramChannelPostCaptionWithinLimit(caption);
1802
- const store = ChannelPosts.createTelegramChannelPostJournalStore({
1803
- ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
2241
+ const store = ChannelPosts.openTelegramChannelPostJournalStore({
1804
2242
  profileName,
1805
- tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
2243
+ botToken,
1806
2244
  });
1807
2245
  const record = await ChannelPosts.publishTelegramChannelPost({
1808
- store, operationId: options.operationId,
1809
- channel: channel, markdown, media,
2246
+ store,
2247
+ operationId: options.operationId,
2248
+ channel: channel,
2249
+ markdown,
2250
+ media,
1810
2251
  async observeChannel(channelAddress) {
1811
- return telegramApiRuntime.call("getChat", { chat_id: channelAddress });
2252
+ return telegramApiRuntime.call("getChat", {
2253
+ chat_id: channelAddress,
2254
+ });
1812
2255
  },
1813
2256
  async send(channelAddress) {
1814
2257
  const sent = await telegramApiRuntime.callMultipart(media.kind === "photo" ? "sendPhoto" : "sendVideo", {
1815
2258
  chat_id: String(channelAddress),
1816
2259
  caption,
1817
2260
  parse_mode: "HTML",
1818
- ...(options.replyMarkup ? { reply_markup: JSON.stringify(options.replyMarkup) } : {}),
2261
+ ...(options.replyMarkup
2262
+ ? { reply_markup: JSON.stringify(options.replyMarkup) }
2263
+ : {}),
1819
2264
  }, media.kind === "photo" ? "photo" : "video", mediaPath, media.fileName);
1820
2265
  return { messageId: sent.message_id, chat: sent.chat };
1821
2266
  },
@@ -1827,10 +2272,9 @@ export default function (pi) {
1827
2272
  const botToken = configStore.getBotToken();
1828
2273
  if (!botToken)
1829
2274
  throw new Error("Telegram channel posts require an active bot token.");
1830
- return ChannelPosts.createTelegramChannelPostJournalStore({
1831
- ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
2275
+ return ChannelPosts.openTelegramChannelPostJournalStore({
1832
2276
  profileName,
1833
- tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
2277
+ botToken,
1834
2278
  }).list(input);
1835
2279
  },
1836
2280
  async mutateChannelPost(input) {
@@ -1840,23 +2284,28 @@ export default function (pi) {
1840
2284
  const botToken = configStore.getBotToken();
1841
2285
  if (!botToken)
1842
2286
  throw new Error("Telegram channel post mutation requires an active bot token.");
1843
- const store = ChannelPosts.createTelegramChannelPostJournalStore({
1844
- ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName), profileName,
1845
- tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
2287
+ const store = ChannelPosts.openTelegramChannelPostJournalStore({
2288
+ profileName,
2289
+ botToken,
1846
2290
  });
1847
2291
  if (input.action === "edit") {
1848
2292
  if (!input.markdown)
1849
2293
  throw new Error("Telegram channel post edit requires markdown.");
1850
2294
  const current = store.get(input.operationId);
1851
2295
  const caption = current?.media
1852
- ? Replies.renderTelegramMarkdownToHtmlDraft(input.markdown) : undefined;
2296
+ ? Replies.renderTelegramMarkdownToHtmlDraft(input.markdown)
2297
+ : undefined;
1853
2298
  if (caption !== undefined) {
1854
2299
  ChannelPosts.assertTelegramChannelPostCaptionWithinLimit(caption);
1855
2300
  }
1856
- const begun = store.beginEdit({ operationId: input.operationId,
1857
- mutationId: input.mutationId, markdown: input.markdown });
2301
+ const begun = store.beginEdit({
2302
+ operationId: input.operationId,
2303
+ mutationId: input.mutationId,
2304
+ markdown: input.markdown,
2305
+ });
1858
2306
  if (!begun.began) {
1859
- if (begun.record.state === "published" && begun.record.lastMutationId === input.mutationId)
2307
+ if (begun.record.state === "published" &&
2308
+ begun.record.lastMutationId === input.mutationId)
1860
2309
  return begun.record;
1861
2310
  throw new Error("Telegram channel post edit outcome is unknown; refusing automatic replay.");
1862
2311
  }
@@ -1866,29 +2315,48 @@ export default function (pi) {
1866
2315
  if (caption === undefined) {
1867
2316
  throw new Error("Telegram channel post media caption edit requires retained media identity.");
1868
2317
  }
1869
- await telegramApiRuntime.call("editMessageCaption", { chat_id: begun.record.channelId,
1870
- message_id: begun.record.messageId, caption, parse_mode: "HTML" });
2318
+ await telegramApiRuntime.call("editMessageCaption", {
2319
+ chat_id: begun.record.channelId,
2320
+ message_id: begun.record.messageId,
2321
+ caption,
2322
+ parse_mode: "HTML",
2323
+ });
1871
2324
  }
1872
2325
  else {
1873
- await telegramApiRuntime.call("editMessageText", { chat_id: begun.record.channelId,
2326
+ await telegramApiRuntime.call("editMessageText", {
2327
+ chat_id: begun.record.channelId,
1874
2328
  message_id: begun.record.messageId,
1875
- text: Replies.renderTelegramMarkdownToHtmlDraft(input.markdown), parse_mode: "HTML" });
2329
+ text: Replies.renderTelegramMarkdownToHtmlDraft(input.markdown),
2330
+ parse_mode: "HTML",
2331
+ });
1876
2332
  }
1877
- return store.confirmEdited({ operationId: input.operationId, mutationId: input.mutationId }).record;
2333
+ return store.confirmEdited({
2334
+ operationId: input.operationId,
2335
+ mutationId: input.mutationId,
2336
+ }).record;
1878
2337
  }
1879
2338
  if (input.markdown !== undefined)
1880
2339
  throw new Error("Telegram channel post deletion does not accept markdown.");
1881
- const begun = store.beginDelete({ operationId: input.operationId, mutationId: input.mutationId });
2340
+ const begun = store.beginDelete({
2341
+ operationId: input.operationId,
2342
+ mutationId: input.mutationId,
2343
+ });
1882
2344
  if (!begun.began) {
1883
- if (begun.record.state === "deleted" && begun.record.mutationId === input.mutationId)
2345
+ if (begun.record.state === "deleted" &&
2346
+ begun.record.mutationId === input.mutationId)
1884
2347
  return begun.record;
1885
2348
  throw new Error("Telegram channel post deletion outcome is unknown; refusing automatic replay.");
1886
2349
  }
1887
2350
  if (begun.record.state !== "delete-outcome-unknown")
1888
2351
  throw new Error("Telegram channel post deletion authority is invalid.");
1889
- await telegramApiRuntime.call("deleteMessage", { chat_id: begun.record.channelId,
1890
- message_id: begun.record.messageId });
1891
- return store.confirmDeleted({ operationId: input.operationId, mutationId: input.mutationId }).record;
2352
+ await telegramApiRuntime.call("deleteMessage", {
2353
+ chat_id: begun.record.channelId,
2354
+ message_id: begun.record.messageId,
2355
+ });
2356
+ return store.confirmDeleted({
2357
+ operationId: input.operationId,
2358
+ mutationId: input.mutationId,
2359
+ }).record;
1892
2360
  },
1893
2361
  callMultipart,
1894
2362
  getDefaultChatId: proactivePushChatIdGetter,