@llblab/pi-telegram 0.21.0 → 0.22.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.
package/index.ts CHANGED
@@ -83,71 +83,47 @@ export default function (pi: Pi.ExtensionAPI) {
83
83
  const telegramBusAuthSecret = Bus.createTelegramBusAuthSecret();
84
84
  let telegramActiveBusAuthSecret: string | undefined;
85
85
  let telegramBusLifecycleOverridePhase:
86
- | Status.TelegramBridgeBusLifecyclePhase
87
- | undefined;
88
- let telegramBusRequestSequence = 0;
86
+ Status.TelegramBridgeBusLifecyclePhase | undefined;
89
87
  const telegramBusFollowerRegistry = Bus.createTelegramBusFollowerRegistry();
90
88
  const telegramBusFollowerRegistrationState =
91
89
  BusFollower.createTelegramBusFollowerRegistrationState();
92
- let telegramBusLeaderTarget: Config.TelegramProactivePushTarget | undefined;
93
- let telegramBusLeaderSlot: string | undefined;
94
- let telegramBusLeaderThreadName: string | undefined;
95
- let telegramTopicModeUnavailable = false;
96
- let telegramTopicProvisioningCount = 0;
97
- const messageOwnershipStore = Ownership.createTelegramMessageOwnershipStore();
90
+ const telegramBusLeaderState =
91
+ Threads.createTelegramLeaderThreadStateRuntime();
92
+ const telegramThreadCapabilityState =
93
+ Polling.createTelegramThreadCapabilityStateRuntime();
94
+ const telegramProvisioningActivity =
95
+ Sync.createTelegramProvisioningActivityRuntime();
96
+ const messageOwnershipRuntime =
97
+ Ownership.createTelegramBusMessageOwnershipRuntime({
98
+ instanceId: telegramInstanceId,
99
+ getProfileKey() {
100
+ return getActiveTelegramThreadProfile() ?? "default";
101
+ },
102
+ listFollowers: telegramBusFollowerRegistry.list,
103
+ });
98
104
  const { abort, lifecycle, queue, setup, typing } = bridgeRuntime;
99
- let configStoreForRedaction: Config.TelegramConfigStore | undefined;
100
- const runtimeEvents = Status.createTelegramRuntimeEventRecorder({
101
- getBotToken() {
102
- return configStoreForRedaction?.getBotToken();
103
- },
104
- });
105
- let getRuntimeLogProfileName = function (): string | undefined {
106
- return undefined;
107
- };
108
- const runtimeJsonlLog = Logs.createTelegramRuntimeJsonlLog({
109
- path: function () {
110
- return Logs.getTelegramRuntimeLogPath(
111
- undefined,
112
- getRuntimeLogProfileName(),
113
- );
114
- },
115
- previousPath: function () {
116
- return Logs.getTelegramPreviousRuntimeLogPath(
117
- undefined,
118
- getRuntimeLogProfileName(),
119
- );
120
- },
121
- });
122
- runtimeJsonlLog.reset("extension-start", {
123
- instanceId: telegramInstanceId,
124
- pid: process.pid,
125
- });
126
- let scheduleRuntimeDiagnosticsSnapshotPersist = function () {};
127
- const recordRuntimeEvent = function (
128
- category: string,
129
- error: unknown,
130
- details?: Record<string, unknown>,
131
- ) {
132
- runtimeEvents.record(category, error, details);
133
- const latestEvent = runtimeEvents.getEvents().at(-1);
134
- if (latestEvent) runtimeJsonlLog.record(latestEvent);
135
- scheduleRuntimeDiagnosticsSnapshotPersist();
136
- };
105
+ const runtimeDiagnostics =
106
+ Logs.createTelegramRuntimeDiagnosticsRuntime<Pi.ExtensionContext>();
107
+ const runtimeEvents = runtimeDiagnostics.events;
108
+ const recordRuntimeEvent = runtimeDiagnostics.recordRuntimeEvent;
137
109
  const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
138
- configStoreForRedaction = configStore;
139
- getRuntimeLogProfileName = configStore.getActiveProfileName;
140
110
  const isTelegramBusConfigured = function (): boolean {
141
111
  return true;
142
112
  };
143
113
  const isTelegramBusRuntimeEnabled = function (): boolean {
144
- return isTelegramBusConfigured() && !telegramTopicModeUnavailable;
114
+ return (
115
+ isTelegramBusConfigured() &&
116
+ !telegramThreadCapabilityState.isTopicModeUnavailable()
117
+ );
145
118
  };
146
119
  Config.bindGlobalTelegramConfigRuntime(configStore);
147
120
  const configControls = Config.createTelegramConfigControls(configStore);
148
- let canPersistThreadState = function (): boolean {
149
- return false;
150
- };
121
+ const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
122
+ key: Locks.createTelegramLockKeyResolver(configStore),
123
+ instanceId: telegramInstanceId,
124
+ busSecret: telegramBusAuthSecret,
125
+ staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
126
+ });
151
127
  const threadStore = Threads.createTelegramTopicTargetStore({
152
128
  path: function () {
153
129
  return Threads.getTelegramTopicTargetsPath(
@@ -155,27 +131,24 @@ export default function (pi: Pi.ExtensionAPI) {
155
131
  configStore.getActiveProfileName(),
156
132
  );
157
133
  },
158
- canPersist: function () {
159
- return canPersistThreadState();
160
- },
134
+ canPersist: lockRuntime.owns,
135
+ commitPersist: lockRuntime.commitIfOwned,
161
136
  });
162
- const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
163
- key: Locks.createTelegramLockKeyResolver(configStore),
164
- instanceId: telegramInstanceId,
165
- busSecret: telegramBusAuthSecret,
166
- staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
137
+ runtimeDiagnostics.bindStorage({
138
+ getBotToken: configStore.getBotToken,
139
+ getProfileName: configStore.getActiveProfileName,
140
+ canReset: lockRuntime.owns,
141
+ commitReset: lockRuntime.commitIfOwned,
167
142
  });
168
- canPersistThreadState = function (): boolean {
169
- return lockRuntime.getState().kind === "active-here";
170
- };
171
143
  const lockOwnershipGuard =
172
144
  Locks.createTelegramLockOwnershipGuard(lockRuntime);
173
- const getCurrentLeaderEpoch = function (): number | undefined {
174
- const state = lockRuntime.getState();
175
- return state.kind === "active-here" ? state.lock.leaderEpoch : undefined;
176
- };
145
+ const getCurrentLeaderEpoch = lockRuntime.getOwnedLeaderEpoch;
177
146
  const telegramSessionContextStore =
178
- Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>();
147
+ Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>({
148
+ getIdentity(ctx) {
149
+ return ctx.sessionManager ?? ctx.cwd;
150
+ },
151
+ });
179
152
  const ownsTelegramDirectDelivery =
180
153
  Locks.createTelegramDirectDeliveryOwnershipChecker({
181
154
  lock: lockRuntime,
@@ -193,7 +166,7 @@ export default function (pi: Pi.ExtensionAPI) {
193
166
  getAssignedTarget() {
194
167
  return (
195
168
  telegramBusFollowerRegistrationState.getTarget() ??
196
- telegramBusLeaderTarget
169
+ telegramBusLeaderState.getTarget()
197
170
  );
198
171
  },
199
172
  getAllowedUserId: configStore.getAllowedUserId,
@@ -223,89 +196,83 @@ export default function (pi: Pi.ExtensionAPI) {
223
196
  TelegramApi.TelegramMessage,
224
197
  Pi.ExtensionContext
225
198
  >();
226
- const telegramQueueStore =
199
+ const rawTelegramQueueStore =
227
200
  Queue.createTelegramQueueStore<Pi.ExtensionContext>();
201
+ const telegramTransportStampRuntime =
202
+ Queue.createTelegramTransportStampRuntime({
203
+ getProfileName: configStore.getActiveProfileName,
204
+ getBotToken: configStore.getBotToken,
205
+ });
206
+ const telegramQueueStore = Queue.createTelegramTransportStampedQueueStore(
207
+ rawTelegramQueueStore,
208
+ telegramTransportStampRuntime.getStamp,
209
+ );
228
210
  const deferredQueueDispatchRuntime =
229
211
  Queue.createTelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>({
230
212
  delayMs: 50,
231
213
  recordRuntimeEvent,
232
214
  });
233
215
  const pollingControllerState = Polling.createTelegramPollingControllerState();
234
- let telegramSyncState = Sync.createUnknownTelegramSyncState();
216
+ const telegramSyncStateRuntime = Sync.createTelegramSyncStateRuntime();
235
217
  const threadReconciliationRuntime =
236
218
  ThreadReconciler.createThreadReconciliationRuntime({
237
219
  recordRuntimeEvent,
238
- scheduleSnapshotPersist() {
239
- scheduleRuntimeDiagnosticsSnapshotPersist();
240
- },
220
+ scheduleSnapshotPersist: runtimeDiagnostics.scheduleSnapshotPersist,
241
221
  });
242
222
  const recordThreadReconciliationPlan = threadReconciliationRuntime.recordPlan;
243
- const markTelegramConfigSyncChange = function (action: string) {
244
- telegramSyncState = Sync.markTelegramConfigSyncChange(
245
- telegramSyncState,
246
- action,
247
- );
248
- };
249
- const persistTelegramConfigWithSync = async function () {
250
- await configStore.persist();
223
+ const markTelegramConfigSyncChange =
224
+ telegramSyncStateRuntime.markConfigChange;
225
+ const persistTelegramConfigWithSync = async function (
226
+ nextConfig?: Config.TelegramConfig,
227
+ ) {
228
+ await configStore.persist(nextConfig);
251
229
  markTelegramConfigSyncChange("config-persist");
252
230
  };
253
- const findCurrentThreadRecord = function ():
254
- | Threads.TelegramTopicTargetRecord
255
- | undefined {
256
- return Threads.findCurrentTelegramInstanceThreadRecord({
257
- records: threadStore.list(),
231
+ const currentInstanceThreadRuntime =
232
+ Threads.createTelegramCurrentInstanceThreadRuntime({
258
233
  instanceId: telegramInstanceId,
259
- preferredTarget:
260
- activeTurnRuntime.getTarget() ??
261
- telegramBusFollowerRegistrationState.getTarget() ??
262
- telegramBusLeaderTarget,
234
+ listRecords: threadStore.list,
235
+ getPreferredTarget() {
236
+ return (
237
+ activeTurnRuntime.getTarget() ??
238
+ telegramBusFollowerRegistrationState.getTarget() ??
239
+ telegramBusLeaderState.getTarget()
240
+ );
241
+ },
242
+ getFollower() {
243
+ const target = telegramBusFollowerRegistrationState.getTarget();
244
+ if (!target) return undefined;
245
+ return {
246
+ registered: telegramBusFollowerRegistrationState.isRegistered(),
247
+ target,
248
+ slot: telegramBusFollowerRegistrationState.getSlot(),
249
+ threadName: telegramBusFollowerRegistrationState.getThreadName(),
250
+ };
251
+ },
252
+ getLeader: telegramBusLeaderState.getIdentity,
263
253
  });
264
- };
265
- const getCurrentThreadRecord = function ():
266
- | Threads.TelegramTopicTargetRecord
267
- | undefined {
268
- const record = findCurrentThreadRecord();
269
- if (
270
- record?.owner?.kind === "manual-follower" &&
271
- !telegramBusFollowerRegistrationState.isRegistered()
272
- ) {
273
- return undefined;
274
- }
275
- return record;
276
- };
277
- const getCurrentInstanceThreadIdentity = function (
278
- target?: Queue.TelegramQueueTarget,
279
- ): Threads.TelegramInstanceThreadIdentityCandidate {
280
- const followerTarget = telegramBusFollowerRegistrationState.getTarget();
281
- const record = target
282
- ? Threads.findCurrentTelegramInstanceThreadRecord({
283
- records: threadStore.list(),
284
- instanceId: telegramInstanceId,
285
- preferredTarget: target,
286
- })
287
- : getCurrentThreadRecord();
288
- return Threads.resolveTelegramInstanceThreadIdentity({
289
- target,
290
- follower:
291
- telegramBusFollowerRegistrationState.isRegistered() && followerTarget
292
- ? {
293
- target: followerTarget,
294
- slot: telegramBusFollowerRegistrationState.getSlot(),
295
- threadName:
296
- telegramBusFollowerRegistrationState.getThreadName(),
297
- }
298
- : undefined,
299
- leader: telegramBusLeaderTarget
300
- ? {
301
- target: telegramBusLeaderTarget,
302
- slot: telegramBusLeaderSlot,
303
- threadName: telegramBusLeaderThreadName,
304
- }
305
- : undefined,
306
- record,
254
+ const findCurrentThreadRecord = currentInstanceThreadRuntime.findRecord;
255
+ const getCurrentInstanceThreadIdentity =
256
+ currentInstanceThreadRuntime.getIdentity;
257
+ const threadStatusProjectionRuntime =
258
+ Threads.createTelegramThreadStatusProjectionRuntime({
259
+ getThreadMode: function () {
260
+ return threadStore.getBotState().threadMode;
261
+ },
262
+ isBusPollingStarted: telegramThreadCapabilityState.isBusPollingStarted,
263
+ isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
264
+ listFollowers: telegramBusFollowerRegistry.list,
265
+ listRecords: threadStore.list,
266
+ listReservations: threadStore.listReservations,
267
+ listSyncObservations: threadStore.listSyncObservations,
268
+ getLeaderSocketPath: getTelegramBusSocketPath,
269
+ getFollowerSocketPath: getTelegramBusFollowerSocketPath,
270
+ getTransportKind: BusTransport.getTelegramBusTransportKind,
271
+ getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
272
+ getFollowerSlot: telegramBusFollowerRegistrationState.getSlot,
273
+ getFollowerThreadName: telegramBusFollowerRegistrationState.getThreadName,
274
+ getCurrentIdentity: getCurrentInstanceThreadIdentity,
307
275
  });
308
- };
309
276
  const statusRuntime = Status.createTelegramBridgeStatusRuntime<
310
277
  Pi.ExtensionContext,
311
278
  Queue.TelegramQueueItem<Pi.ExtensionContext>
@@ -326,108 +293,29 @@ export default function (pi: Pi.ExtensionAPI) {
326
293
  formatQueuedStatus: Queue.formatQueuedTelegramItemsStatus,
327
294
  getRecentRuntimeEvents: runtimeEvents.getEvents,
328
295
  getRuntimeLockState: lockRuntime.getStatusLabel,
329
- getBusRole() {
330
- if (threadStore.getBotState().threadMode === "disabled") return undefined;
331
- if (pollingStartedWithTelegramBus) return "leader";
332
- if (telegramBusFollowerRegistrationState.isRegistered())
333
- return "follower";
334
- return undefined;
335
- },
296
+ ...threadStatusProjectionRuntime,
336
297
  getBusLifecyclePhase() {
337
298
  return telegramBusLifecycleOverridePhase;
338
299
  },
339
300
  getBotThreadMode() {
340
301
  return threadStore.getBotState();
341
302
  },
342
- getBusFollowers() {
343
- return Threads.listTelegramThreadStatusFollowers({
344
- followers: telegramBusFollowerRegistry.list(),
345
- records: threadStore.list(),
346
- });
347
- },
348
- getLocalBus() {
349
- return {
350
- leaderSocketPath: getTelegramBusSocketPath(),
351
- leaderTransport: BusTransport.getTelegramBusTransportKind(
352
- getTelegramBusSocketPath(),
353
- ),
354
- followerSocketPath: getTelegramBusFollowerSocketPath(),
355
- followerTransport: BusTransport.getTelegramBusTransportKind(
356
- getTelegramBusFollowerSocketPath(),
357
- ),
358
- followerRegistered: telegramBusFollowerRegistrationState.isRegistered(),
359
- followerTarget: telegramBusFollowerRegistrationState.getTarget(),
360
- followerSlot: telegramBusFollowerRegistrationState.getSlot(),
361
- followerThreadName: telegramBusFollowerRegistrationState.getThreadName(),
362
- };
363
- },
364
- getTopicTargets() {
365
- return Threads.listTelegramThreadStatusTargets(threadStore.list());
366
- },
367
- getThreadReservations() {
368
- return Threads.listTelegramThreadStatusReservations(
369
- threadStore.listReservations(),
370
- );
371
- },
372
- getTopicSyncObservations() {
373
- return Threads.listTelegramThreadStatusObservations(
374
- threadStore.listSyncObservations(),
375
- );
376
- },
377
- getSyncState() {
378
- return telegramSyncState;
379
- },
303
+ getSyncState: telegramSyncStateRuntime.getState,
380
304
  getThreadReconciliationState() {
381
305
  return threadReconciliationRuntime.getState();
382
306
  },
383
- getInstanceSlot() {
384
- if (threadStore.getBotState().threadMode === "disabled") return undefined;
385
- return getCurrentInstanceThreadIdentity().slot;
386
- },
387
- getInstanceThreadName() {
388
- if (threadStore.getBotState().threadMode === "disabled") return undefined;
389
- return getCurrentInstanceThreadIdentity().threadName;
307
+ });
308
+ runtimeDiagnostics.bindStatus({
309
+ instanceId: telegramInstanceId,
310
+ updateStatus: statusRuntime.updateStatus,
311
+ getStatusState: statusRuntime.getStatusState,
312
+ async persistSnapshot(snapshot) {
313
+ threadStore.setStatusSnapshot(snapshot);
314
+ await threadStore.persist();
390
315
  },
391
316
  });
392
- const { updateStatus: updateStatusLine } = statusRuntime;
393
- const updateRuntimeLogScope = function (reason: string) {
394
- const scope = Status.createTelegramRuntimeLogScope({
395
- state: statusRuntime.getStatusState(),
396
- instanceId: telegramInstanceId,
397
- });
398
- const scopeKey = JSON.stringify(scope);
399
- runtimeJsonlLog.resetIfScopeChanged(scopeKey, reason, scope);
400
- };
401
- const updateStatus = function (ctx: Pi.ExtensionContext) {
402
- updateStatusLine(ctx);
403
- updateRuntimeLogScope("status-scope-change");
404
- };
405
- const persistCurrentStatusSnapshot = function () {
406
- threadStore.setStatusSnapshot(
407
- Status.createTelegramStatusSnapshot(statusRuntime.getStatusState()),
408
- );
409
- return threadStore.persist();
410
- };
411
- scheduleRuntimeDiagnosticsSnapshotPersist =
412
- Status.createTelegramRuntimeDiagnosticsSnapshotScheduler({
413
- persistSnapshot: persistCurrentStatusSnapshot,
414
- recordError(error) {
415
- runtimeEvents.record("telegram", error, {
416
- phase: "runtime-diagnostics-snapshot-persist",
417
- });
418
- },
419
- });
420
- const getStatusLines = function (
421
- options?: Status.TelegramBridgeStatusLineOptions,
422
- ): string[] {
423
- const state = statusRuntime.getStatusState();
424
- void persistCurrentStatusSnapshot().catch(function (error) {
425
- recordRuntimeEvent("telegram", error, {
426
- phase: "status-snapshot-persist",
427
- });
428
- });
429
- return Status.buildTelegramBridgeStatusLines(state, options);
430
- };
317
+ const updateStatus = runtimeDiagnostics.updateStatus;
318
+ const getStatusLines = runtimeDiagnostics.getStatusLines;
431
319
  const inboundHandlerRuntime = Inbound.createTelegramInboundHandlerRuntime({
432
320
  getHandlers: configStore.getInboundHandlers,
433
321
  execCommand: CommandTemplates.execCommandTemplate,
@@ -442,29 +330,32 @@ export default function (pi: Pi.ExtensionAPI) {
442
330
  getBotToken: configStore.getBotToken,
443
331
  recordRuntimeEvent,
444
332
  });
333
+ const telegramBusFollowerClients =
334
+ BusFollower.createTelegramBusFollowerClientRuntime<
335
+ Pi.ExtensionContext,
336
+ Updates.TelegramMessageReactionUpdated,
337
+ Routing.TelegramRoutedCallbackQuery,
338
+ Routing.TelegramRoutedMessage
339
+ >({
340
+ socketPath: getTelegramBusSocketPath,
341
+ instanceId: telegramInstanceId,
342
+ getApiAuthSecret() {
343
+ return telegramActiveBusAuthSecret;
344
+ },
345
+ getForwardingAuthSecret() {
346
+ return telegramBusAuthSecret;
347
+ },
348
+ getRegistrationGeneration:
349
+ telegramBusFollowerRegistrationState.getGeneration,
350
+ timeoutMs: 30_000,
351
+ });
445
352
  const telegramApiRuntime = BusApi.createTelegramBusAwareApiRuntime({
446
353
  directRuntime: directTelegramApiRuntime,
447
354
  ownsDirect() {
448
- return (
449
- lockRuntime.owns() ||
450
- !telegramBusFollowerRegistrationState.isRegistered()
451
- );
355
+ return lockRuntime.owns();
452
356
  },
453
357
  getDefaultTarget: proactivePushTargetGetter,
454
- callFollowerApi: BusFollower.createTelegramBusFollowerApiCaller({
455
- socketPath: getTelegramBusSocketPath,
456
- instanceId: telegramInstanceId,
457
- createRequestId() {
458
- telegramBusRequestSequence += 1;
459
- return Bus.createTelegramBusRequestId({
460
- instanceId: telegramInstanceId,
461
- sequence: telegramBusRequestSequence,
462
- });
463
- },
464
- getAuthSecret() {
465
- return telegramActiveBusAuthSecret;
466
- },
467
- }),
358
+ callFollowerApi: telegramBusFollowerClients.callApi,
468
359
  });
469
360
  const {
470
361
  call: callTelegramApi,
@@ -518,14 +409,7 @@ export default function (pi: Pi.ExtensionAPI) {
518
409
  // --- Reply Runtime & Preview ---
519
410
 
520
411
  const replyRuntime = Replies.createTelegramRenderedMessageDeliveryRuntime({
521
- recordOwnership(input) {
522
- messageOwnershipStore.record({
523
- chatId: input.chatId,
524
- messageId: input.messageId,
525
- target: input.target,
526
- instanceId: telegramInstanceId,
527
- });
528
- },
412
+ recordOwnership: messageOwnershipRuntime.recordLocal,
529
413
  sendMessage,
530
414
  sendRichMessage,
531
415
  getAssistantRenderingMode: configControls.getAssistantRenderingMode,
@@ -533,37 +417,27 @@ export default function (pi: Pi.ExtensionAPI) {
533
417
  });
534
418
  const { replyTransport, editInteractiveMessage, sendInteractiveMessage } =
535
419
  replyRuntime;
536
- const getDeliveryTargetPolicyView = function (): Delivery.TelegramDeliveryTargetPolicyView {
537
- const ownsDirect = lockRuntime.owns();
538
- const followerTarget = telegramBusFollowerRegistrationState.getTarget();
539
- return {
540
- canDeliver:
541
- ownsDirect || telegramBusFollowerRegistrationState.isRegistered(),
542
- ownsDirect,
543
- allowedChatId: configStore.getAllowedUserId(),
544
- followerTarget,
545
- leaderTarget: telegramBusLeaderTarget,
546
- liveTargets: threadStore.list().map(function (record) {
547
- return record.target;
548
- }),
549
- };
550
- };
420
+ const deliveryTargetPolicyRuntime =
421
+ Delivery.createTelegramDeliveryTargetPolicyRuntime({
422
+ ownsDirect: lockRuntime.owns,
423
+ isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
424
+ getAllowedChatId: configStore.getAllowedUserId,
425
+ getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
426
+ getLeaderTarget: telegramBusLeaderState.getTarget,
427
+ listThreadRecords: threadStore.list,
428
+ getActiveTurnTarget: activeTurnRuntime.getTarget,
429
+ getActiveGuestQueryId: activeTurnRuntime.getGuestQueryId,
430
+ });
551
431
  const deliveryGenerationSeed = `${telegramInstanceId}:${Date.now()}`;
552
432
  const deliveryLifecycleRuntime =
553
433
  Delivery.createTelegramBridgeDeliveryLifecycleHooks({
554
434
  generationSeed: deliveryGenerationSeed,
555
- getTargetPolicyView: getDeliveryTargetPolicyView,
556
- getActiveTurnTarget() {
557
- if (activeTurnRuntime.getGuestQueryId()) return undefined;
558
- return activeTurnRuntime.getTarget();
559
- },
435
+ getTargetPolicyView: deliveryTargetPolicyRuntime.getTargetPolicyView,
436
+ getTransportStamp: telegramTransportStampRuntime.getStamp,
437
+ isTransportStampActive: telegramTransportStampRuntime.isActive,
438
+ getActiveTurnTarget: deliveryTargetPolicyRuntime.getActiveTurnTarget,
560
439
  api: telegramApiRuntime,
561
- recordOwnership(input) {
562
- messageOwnershipStore.record({
563
- ...input,
564
- instanceId: telegramInstanceId,
565
- });
566
- },
440
+ recordOwnership: messageOwnershipRuntime.recordLocal,
567
441
  recordFailure(operation, error, target) {
568
442
  recordRuntimeEvent("delivery", error, {
569
443
  operation,
@@ -598,6 +472,9 @@ export default function (pi: Pi.ExtensionAPI) {
598
472
  isIdle,
599
473
  hasPendingMessages,
600
474
  hasDispatchContext: deferredQueueDispatchRuntime.isBound,
475
+ isQueueItemTransportActive(item) {
476
+ return telegramTransportStampRuntime.isActive(item.transportStamp);
477
+ },
601
478
  updateStatus,
602
479
  sendTextReply,
603
480
  recordRuntimeEvent,
@@ -723,111 +600,55 @@ export default function (pi: Pi.ExtensionAPI) {
723
600
  // --- Polling ---
724
601
 
725
602
  const foreignOwnedUpdateForwarder =
726
- Bus.createTelegramBusForeignOwnedUpdateForwarder<
727
- Pi.ExtensionContext,
728
- Updates.TelegramMessageReactionUpdated,
729
- Routing.TelegramRoutedCallbackQuery,
730
- Routing.TelegramRoutedMessage
731
- >({
732
- socketPath: getTelegramBusSocketPath,
733
- createRequestId() {
734
- telegramBusRequestSequence += 1;
735
- return Bus.createTelegramBusRequestId({
736
- instanceId: telegramInstanceId,
737
- sequence: telegramBusRequestSequence,
738
- });
739
- },
740
- getAuthSecret() {
741
- return telegramBusAuthSecret;
742
- },
743
- timeoutMs: 30_000,
744
- });
745
- const followerTargetController =
746
- Bus.createTelegramBusFollowerTargetController({
747
- socketPath: getTelegramBusSocketPath,
748
- createRequestId() {
749
- telegramBusRequestSequence += 1;
750
- return Bus.createTelegramBusRequestId({
751
- instanceId: telegramInstanceId,
752
- sequence: telegramBusRequestSequence,
753
- });
754
- },
755
- getAuthSecret() {
756
- return telegramBusAuthSecret;
757
- },
758
- timeoutMs: 30_000,
759
- });
603
+ telegramBusFollowerClients.foreignOwnedUpdateForwarder;
604
+ const followerTargetController = telegramBusFollowerClients.targetController;
760
605
  const restoreFollowerThreadTarget =
761
606
  Bus.createTelegramBusFollowerThreadRestoreHandler({
762
607
  followerRegistry: telegramBusFollowerRegistry,
763
608
  followerTargetController,
764
609
  onRestored() {
765
- telegramSyncState = Sync.markTelegramSyncSliceFresh(
766
- telegramSyncState,
767
- "target-bindings",
768
- { nowMs: Date.now(), action: "follower-thread-restore" },
769
- );
610
+ telegramSyncStateRuntime.markSliceFresh("target-bindings", {
611
+ nowMs: Date.now(),
612
+ action: "follower-thread-restore",
613
+ });
770
614
  },
771
615
  });
772
- let observeTelegramThreadTarget: Polling.TelegramThreadTargetObservationHandler<
773
- Pi.ExtensionContext
774
- > = async function noopTelegramThreadTargetObservation() {};
775
- const topicLifecycleSync = Sync.createTelegramObservedTopicLifecycleSyncHandler({
776
- topicTargetStore: threadStore,
777
- isBusEnabled: isTelegramBusRuntimeEnabled,
778
- callApi: callTelegramApi,
779
- isTopicProvisioningActive() {
780
- return telegramTopicProvisioningCount > 0;
781
- },
782
- getCurrentLeaderEpoch,
783
- getThreadReconciliationMachineState() {
784
- return threadReconciliationRuntime.getState();
785
- },
786
- recordThreadReconciliationPlan,
787
- getSyncState() {
788
- return telegramSyncState;
789
- },
790
- setSyncState(state) {
791
- telegramSyncState = state;
792
- },
793
- recordEvent: recordRuntimeEvent,
794
- });
616
+ const observedThreadTargetBinding =
617
+ Polling.createTelegramThreadTargetObservationBinding<Pi.ExtensionContext>();
618
+ const topicLifecycleSync =
619
+ Sync.createTelegramObservedTopicLifecycleSyncHandler({
620
+ topicTargetStore: threadStore,
621
+ isBusEnabled: isTelegramBusRuntimeEnabled,
622
+ callApi: callTelegramApi,
623
+ isTopicProvisioningActive: telegramProvisioningActivity.isActive,
624
+ getCurrentLeaderEpoch,
625
+ getThreadReconciliationMachineState() {
626
+ return threadReconciliationRuntime.getState();
627
+ },
628
+ recordThreadReconciliationPlan,
629
+ getSyncState: telegramSyncStateRuntime.getState,
630
+ setSyncState: telegramSyncStateRuntime.setState,
631
+ recordEvent: recordRuntimeEvent,
632
+ });
633
+ const inboundBusProjectionRuntime =
634
+ Routing.createTelegramInboundBusProjectionRuntime({
635
+ instanceId: telegramInstanceId,
636
+ listFollowers: telegramBusFollowerRegistry.list,
637
+ listThreadRecords: threadStore.list,
638
+ getLeaderTarget: telegramBusLeaderState.getTarget,
639
+ isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
640
+ getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
641
+ getCurrentIdentity: getCurrentInstanceThreadIdentity,
642
+ });
795
643
  const inboundRouteRuntime = Routing.createTelegramInboundRouteRuntime({
796
644
  configStore,
797
645
  callApi: callTelegramApi,
798
646
  getCurrentInstanceId() {
799
647
  return telegramInstanceId;
800
648
  },
801
- getMessageOwnership: messageOwnershipStore.get,
802
- recordMessageOwnership(input) {
803
- messageOwnershipStore.record(input);
804
- },
805
- getTargetOwnership(target) {
806
- return Bus.getTelegramFollowerTargetOwnership({
807
- target,
808
- followers: telegramBusFollowerRegistry.list(),
809
- activeThreadRecords: threadStore.list(),
810
- currentInstanceId: telegramInstanceId,
811
- });
812
- },
813
- getLiveThreadTargets() {
814
- return Bus.listTelegramBusLiveThreadTargets({
815
- leaderTarget: telegramBusLeaderTarget,
816
- followers: telegramBusFollowerRegistry.list(),
817
- });
818
- },
819
- getLocalThreadLabelForTarget(target) {
820
- const followerTarget = telegramBusFollowerRegistrationState.getTarget();
821
- const isLocalFollowerTarget =
822
- telegramBusFollowerRegistrationState.isRegistered() &&
823
- followerTarget?.chatId === target.chatId &&
824
- followerTarget.threadId === target.threadId;
825
- const isLocalLeaderTarget =
826
- telegramBusLeaderTarget?.chatId === target.chatId &&
827
- telegramBusLeaderTarget.threadId === target.threadId;
828
- if (!isLocalFollowerTarget && !isLocalLeaderTarget) return undefined;
829
- return getCurrentInstanceThreadIdentity(target).threadName;
830
- },
649
+ getMessageOwnership: messageOwnershipRuntime.store.get,
650
+ recordMessageOwnership: messageOwnershipRuntime.recordRouted,
651
+ ...inboundBusProjectionRuntime,
831
652
  getCurrentLeaderEpoch,
832
653
  getThreadReconciliationMachineState() {
833
654
  return threadReconciliationRuntime.getState();
@@ -835,7 +656,7 @@ export default function (pi: Pi.ExtensionAPI) {
835
656
  recordThreadReconciliationPlan,
836
657
  handleTelegramTopicLifecycleUpdate: topicLifecycleSync,
837
658
  handleTelegramThreadTargetObserved(_target, ctx) {
838
- return observeTelegramThreadTarget(ctx);
659
+ return observedThreadTargetBinding.handle(ctx);
839
660
  },
840
661
  foreignOwnedUpdateForwarder,
841
662
  replaceFollowerThreadTarget: restoreFollowerThreadTarget,
@@ -893,11 +714,26 @@ export default function (pi: Pi.ExtensionAPI) {
893
714
  topicTargetStore: threadStore,
894
715
  instanceId: telegramInstanceId,
895
716
  getActiveProfileName: getActiveTelegramThreadProfile,
896
- async startLeader(ctx): Promise<void> {
897
- await lockedPollingRuntime.start(ctx, { force: true });
717
+ async startLeader(ctx, election, onAcquired): Promise<boolean> {
718
+ const result = await lockedPollingRuntime.start(ctx, {
719
+ election,
720
+ onAcquired,
721
+ });
722
+ return result.ok;
898
723
  },
899
724
  recordRuntimeEvent,
900
725
  });
726
+ const forwardedRouteHandlers =
727
+ BusFollower.createTelegramBusForwardedRouteHandlers<
728
+ Pi.ExtensionContext,
729
+ Updates.TelegramMessageReactionUpdated,
730
+ Routing.TelegramRoutedCallbackQuery,
731
+ Routing.TelegramRoutedMessage
732
+ >({
733
+ handleUpdate: inboundRouteRuntime.handleUpdate,
734
+ handleAuthorizedReactionUpdate:
735
+ inboundRouteRuntime.handleAuthorizedReactionUpdate,
736
+ });
901
737
  const telegramBusFollowerAssembly: BusFollower.TelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext> =
902
738
  BusFollower.createTelegramBusFollowerRuntimeAssembly<
903
739
  Pi.ExtensionContext,
@@ -914,27 +750,7 @@ export default function (pi: Pi.ExtensionAPI) {
914
750
  getAuthSecret() {
915
751
  return telegramActiveBusAuthSecret;
916
752
  },
917
- handleForwardedCallback(query, ctx) {
918
- return inboundRouteRuntime.handleUpdate({ callback_query: query }, ctx);
919
- },
920
- handleForwardedReaction(reactionUpdate, ctx) {
921
- return inboundRouteRuntime.handleAuthorizedReactionUpdate(
922
- reactionUpdate,
923
- ctx,
924
- );
925
- },
926
- handleForwardedMessage(message, ctx) {
927
- return inboundRouteRuntime.handleUpdate(
928
- { message: message as never },
929
- ctx,
930
- );
931
- },
932
- async handleForwardedEditedMessage(message, ctx) {
933
- return inboundRouteRuntime.handleUpdate(
934
- { edited_message: message as never },
935
- ctx,
936
- );
937
- },
753
+ ...forwardedRouteHandlers,
938
754
  recordRuntimeEvent,
939
755
  },
940
756
  targetReplacement: {
@@ -943,12 +759,8 @@ export default function (pi: Pi.ExtensionAPI) {
943
759
  instanceId: telegramInstanceId,
944
760
  getManualFollowerProfileKey: getTelegramManualFollowerProfileKey,
945
761
  manualFollowerOwnerId: telegramManualFollowerOwnerId,
946
- getSyncState() {
947
- return telegramSyncState;
948
- },
949
- setSyncState(state) {
950
- telegramSyncState = state;
951
- },
762
+ getSyncState: telegramSyncStateRuntime.getState,
763
+ setSyncState: telegramSyncStateRuntime.setState,
952
764
  updateStatus,
953
765
  recordRuntimeEvent,
954
766
  },
@@ -962,10 +774,9 @@ export default function (pi: Pi.ExtensionAPI) {
962
774
  },
963
775
  updateStatus,
964
776
  promoteToLeader: promoteTelegramBusFollowerToLeader,
965
- sleep(ms) {
966
- return Polling.sleepTelegramPollingRetry(ms);
777
+ getActiveContext() {
778
+ return telegramSessionContextStore.get();
967
779
  },
968
- promotionGraceMs: BusFollower.TELEGRAM_BUS_FOLLOWER_PROMOTION_GRACE_MS,
969
780
  recordRuntimeEvent,
970
781
  },
971
782
  registration: {
@@ -973,13 +784,10 @@ export default function (pi: Pi.ExtensionAPI) {
973
784
  getFollowerBusSocketPath: getTelegramBusFollowerSocketPath,
974
785
  getLeaderSocketPath: getTelegramBusSocketPath,
975
786
  registrationState: telegramBusFollowerRegistrationState,
976
- createRequestId() {
977
- telegramBusRequestSequence += 1;
978
- return Bus.createTelegramBusRequestId({
979
- instanceId: telegramInstanceId,
980
- sequence: telegramBusRequestSequence,
981
- });
787
+ isContextActive(ctx) {
788
+ return telegramSessionContextStore.isCurrent(ctx);
982
789
  },
790
+ createRequestId: telegramBusFollowerClients.createRequestId,
983
791
  getLeaderAuthSecret(owner) {
984
792
  return owner.busSecret;
985
793
  },
@@ -1017,31 +825,28 @@ export default function (pi: Pi.ExtensionAPI) {
1017
825
  ) {
1018
826
  return Sync.recoverStaleTelegramTopicApiError(apiBody, error, {
1019
827
  topicTargetStore: threadStore,
1020
- getSyncState() {
1021
- return telegramSyncState;
1022
- },
1023
- setSyncState(state) {
1024
- telegramSyncState = state;
1025
- },
828
+ getSyncState: telegramSyncStateRuntime.getState,
829
+ setSyncState: telegramSyncStateRuntime.setState,
1026
830
  recordEvent: recordRuntimeEvent,
1027
831
  });
1028
832
  };
833
+ const authorizeFollowerApiCall = Bus.createTelegramFollowerApiCallAuthorizer({
834
+ isMessageOwned: messageOwnershipRuntime.isOwnedByFollower,
835
+ });
1029
836
  const telegramBusLeaderRuntime =
1030
837
  BusLeader.createTelegramBusLeaderRuntimeAssembly<Pi.ExtensionContext>({
1031
838
  runtime: {
1032
839
  socketPath: getTelegramBusSocketPath,
840
+ commitEndpointPublication(commit) {
841
+ return lockRuntime.commitIfOwned(commit);
842
+ },
1033
843
  followerRegistry: telegramBusFollowerRegistry,
1034
844
  authSecret: telegramBusAuthSecret,
1035
845
  startPolling: pollingRuntime.start,
1036
846
  stopPolling: pollingRuntime.stop,
1037
- authorizeFollowerApiCall: Bus.isTelegramFollowerApiCallAllowed,
847
+ authorizeFollowerApiCall,
1038
848
  recordFollowerMessageOwnership(record) {
1039
- messageOwnershipStore.record({
1040
- chatId: record.chatId,
1041
- messageId: record.messageId,
1042
- target: record.target,
1043
- instanceId: record.follower.instanceId,
1044
- });
849
+ messageOwnershipRuntime.recordFollower(record);
1045
850
  },
1046
851
  },
1047
852
  getAllowedUserId: configStore.getAllowedUserId,
@@ -1050,9 +855,8 @@ export default function (pi: Pi.ExtensionAPI) {
1050
855
  return typeof ctx.cwd === "string" ? ctx.cwd : undefined;
1051
856
  },
1052
857
  getTelegramProfile: getActiveTelegramThreadProfile,
1053
- shouldForceFreshUnnamed() {
1054
- return forceFreshLeaderThreadOnNextStart;
1055
- },
858
+ shouldForceFreshUnnamed:
859
+ telegramThreadCapabilityState.shouldForceFreshLeaderThread,
1056
860
  topicTargetStore: threadStore,
1057
861
  callApi(method, body) {
1058
862
  return directTelegramApiRuntime.call(method, body);
@@ -1065,158 +869,52 @@ export default function (pi: Pi.ExtensionAPI) {
1065
869
  return threadReconciliationRuntime.getState();
1066
870
  },
1067
871
  recordThreadReconciliationPlan,
1068
- getSyncState() {
1069
- return telegramSyncState;
1070
- },
1071
- setSyncState(state) {
1072
- telegramSyncState = state;
1073
- },
1074
- setLeaderTarget(input) {
1075
- telegramBusLeaderTarget = input.target;
1076
- telegramBusLeaderSlot = input.slot;
1077
- telegramBusLeaderThreadName = input.threadName;
1078
- },
1079
- onProvisioningStart() {
1080
- telegramTopicProvisioningCount += 1;
1081
- },
1082
- onProvisioningEnd() {
1083
- telegramTopicProvisioningCount = Math.max(
1084
- 0,
1085
- telegramTopicProvisioningCount - 1,
1086
- );
1087
- },
872
+ getSyncState: telegramSyncStateRuntime.getState,
873
+ setSyncState: telegramSyncStateRuntime.setState,
874
+ setLeaderTarget: telegramBusLeaderState.set,
875
+ onProvisioningStart: telegramProvisioningActivity.start,
876
+ onProvisioningEnd: telegramProvisioningActivity.end,
1088
877
  recordRuntimeEvent,
1089
878
  });
1090
- let pollingStartedWithTelegramBus = false;
1091
- let forceFreshLeaderThreadOnNextStart = false;
1092
879
  const telegramLeaderHealthRuntime = Sync.createTelegramLeaderHealthRuntime({
1093
880
  callGetMe() {
1094
881
  return directTelegramApiRuntime.call("getMe", {});
1095
882
  },
1096
- getSyncState() {
1097
- return telegramSyncState;
1098
- },
1099
- setSyncState(state) {
1100
- telegramSyncState = state;
1101
- },
883
+ getSyncState: telegramSyncStateRuntime.getState,
884
+ setSyncState: telegramSyncStateRuntime.setState,
1102
885
  recordEvent: recordRuntimeEvent,
1103
886
  });
1104
- const telegramThreadCapabilityMonitor =
1105
- Polling.createTelegramThreadCapabilityMonitor<Pi.ExtensionContext>({
1106
- getAllowedUserId: configStore.getAllowedUserId,
1107
- callApi: callTelegramApi,
1108
- topicTargetStore: threadStore,
1109
- isBusConfigured: isTelegramBusConfigured,
1110
- ownsLock(ctx) {
1111
- return lockRuntime.owns(ctx);
1112
- },
1113
- getPollingStartedWithTelegramBus() {
1114
- return pollingStartedWithTelegramBus;
1115
- },
1116
- setPollingStartedWithTelegramBus(started) {
1117
- pollingStartedWithTelegramBus = started;
1118
- },
1119
- setTopicModeUnavailable(unavailable) {
1120
- telegramTopicModeUnavailable = unavailable;
1121
- },
1122
- stopFollowerRegistration: telegramBusFollowerRegistration.stop,
1123
- startClassicPolling(ctx) {
1124
- return pollingRuntime.start(ctx);
1125
- },
1126
- stopClassicPolling() {
1127
- return pollingRuntime.stop();
1128
- },
1129
- startBusPolling(ctx) {
1130
- return telegramBusLeaderRuntime.startPolling(ctx);
1131
- },
1132
- stopBusPolling() {
1133
- return telegramBusLeaderRuntime.stopPolling();
1134
- },
1135
- startLeaderHealth: telegramLeaderHealthRuntime.start,
1136
- stopLeaderHealth: telegramLeaderHealthRuntime.stop,
1137
- isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
1138
- updateStatus,
1139
- recordEvent: recordRuntimeEvent,
1140
- });
1141
- observeTelegramThreadTarget = Polling.createTelegramThreadTargetObservationHandler({
1142
- getAllowedUserId: configStore.getAllowedUserId,
1143
- callApi: callTelegramApi,
1144
- topicTargetStore: threadStore,
1145
- isBusConfigured: isTelegramBusConfigured,
1146
- ownsLock(ctx) {
1147
- return lockRuntime.owns(ctx);
1148
- },
1149
- getPollingStartedWithTelegramBus() {
1150
- return pollingStartedWithTelegramBus;
1151
- },
1152
- setPollingStartedWithTelegramBus(started) {
1153
- pollingStartedWithTelegramBus = started;
1154
- },
1155
- setTopicModeUnavailable(unavailable) {
1156
- telegramTopicModeUnavailable = unavailable;
1157
- },
1158
- stopFollowerRegistration: telegramBusFollowerRegistration.stop,
1159
- startClassicPolling(ctx) {
1160
- return pollingRuntime.start(ctx);
1161
- },
1162
- stopClassicPolling() {
1163
- return pollingRuntime.stop();
1164
- },
1165
- startBusPolling(ctx) {
1166
- return telegramBusLeaderRuntime.startPolling(ctx);
1167
- },
1168
- stopBusPolling() {
1169
- return telegramBusLeaderRuntime.stopPolling();
1170
- },
1171
- startLeaderHealth: telegramLeaderHealthRuntime.start,
1172
- stopLeaderHealth: telegramLeaderHealthRuntime.stop,
1173
- isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
1174
- updateStatus,
1175
- recordEvent: recordRuntimeEvent,
1176
- });
1177
- const threadAwarePollingPorts =
1178
- Polling.createTelegramThreadAwarePollingPorts<
887
+ const telegramThreadCapabilityRuntime =
888
+ Polling.createTelegramThreadCapabilityOrchestration<
1179
889
  Pi.ExtensionContext,
1180
890
  Locks.TelegramLockEntry
1181
891
  >({
892
+ state: telegramThreadCapabilityState,
1182
893
  getAllowedUserId: configStore.getAllowedUserId,
1183
894
  callApi: callTelegramApi,
1184
895
  topicTargetStore: threadStore,
1185
896
  isBusConfigured: isTelegramBusConfigured,
1186
897
  isBusRuntimeEnabled: isTelegramBusRuntimeEnabled,
1187
- isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
1188
- getPollingStartedWithTelegramBus() {
1189
- return pollingStartedWithTelegramBus;
1190
- },
1191
- setPollingStartedWithTelegramBus(started) {
1192
- pollingStartedWithTelegramBus = started;
1193
- },
1194
- setForceFreshLeaderThreadOnNextStart(forceFresh) {
1195
- forceFreshLeaderThreadOnNextStart = forceFresh;
1196
- },
1197
- setTopicModeUnavailable(unavailable) {
1198
- telegramTopicModeUnavailable = unavailable;
1199
- },
1200
- startClassicPolling(ctx) {
1201
- return pollingRuntime.start(ctx);
1202
- },
1203
- stopClassicPolling() {
1204
- return pollingRuntime.stop();
1205
- },
1206
- startBusLeaderPolling(ctx) {
1207
- return telegramBusLeaderRuntime.startPolling(ctx);
1208
- },
1209
- stopBusLeaderPolling() {
1210
- return telegramBusLeaderRuntime.stopPolling();
1211
- },
898
+ ownsLock: lockRuntime.owns,
899
+ startClassicPolling: pollingRuntime.start,
900
+ stopClassicPolling: pollingRuntime.stop,
901
+ startBusLeaderPolling: telegramBusLeaderRuntime.startPolling,
902
+ stopBusLeaderPolling: telegramBusLeaderRuntime.stopPolling,
1212
903
  startLeaderHealth: telegramLeaderHealthRuntime.start,
1213
904
  stopLeaderHealth: telegramLeaderHealthRuntime.stop,
1214
- registerFollowerWithLeader(ctx, owner) {
1215
- return telegramBusFollowerRegistration.registerWithLeader(ctx, owner);
1216
- },
905
+ registerFollowerWithLeader:
906
+ telegramBusFollowerRegistration.registerWithLeader,
1217
907
  stopFollowerRegistration: telegramBusFollowerRegistration.stop,
908
+ isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
909
+ updateStatus,
1218
910
  recordEvent: recordRuntimeEvent,
1219
911
  });
912
+ const telegramThreadCapabilityMonitor =
913
+ telegramThreadCapabilityRuntime.monitor;
914
+ observedThreadTargetBinding.set(
915
+ telegramThreadCapabilityRuntime.observeTarget,
916
+ );
917
+ const threadAwarePollingPorts = telegramThreadCapabilityRuntime.pollingPorts;
1220
918
  const lockedPollingRuntime = Locks.createTelegramLockedPollingRuntime({
1221
919
  lock: lockRuntime,
1222
920
  hasBotToken: configStore.hasBotToken,
@@ -1224,7 +922,8 @@ export default function (pi: Pi.ExtensionAPI) {
1224
922
  formatStartBlockedMessage: Pi.formatPollingStartBlockedByRunMode,
1225
923
  startPolling: threadAwarePollingPorts.startPolling,
1226
924
  stopPolling: threadAwarePollingPorts.stopPolling,
1227
- registerFollowerWithOwner: threadAwarePollingPorts.registerFollowerWithOwner,
925
+ registerFollowerWithOwner:
926
+ threadAwarePollingPorts.registerFollowerWithOwner,
1228
927
  stopFollowerRegistration: threadAwarePollingPorts.stopFollowerRegistration,
1229
928
  updateStatus,
1230
929
  recordRuntimeEvent,
@@ -1235,89 +934,61 @@ export default function (pi: Pi.ExtensionAPI) {
1235
934
  getCurrentThreadRecord: findCurrentThreadRecord,
1236
935
  topicTargetStore: threadStore,
1237
936
  callApi: callTelegramApi,
1238
- getLeaderTarget() {
1239
- return telegramBusLeaderTarget;
1240
- },
1241
- clearLeaderTarget() {
1242
- telegramBusLeaderTarget = undefined;
1243
- telegramBusLeaderSlot = undefined;
1244
- telegramBusLeaderThreadName = undefined;
1245
- },
1246
- getSyncState() {
1247
- return telegramSyncState;
1248
- },
1249
- setSyncState(state) {
1250
- telegramSyncState = state;
1251
- },
937
+ getCurrentLeaderEpoch,
938
+ getLeaderTarget: telegramBusLeaderState.getTarget,
939
+ clearLeaderTarget: telegramBusLeaderState.clear,
940
+ getSyncState: telegramSyncStateRuntime.getState,
941
+ setSyncState: telegramSyncStateRuntime.setState,
1252
942
  stopPolling: lockedPollingRuntime.stop,
1253
943
  recordRuntimeEvent,
1254
944
  });
1255
- const suspendTelegramForSessionReplacement =
1256
- BusFollower.createTelegramBusFollowerSessionReplacementSuspender({
1257
- registrationState: telegramBusFollowerRegistrationState,
1258
- instanceId: telegramInstanceId,
1259
- suspendPolling: lockedPollingRuntime.suspend,
1260
- recordRuntimeEvent,
1261
- });
1262
- const queueSessionLifecycle = Queue.createTelegramSessionLifecycleRuntime({
1263
- getCurrentModel: getContextModel,
1264
- loadConfig: configStore.load,
1265
- setQueuedItems: telegramQueueStore.setQueuedItems,
1266
- setCurrentModel: currentModelRuntime.set,
1267
- setPendingModelSwitch: pendingModelSwitchStore.set,
1268
- syncCounters: queue.syncCounters,
1269
- syncFlags: lifecycle.syncFlags,
1270
- bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
1271
- prepareTempDir,
1272
- updateStatus,
1273
- unbindDeferredDispatchContext: deferredQueueDispatchRuntime.unbind,
1274
- clearPendingMediaGroups: TextGroups.createTelegramGroupedInputClearer({
1275
- clearMediaGroups: mediaGroupRuntime.clear,
1276
- clearTextGroups: textGroupRuntime.clear,
1277
- }),
1278
- clearModelMenuState: modelMenuRuntime.clear,
1279
- getActiveTurnChatId: activeTurnRuntime.getChatId,
1280
- getActiveTurnTarget: activeTurnRuntime.getTarget,
1281
- clearPreview: previewRuntime.clear,
1282
- clearActiveTurn: activeTurnRuntime.clear,
1283
- clearAbort: abort.clearHandler,
1284
- stopPolling: suspendTelegramForSessionReplacement,
1285
- recordRuntimeEvent,
1286
- });
1287
- const baseSessionLifecycleRuntime = Lifecycle.appendTelegramLifecycleHooks(
1288
- queueSessionLifecycle,
1289
- {
1290
- async onSessionStart(event, ctx) {
1291
- await deliveryLifecycleRuntime.onSessionStart();
1292
- await lockedPollingRuntime.onSessionStart(event, ctx);
1293
- telegramThreadCapabilityMonitor.start(ctx);
1294
- queueDispatchWatchdogRuntime.start(ctx);
1295
- },
1296
- async onSessionShutdown() {
1297
- await deliveryLifecycleRuntime.onSessionShutdown();
1298
- queueDispatchWatchdogRuntime.stop();
1299
- telegramThreadCapabilityMonitor.stop();
945
+ const sessionLifecycleRuntime =
946
+ Lifecycle.createTelegramBridgeSessionLifecycleAssembly({
947
+ contextStore: telegramSessionContextStore,
948
+ queue: {
949
+ getCurrentModel: getContextModel,
950
+ loadConfig: configStore.load,
951
+ setQueuedItems: telegramQueueStore.setQueuedItems,
952
+ setCurrentModel: currentModelRuntime.set,
953
+ setPendingModelSwitch: pendingModelSwitchStore.set,
954
+ syncCounters: queue.syncCounters,
955
+ syncFlags: lifecycle.syncFlags,
956
+ bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
957
+ prepareTempDir,
958
+ updateStatus,
959
+ unbindDeferredDispatchContext: deferredQueueDispatchRuntime.unbind,
960
+ clearModelMenuState: modelMenuRuntime.clear,
961
+ getActiveTurnChatId: activeTurnRuntime.getChatId,
962
+ getActiveTurnTarget: activeTurnRuntime.getTarget,
963
+ clearPreview: previewRuntime.clear,
964
+ clearActiveTurn: activeTurnRuntime.clear,
965
+ clearAbort: abort.clearHandler,
966
+ recordRuntimeEvent,
1300
967
  },
1301
- },
1302
- );
1303
- const sessionLifecycleWithContext = Lifecycle.appendTelegramLifecycleHooks(
1304
- baseSessionLifecycleRuntime,
1305
- Lifecycle.createTelegramSessionContextTracker(telegramSessionContextStore),
1306
- );
1307
- const sessionLifecycleRuntime = Lifecycle.appendTelegramLifecycleHooks(
1308
- sessionLifecycleWithContext,
1309
- {
1310
- onSessionStart: BusFollower.createTelegramBusFollowerSessionRefreshHook({
968
+ follower: {
1311
969
  registrationState: telegramBusFollowerRegistrationState,
1312
970
  registrationRuntime: telegramBusFollowerRegistration,
1313
- getLeaderState() {
1314
- return lockRuntime.getState();
1315
- },
971
+ instanceId: telegramInstanceId,
972
+ suspendPolling: lockedPollingRuntime.suspend,
973
+ getLeaderState: lockRuntime.getState,
1316
974
  updateStatus,
1317
975
  recordRuntimeEvent,
1318
- }),
1319
- },
1320
- );
976
+ },
977
+ services: {
978
+ resumeGroupedInput(ctx) {
979
+ mediaGroupRuntime.resume(ctx);
980
+ textGroupRuntime.resume(ctx);
981
+ },
982
+ suspendGroupedInput: TextGroups.createTelegramGroupedInputClearer({
983
+ clearMediaGroups: mediaGroupRuntime.suspend,
984
+ clearTextGroups: textGroupRuntime.suspend,
985
+ }),
986
+ delivery: deliveryLifecycleRuntime,
987
+ polling: lockedPollingRuntime,
988
+ capabilityMonitor: telegramThreadCapabilityMonitor,
989
+ queueWatchdog: queueDispatchWatchdogRuntime,
990
+ },
991
+ });
1321
992
 
1322
993
  // --- Extension API Bindings ---
1323
994
 
@@ -1329,6 +1000,7 @@ export default function (pi: Pi.ExtensionAPI) {
1329
1000
  activeTurnRuntime,
1330
1001
  lockedPollingRuntime,
1331
1002
  stopPolling: disconnectTelegramAndDeleteCurrentThread,
1003
+ onTransportChanged: deliveryLifecycleRuntime.onSessionStart,
1332
1004
  getStatusLines,
1333
1005
  buttonActionStore,
1334
1006
  sendMarkdownReply,
@@ -1391,6 +1063,12 @@ export default function (pi: Pi.ExtensionAPI) {
1391
1063
  telegramBusFollowerRegistrationState.isRegistered()
1392
1064
  );
1393
1065
  },
1066
+ isSessionContextActive(ctx) {
1067
+ return telegramSessionContextStore.isCurrent(ctx);
1068
+ },
1069
+ isTurnTransportActive(turn) {
1070
+ return telegramTransportStampRuntime.isActive(turn.transportStamp);
1071
+ },
1394
1072
  updateStatus,
1395
1073
  recordRuntimeEvent,
1396
1074
  });