@llblab/pi-telegram 0.44.0 → 0.45.1

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 (56) hide show
  1. package/AGENTS.md +14 -9
  2. package/BACKLOG.md +23 -5
  3. package/CHANGELOG.md +18 -0
  4. package/README.md +13 -9
  5. package/docs/README.md +1 -0
  6. package/docs/architecture.md +227 -23
  7. package/docs/generative-apps.md +1 -1
  8. package/docs/multi-instance-bus.md +70 -19
  9. package/docs/outbound.md +6 -6
  10. package/docs/public-api.md +13 -6
  11. package/docs/ui-style.md +3 -1
  12. package/index.ts +4 -1418
  13. package/lib/agent-messages.ts +6 -3
  14. package/lib/bindings.ts +46 -1
  15. package/lib/bus-api.ts +32 -19
  16. package/lib/bus-follower.ts +600 -135
  17. package/lib/bus-leader.ts +962 -55
  18. package/lib/bus.ts +355 -26
  19. package/lib/channel-posts.ts +718 -0
  20. package/lib/commands.ts +237 -11
  21. package/lib/config.ts +242 -26
  22. package/lib/extension.ts +1851 -0
  23. package/lib/generative-apps.ts +20 -2
  24. package/lib/journal.ts +2184 -126
  25. package/lib/locks.ts +44 -2
  26. package/lib/menu-settings.ts +152 -13
  27. package/lib/outbound-attachments.ts +97 -10
  28. package/lib/paths.ts +29 -0
  29. package/lib/polling.ts +85 -17
  30. package/lib/preview.ts +17 -0
  31. package/lib/prompts.ts +6 -2
  32. package/lib/queue.ts +118 -26
  33. package/lib/rendering.ts +4 -1
  34. package/lib/replies.ts +21 -2
  35. package/lib/routing.ts +344 -112
  36. package/lib/setup.ts +44 -4
  37. package/lib/status.ts +51 -4
  38. package/lib/sync.ts +308 -39
  39. package/lib/telegram-api.ts +353 -22
  40. package/lib/thread-cleanup-manager.ts +664 -0
  41. package/lib/thread-display.ts +226 -0
  42. package/lib/thread-naming.ts +118 -0
  43. package/lib/threads.ts +1686 -129
  44. package/lib/turns.ts +7 -0
  45. package/lib/updates.ts +1319 -97
  46. package/lib/workspace-admission.ts +1643 -0
  47. package/lib/workspace-retirement.ts +968 -0
  48. package/lib/workspace-slots.ts +84 -0
  49. package/package.json +1 -1
  50. package/screenshot.png +0 -0
  51. package/scripts/measure-bus.mjs +83 -0
  52. package/scripts/measure-workspace.mjs +101 -0
  53. package/skills/show-me/SKILL.md +166 -0
  54. package/skills/show-me/references/telegram-surfaces.md +43 -0
  55. package/skills/telegram-bridge/references/delivery-and-threads.md +1 -1
  56. /package/lib/{logs.ts → logging.ts} +0 -0
package/index.ts CHANGED
@@ -1,1421 +1,7 @@
1
1
  /**
2
- * Telegram bridge extension entrypoint and orchestration layer
3
- * Zones: telegram, pi agent, orchestration
4
- * Keeps the runtime wiring in one place while delegating reusable domain logic to /lib modules
2
+ * Telegram package entrypoint
3
+ * Zones: telegram, pi agent, public API
4
+ * Re-exports the extension composition root without owning runtime wiring.
5
5
  */
6
6
 
7
- import * as AgentMessages from "./lib/agent-messages.ts";
8
- import * as Bindings from "./lib/bindings.ts";
9
- import * as BusApi from "./lib/bus-api.ts";
10
- import * as BusFollower from "./lib/bus-follower.ts";
11
- import * as BusLeader from "./lib/bus-leader.ts";
12
- import * as BusTransport from "./lib/bus-transport.ts";
13
- import * as Bus from "./lib/bus.ts";
14
- import * as CommandTemplates from "./lib/command-templates.ts";
15
- import * as Commands from "./lib/commands.ts";
16
- import * as Config from "./lib/config.ts";
17
- import * as Delivery from "./lib/delivery.ts";
18
- import * as Inbound from "./lib/inbound.ts";
19
- import * as Journal from "./lib/journal.ts";
20
- import * as Lifecycle from "./lib/lifecycle.ts";
21
- import * as Locks from "./lib/locks.ts";
22
- import * as Logs from "./lib/logs.ts";
23
- import * as Media from "./lib/media.ts";
24
- import * as MenuQueue from "./lib/menu-queue.ts";
25
- import * as MenuSettings from "./lib/menu-settings.ts";
26
- import * as Menu from "./lib/menu.ts";
27
- import * as Model from "./lib/model.ts";
28
- import * as Outbound from "./lib/outbound.ts";
29
- import * as Ownership from "./lib/ownership.ts";
30
- import * as Paths from "./lib/paths.ts";
31
- import * as Pi from "./lib/pi.ts";
32
- import * as Polling from "./lib/polling.ts";
33
- import * as Preview from "./lib/preview.ts";
34
- import * as PromptTemplates from "./lib/prompt-templates.ts";
35
- import * as Prompts from "./lib/prompts.ts";
36
- import * as Queue from "./lib/queue.ts";
37
- import * as Recovery from "./lib/recovery.ts";
38
- import * as Replies from "./lib/replies.ts";
39
- import * as Routing from "./lib/routing.ts";
40
- import * as Runtime from "./lib/runtime.ts";
41
- import * as Sections from "./lib/sections.ts";
42
- import * as Skills from "./lib/skills.ts";
43
- import * as Status from "./lib/status.ts";
44
- import * as Sync from "./lib/sync.ts";
45
- import * as TelegramApi from "./lib/telegram-api.ts";
46
- import * as TextGroups from "./lib/text-groups.ts";
47
- import * as ThreadReconciler from "./lib/thread-reconciler.ts";
48
- import * as Threads from "./lib/threads.ts";
49
- import * as TimeInjection from "./lib/time-injection.ts";
50
- import * as Updates from "./lib/updates.ts";
51
- import * as Voice from "./lib/voice.ts";
52
-
53
- type ActivePiModel = NonNullable<Pi.ExtensionContext["model"]>;
54
-
55
- const telegramBusProtocolIdentity =
56
- Bus.createTelegramCurrentBusProtocolIdentity([
57
- Bus.TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION,
58
- Bus.TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF,
59
- ]);
60
-
61
- // --- Extension Runtime ---
62
-
63
- export default function (pi: Pi.ExtensionAPI) {
64
- Skills.registerTelegramSkillDiscovery(pi);
65
- const piRuntime = Pi.createExtensionApiRuntimePorts(pi);
66
- const {
67
- getActiveTools,
68
- getCommands,
69
- getThinkingLevel,
70
- sendUserMessage,
71
- setActiveTools,
72
- setModel,
73
- setThinkingLevel,
74
- } = piRuntime;
75
- const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
76
- const runtimeDiagnostics =
77
- Logs.createTelegramRuntimeDiagnosticsRuntime<Pi.ExtensionContext>();
78
- const runtimeEvents = runtimeDiagnostics.events;
79
- const recordRuntimeEvent = runtimeDiagnostics.recordRuntimeEvent;
80
- const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
81
- const busProcessRuntime = Bus.createCurrentTelegramBusProcessRuntime({
82
- getActiveProfileName: configStore.getActiveProfileName,
83
- });
84
- const {
85
- instanceId: telegramInstanceId,
86
- processId: telegramProcessId,
87
- processBirthId: telegramQueueProcessBirthId,
88
- manualFollowerOwnerId: telegramManualFollowerOwnerId,
89
- getLeaderSocketPath: getTelegramBusSocketPath,
90
- getFollowerSocketPath: getTelegramBusFollowerSocketPath,
91
- } = busProcessRuntime;
92
- const getTelegramBotId = Config.createTelegramConfigBotIdGetter(configStore);
93
- const getTelegramActiveProfileKey =
94
- Config.createTelegramActiveProfileKeyGetter(configStore);
95
- const getTelegramManualFollowerProfileKey =
96
- BusFollower.createTelegramManualFollowerProfileKeyResolver({
97
- getActiveProfileName: configStore.getActiveProfileName,
98
- manualFollowerOwnerId: telegramManualFollowerOwnerId,
99
- });
100
- const telegramBusAuthSecret = Bus.createTelegramBusAuthSecret();
101
- const telegramBusFollowerControlState =
102
- BusFollower.createTelegramBusFollowerControlState();
103
- const telegramBusFollowerRegistry = Bus.createTelegramBusFollowerRegistry();
104
- const modelContextAvailabilityBinding =
105
- Prompts.createTelegramModelContextAvailabilityBinding();
106
- const telegramBusFollowerRegistrationState =
107
- BusFollower.createTelegramBusFollowerRegistrationState({
108
- onAvailabilityChanged: modelContextAvailabilityBinding.reconcile,
109
- });
110
- const telegramBusLeaderState =
111
- Threads.createTelegramLeaderThreadStateRuntime();
112
- const telegramThreadCapabilityState =
113
- Polling.createTelegramThreadCapabilityStateRuntime();
114
- const telegramProvisioningActivity =
115
- Sync.createTelegramProvisioningActivityRuntime();
116
- const messageOwnershipRuntime =
117
- Ownership.createTelegramBusMessageOwnershipRuntime({
118
- instanceId: telegramInstanceId,
119
- getProfileKey: getTelegramActiveProfileKey,
120
- listFollowers: telegramBusFollowerRegistry.list,
121
- });
122
- const { abort, lifecycle, queue, setup, typing } = bridgeRuntime;
123
- const getTelegramUpdateAdmissionScope =
124
- Journal.createTelegramUpdateJournalReceiptScopeResolver({
125
- getProfileName: configStore.getActiveProfileName,
126
- getBotToken: configStore.getBotToken,
127
- getBotId: getTelegramBotId,
128
- });
129
- const telegramJournalBindingRuntime =
130
- Journal.createTelegramUpdateJournalBindingRuntime({
131
- base: {
132
- getProfileName: configStore.getActiveProfileName,
133
- getBotToken: configStore.getBotToken,
134
- getBotId: getTelegramBotId,
135
- onRecovery(event) {
136
- recordRuntimeEvent(
137
- "recovery",
138
- event.kind === "repaired"
139
- ? "Telegram update journal was repaired automatically."
140
- : "Telegram update journal was reset after its damaged files were quarantined.",
141
- {
142
- phase: "journal-auto-recovery",
143
- recoveryKind: event.kind,
144
- journalPath: event.path,
145
- revision: event.revision,
146
- quarantinePath: event.quarantinePath,
147
- reason: event.reason,
148
- },
149
- );
150
- },
151
- getQueueRuntimeIdentity() {
152
- return {
153
- instanceId: telegramInstanceId,
154
- processId: telegramProcessId,
155
- processBirthId: telegramQueueProcessBirthId,
156
- };
157
- },
158
- },
159
- getLeaderJournalPath: Paths.resolveTelegramUpdateJournalPath,
160
- getFollowerJournalPath(bindingKey, profileName) {
161
- return Paths.resolveTelegramFollowerJournalPath(
162
- bindingKey,
163
- undefined,
164
- profileName,
165
- );
166
- },
167
- getActiveFollowerBindingKey: getTelegramManualFollowerProfileKey,
168
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
169
- });
170
- const resolveTelegramUpdateJournalBinding =
171
- telegramJournalBindingRuntime.resolveLeader;
172
- const resolveTelegramFollowerJournalBinding =
173
- telegramJournalBindingRuntime.resolveFollower;
174
- const getTelegramQueueJournalBinding =
175
- telegramJournalBindingRuntime.getActiveRecoveryKey;
176
- const isTelegramBusRuntimeEnabled =
177
- telegramThreadCapabilityState.isBusRuntimeEnabled;
178
- Config.bindGlobalTelegramConfigRuntime(configStore);
179
- const configControls = Config.createTelegramConfigControls(configStore);
180
- const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
181
- key: Locks.createTelegramLockKeyResolver(configStore),
182
- instanceId: telegramInstanceId,
183
- busSecret: telegramBusAuthSecret,
184
- staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
185
- });
186
- const threadStore = Threads.createTelegramTopicTargetStore({
187
- path: function () {
188
- return Threads.getTelegramTopicTargetsPath(
189
- undefined,
190
- configStore.getActiveProfileName(),
191
- );
192
- },
193
- canPersist: lockRuntime.owns,
194
- commitPersist: lockRuntime.commitIfOwned,
195
- });
196
- runtimeDiagnostics.bindStorage({
197
- getBotToken: configStore.getBotToken,
198
- getProfileName: configStore.getActiveProfileName,
199
- canReset: lockRuntime.owns,
200
- commitReset: lockRuntime.commitIfOwned,
201
- });
202
- const lockOwnershipGuard =
203
- Locks.createTelegramLockOwnershipGuard(lockRuntime);
204
- const getCurrentLeaderEpoch = lockRuntime.getOwnedLeaderEpoch;
205
- const telegramSessionContextStore =
206
- Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>({
207
- getIdentity(ctx) {
208
- return ctx.sessionManager ?? ctx.cwd;
209
- },
210
- });
211
- const ownsTelegramDirectDelivery =
212
- Locks.createTelegramDirectDeliveryOwnershipChecker({
213
- lock: lockRuntime,
214
- contextStore: telegramSessionContextStore,
215
- });
216
- const modelContextAvailabilityRuntime =
217
- Prompts.createTelegramModelContextAvailabilityRuntime({
218
- getActiveTools,
219
- setActiveTools,
220
- isAvailable() {
221
- return (
222
- ownsTelegramDirectDelivery() ||
223
- telegramBusFollowerRegistrationState.isRegistered()
224
- );
225
- },
226
- canReconcile() {
227
- const ctx = telegramSessionContextStore.get();
228
- return !ctx || Pi.isExtensionContextIdle(ctx);
229
- },
230
- });
231
- modelContextAvailabilityBinding.bind(modelContextAvailabilityRuntime);
232
- const activeTurnRuntime = Queue.createTelegramActiveTurnStore();
233
- const proactivePushTargetGetter =
234
- Config.createTelegramProactivePushTargetGetter({
235
- getActiveTurnTarget: activeTurnRuntime.getTarget,
236
- getAssignedTarget() {
237
- return (
238
- telegramBusFollowerRegistrationState.getTarget() ??
239
- telegramBusLeaderState.getTarget()
240
- );
241
- },
242
- getAllowedUserId: configStore.getAllowedUserId,
243
- });
244
- const proactivePushChatIdGetter =
245
- Config.createTelegramProactivePushChatIdGetter(proactivePushTargetGetter);
246
- const buttonActionStore = Outbound.createTelegramButtonActionStore();
247
- const planGenerativeAppOutput =
248
- Outbound.createTelegramOutboundReplyPlanner(
249
- buttonActionStore,
250
- configControls.getAssistantRenderingMode,
251
- );
252
- const pendingModelSwitchStore =
253
- Model.createPendingModelSwitchStore<
254
- Model.ScopedTelegramModel<ActivePiModel>
255
- >();
256
- const modelMenuRuntime = Menu.createTelegramModelMenuRuntime<ActivePiModel>();
257
- const sectionRegistry = Sections.createAndBindTelegramSectionRegistry();
258
-
259
- const timeInjectionRuntime = TimeInjection.createTimeInjectionRuntime({
260
- getConfig: Config.createTelegramTimeConfigGetter(configStore),
261
- recordRuntimeEvent,
262
- });
263
- Outbound.bindTelegramRuntimeEventRecorder(recordRuntimeEvent);
264
- const getContextModel = Pi.getExtensionContextModel;
265
- const isIdle = Pi.isExtensionContextIdle;
266
- const hasPendingMessages = Pi.hasExtensionContextPendingMessages;
267
- const compact = Pi.compactExtensionContext;
268
- const mediaGroupRuntime = Media.createTelegramMediaGroupController<
269
- TelegramApi.TelegramMessage,
270
- Pi.ExtensionContext
271
- >();
272
- const textGroupRuntime = TextGroups.createTelegramTextGroupController<
273
- TelegramApi.TelegramMessage,
274
- Pi.ExtensionContext
275
- >();
276
- const rawTelegramQueueStore =
277
- Queue.createTelegramQueueStore<Pi.ExtensionContext>();
278
- const telegramTransportStampRuntime =
279
- Queue.createTelegramTransportStampRuntime({
280
- getProfileName: configStore.getActiveProfileName,
281
- getBotToken: configStore.getBotToken,
282
- });
283
- const telegramQueueStore = Queue.createTelegramTransportStampedQueueStore(
284
- rawTelegramQueueStore,
285
- telegramTransportStampRuntime.getStamp,
286
- );
287
- const updateAdmissionRuntimeBinding =
288
- Updates.createTelegramUpdateAdmissionRuntimeBinding<Pi.ExtensionContext>({
289
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
290
- });
291
- const deferredQueueDispatchRuntime =
292
- Queue.createTelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>({
293
- recordRuntimeEvent,
294
- });
295
- const pollingControllerState = Polling.createTelegramPollingControllerState();
296
- const telegramSyncStateRuntime = Sync.createTelegramSyncStateRuntime();
297
- const threadReconciliationRuntime =
298
- ThreadReconciler.createThreadReconciliationRuntime({
299
- recordRuntimeEvent,
300
- scheduleSnapshotPersist: runtimeDiagnostics.scheduleSnapshotPersist,
301
- });
302
- const recordThreadReconciliationPlan = threadReconciliationRuntime.recordPlan;
303
- const persistTelegramConfigWithSync =
304
- Sync.createTelegramConfigSyncPersister<Config.TelegramConfig>({
305
- persist: configStore.persist,
306
- markConfigChange: telegramSyncStateRuntime.markConfigChange,
307
- });
308
- const {
309
- current: currentInstanceThreadRuntime,
310
- status: threadStatusProjectionRuntime,
311
- } = Threads.createTelegramCurrentThreadAssembly({
312
- instanceId: telegramInstanceId,
313
- listRecords: threadStore.list,
314
- getActiveTurnTarget: activeTurnRuntime.getTarget,
315
- getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
316
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
317
- getFollowerSlot: telegramBusFollowerRegistrationState.getSlot,
318
- getFollowerThreadName: telegramBusFollowerRegistrationState.getThreadName,
319
- getLeaderIdentity: telegramBusLeaderState.getIdentity,
320
- getLeaderTarget: telegramBusLeaderState.getTarget,
321
- getLeaderProtocol: telegramBusFollowerRegistrationState.getLeaderProtocol,
322
- status: {
323
- getThreadMode: function () {
324
- return threadStore.getBotState().threadMode;
325
- },
326
- isBusPollingStarted: telegramThreadCapabilityState.isBusPollingStarted,
327
- listFollowers: telegramBusFollowerRegistry.list,
328
- listReservations: threadStore.listReservations,
329
- listSyncObservations: threadStore.listSyncObservations,
330
- getLeaderSocketPath: getTelegramBusSocketPath,
331
- getFollowerSocketPath: getTelegramBusFollowerSocketPath,
332
- getTransportKind: BusTransport.getTelegramBusTransportKind,
333
- },
334
- });
335
- const findCurrentThreadRecord = currentInstanceThreadRuntime.findRecord;
336
- const getCurrentInstanceThreadIdentity =
337
- currentInstanceThreadRuntime.getIdentity;
338
- const statusRuntime = Status.createTelegramBridgeStatusRuntime<
339
- Pi.ExtensionContext,
340
- Queue.TelegramQueueItem<Pi.ExtensionContext>
341
- >({
342
- getConfig: configStore.get,
343
- getActiveProfileName: configStore.getActiveProfileName,
344
- getDiagnosticPaths: Paths.getTelegramDiagnosticsDisplayPaths,
345
- isPollingActive: Polling.createTelegramPollingActivityReader(
346
- pollingControllerState,
347
- ),
348
- getPollingState: Polling.createTelegramPollingStateReader(
349
- pollingControllerState,
350
- ),
351
- getInboundWorkerState() {
352
- return updateAdmissionRuntimeBinding.getActive()?.getState();
353
- },
354
- getAcceptedThroughUpdateId() {
355
- return resolveTelegramUpdateJournalBinding()?.journal.read()
356
- .acceptedThroughUpdateId;
357
- },
358
- getActiveSourceMessageIds: activeTurnRuntime.getSourceMessageIds,
359
- hasActiveTurn: activeTurnRuntime.has,
360
- hasDispatchPending: lifecycle.hasDispatchPending,
361
- isCompactionInProgress: lifecycle.isCompactionInProgress,
362
- getActiveToolExecutions: lifecycle.getActiveToolExecutions,
363
- hasPendingModelSwitch: pendingModelSwitchStore.has,
364
- getQueuedItems: telegramQueueStore.getQueuedItems,
365
- getQueuedItemCount: Queue.countExecutableTelegramQueueItems,
366
- formatQueuedStatus: Queue.formatQueuedTelegramItemsStatus,
367
- getRecentRuntimeEvents: runtimeEvents.getEvents,
368
- getRuntimeLockState: lockRuntime.getStatusLabel,
369
- ...threadStatusProjectionRuntime,
370
- getBusProtocol() {
371
- return telegramBusProtocolIdentity;
372
- },
373
- getBusLifecyclePhase: telegramBusFollowerControlState.getLifecyclePhase,
374
- getBotThreadMode() {
375
- return threadStore.getBotState();
376
- },
377
- getSyncState: telegramSyncStateRuntime.getState,
378
- getThreadReconciliationState() {
379
- return threadReconciliationRuntime.getState();
380
- },
381
- });
382
- runtimeDiagnostics.bindStatus({
383
- instanceId: telegramInstanceId,
384
- updateStatus: statusRuntime.updateStatus,
385
- getStatusState: statusRuntime.getStatusState,
386
- async persistSnapshot(snapshot) {
387
- threadStore.setStatusSnapshot(snapshot);
388
- await threadStore.persist();
389
- },
390
- });
391
- const updateStatus = runtimeDiagnostics.updateStatus;
392
- const getStatusLines = runtimeDiagnostics.getStatusLines;
393
- const inboundHandlerRuntime = Inbound.createTelegramInboundHandlerRuntime({
394
- getHandlers: configStore.getInboundHandlers,
395
- execCommand: CommandTemplates.execCommandTemplate,
396
- getCwd: Pi.getExtensionContextCwd,
397
- recordRuntimeEvent,
398
- });
399
-
400
- // --- Telegram API ---
401
-
402
- const directTelegramApiRuntime =
403
- TelegramApi.createDefaultTelegramBridgeApiRuntime({
404
- getBotToken: configStore.getBotToken,
405
- recordRuntimeEvent,
406
- captureRequestErrorHandler(body) {
407
- return Sync.captureTelegramStaleTargetRequestRecovery(body, {
408
- ...staleTopicApiErrorRecoveryDeps,
409
- getCurrentLeaderEpoch,
410
- getSessionGeneration: telegramSessionContextStore.getGeneration,
411
- getProfileName: configStore.getActiveProfileName,
412
- onRecovered: runtimeDiagnostics.scheduleSnapshotPersist,
413
- });
414
- },
415
- });
416
- const telegramBusFollowerClients =
417
- BusFollower.createTelegramBusFollowerClientRuntime<
418
- Pi.ExtensionContext,
419
- Updates.TelegramMessageReactionUpdated,
420
- Routing.TelegramRoutedCallbackQuery,
421
- Routing.TelegramRoutedMessage
422
- >({
423
- socketPath: getTelegramBusSocketPath,
424
- instanceId: telegramInstanceId,
425
- getApiAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
426
- getForwardingAuthSecret() {
427
- return telegramBusAuthSecret;
428
- },
429
- getRegistrationGeneration:
430
- telegramBusFollowerRegistrationState.getGeneration,
431
- waitForRegistrationGeneration:
432
- telegramBusFollowerRegistrationState.waitForGeneration,
433
- getForwardCommentBatchPosition:
434
- textGroupRuntime.getPreparedForwardingPosition,
435
- recordRuntimeEvent,
436
- });
437
- const telegramApiRuntime = BusApi.createTelegramBusAwareApiRuntime({
438
- directRuntime: directTelegramApiRuntime,
439
- ownsDirect() {
440
- return lockRuntime.owns();
441
- },
442
- getDefaultTarget: proactivePushTargetGetter,
443
- callFollowerApi: telegramBusFollowerClients.callApi,
444
- });
445
- const {
446
- call: callTelegramApi,
447
- callMultipart,
448
- deleteWebhook,
449
- getUpdates,
450
- setMyCommands,
451
- sendTypingAction,
452
- sendChatAction,
453
- sendRecordVoiceAction,
454
- sendMessageDraft,
455
- sendMessage,
456
- sendRichMessage,
457
- sendRichMessageDraft,
458
- downloadFile: downloadTelegramBridgeFile,
459
- editMessageText: editTelegramMessageText,
460
- editMessageReplyMarkup: editTelegramMessageReplyMarkup,
461
- answerCallbackQuery,
462
- answerGuestQuery,
463
- deleteMessage: deleteTelegramMessage,
464
- prepareTempDir,
465
- } = telegramApiRuntime;
466
-
467
- // --- Message Delivery ---
468
-
469
- const sendGuestReply = Replies.createGuestMarkdownReplySender({
470
- answerGuestQuery,
471
- });
472
-
473
- const promptDispatchRuntime = Runtime.createTelegramPromptDispatchRuntime({
474
- lifecycle,
475
- typing,
476
- getDefaultChatId: proactivePushChatIdGetter,
477
- sendTypingAction,
478
- sendAggregateTypingAction:
479
- BusApi.createTelegramAggregateTypingActionSender(telegramApiRuntime),
480
- updateStatus,
481
- isContextActive: telegramSessionContextStore.isCurrent,
482
- getTransportAuthority() {
483
- if (ownsTelegramDirectDelivery()) {
484
- const epoch = getCurrentLeaderEpoch();
485
- return epoch === undefined ? undefined : `direct:${epoch}`;
486
- }
487
- if (!telegramBusFollowerRegistrationState.isRegistered()) return undefined;
488
- const generation = telegramBusFollowerRegistrationState.getGeneration();
489
- return generation ? `follower:${generation}` : undefined;
490
- },
491
- recordRuntimeEvent,
492
- });
493
- const currentModelRuntime = Model.createCurrentModelRuntime({
494
- getContextModel,
495
- updateStatus,
496
- });
497
-
498
- // --- Reply Runtime & Preview ---
499
-
500
- const replyRuntime = Replies.createTelegramRenderedMessageDeliveryRuntime({
501
- recordOwnership: messageOwnershipRuntime.recordLocal,
502
- sendMessage,
503
- sendRichMessage,
504
- getAssistantRenderingMode: configControls.getAssistantRenderingMode,
505
- editMessage: editTelegramMessageText,
506
- });
507
- const { replyTransport, editInteractiveMessage, sendInteractiveMessage } =
508
- replyRuntime;
509
- const deliveryTargetPolicyRuntime =
510
- Delivery.createTelegramDeliveryTargetPolicyRuntime({
511
- ownsDirect: lockRuntime.owns,
512
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
513
- getAllowedChatId: configStore.getAllowedUserId,
514
- getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
515
- getLeaderTarget: telegramBusLeaderState.getTarget,
516
- listThreadRecords: threadStore.list,
517
- getActiveTurnTarget: activeTurnRuntime.getTarget,
518
- getActiveGuestQueryId: activeTurnRuntime.getGuestQueryId,
519
- });
520
- const deliveryGenerationSeed =
521
- Delivery.createTelegramDeliveryGenerationSeed(telegramInstanceId);
522
- const deliveryLifecycleRuntime =
523
- Delivery.createTelegramBridgeDeliveryLifecycleHooks({
524
- generationSeed: deliveryGenerationSeed,
525
- getTargetPolicyView: deliveryTargetPolicyRuntime.getTargetPolicyView,
526
- getTransportStamp: telegramTransportStampRuntime.getStamp,
527
- isTransportStampActive: telegramTransportStampRuntime.isActive,
528
- getActiveTurnTarget: deliveryTargetPolicyRuntime.getActiveTurnTarget,
529
- api: telegramApiRuntime,
530
- recordOwnership: messageOwnershipRuntime.recordLocal,
531
- recordFailure(operation, error, target) {
532
- recordRuntimeEvent("delivery", error, {
533
- operation,
534
- scope: target?.threadId === undefined ? "aggregate" : "thread",
535
- });
536
- },
537
- });
538
- const { sendTextReply, sendMarkdownReply } =
539
- Outbound.createTelegramOutboundTextReplyRuntime({
540
- sendTextReply: replyRuntime.sendTextReply,
541
- sendMarkdownReply: replyRuntime.sendMarkdownReply,
542
- execCommand: CommandTemplates.execCommandTemplate,
543
- getHandlers: configStore.getOutboundHandlers,
544
- recordRuntimeEvent,
545
- });
546
- const invokeGenerativeAppBoundButtonAction =
547
- Bindings.createTelegramGenerativeAppBoundButtonActionInvoker({
548
- agentDir: Paths.resolveAgentDir(),
549
- assertExecutionCurrent: Updates.assertTelegramUpdateExecutionCurrent,
550
- getExecutionFence: Updates.getTelegramUpdateExecutionFence,
551
- planOutput: planGenerativeAppOutput,
552
- sendMarkdownReply,
553
- editInteractiveMessage,
554
- recordRuntimeEvent,
555
- });
556
- const nativeMarkdownDraftSender =
557
- TelegramApi.createTelegramAssistantDraftSender({
558
- getAssistantRenderingMode: configControls.getAssistantRenderingMode,
559
- renderMarkdownToHtmlDraft: Replies.renderTelegramMarkdownToHtmlDraft,
560
- sendMessageDraft,
561
- sendRichMessageDraft,
562
- });
563
- const previewRuntime = Preview.createTelegramAssistantPreviewRuntime({
564
- getActiveTurn: activeTurnRuntime.get,
565
- isAssistantMessage: Replies.isAssistantAgentMessage,
566
- getMessageText: Replies.getAgentMessageText,
567
- getDefaultReplyToMessageId: activeTurnRuntime.getReplyToMessageId,
568
- sendDraft: nativeMarkdownDraftSender,
569
- canSend: configControls.areDraftPreviewsEnabled,
570
- sendMarkdownReply,
571
- recordRuntimeEvent,
572
- ...replyTransport,
573
- });
574
- const {
575
- activityRuntime,
576
- activityVerbosityRuntime,
577
- assistantOutputRuntime,
578
- publicationRuntime,
579
- } = Bindings.createTelegramActivityBindingRuntime({
580
- generation: deliveryGenerationSeed,
581
- assistantOutput: {
582
- prepareTelegramPreview: previewRuntime.preparePublication,
583
- authority: {
584
- getPreferredTarget: proactivePushTargetGetter,
585
- getFallbackChatId: proactivePushChatIdGetter,
586
- getTransportStamp: telegramTransportStampRuntime.getStamp,
587
- isTransportStampActive: telegramTransportStampRuntime.isActive,
588
- ownsDirect: lockRuntime.owns,
589
- getDirectEpoch: lockRuntime.getOwnedLeaderEpoch,
590
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
591
- getFollowerGeneration:
592
- telegramBusFollowerRegistrationState.getGeneration,
593
- },
594
- sender: {
595
- recordOwnership: messageOwnershipRuntime.recordLocal,
596
- sendMessage,
597
- sendRichMessage,
598
- editMessage: editTelegramMessageText,
599
- getAssistantRenderingMode: configControls.getAssistantRenderingMode,
600
- planButtonReply:
601
- Outbound.createTelegramButtonReplyPlanner(buttonActionStore),
602
- execCommand: CommandTemplates.execCommandTemplate,
603
- getHandlers: configStore.getOutboundHandlers,
604
- recordRuntimeEvent,
605
- },
606
- recordRuntimeEvent,
607
- },
608
- activityVerbosity: {
609
- getActivityMode: configControls.getActivityVerbosity,
610
- refreshActivityMode: configControls.refreshActivityVerbosity,
611
- resolveTarget(event) {
612
- return event.target ?? proactivePushTargetGetter();
613
- },
614
- sendMessage,
615
- sendRichMessage,
616
- editMessageText: editTelegramMessageText,
617
- },
618
- });
619
- const {
620
- mutation: queueMutationRuntime,
621
- dispatchNext: dispatchNextQueuedTelegramTurn,
622
- watchdog: queueDispatchWatchdogRuntime,
623
- } = Bindings.createTelegramQueueBindingRuntime({
624
- store: telegramQueueStore,
625
- queue,
626
- lifecycle,
627
- activeTurn: activeTurnRuntime,
628
- admission: updateAdmissionRuntimeBinding,
629
- transportStamp: telegramTransportStampRuntime,
630
- deferredDispatch: deferredQueueDispatchRuntime,
631
- promptDispatch: promptDispatchRuntime,
632
- isIdle,
633
- hasPendingMessages,
634
- updateStatus,
635
- sendTextReply,
636
- sendUserMessage,
637
- recordRuntimeEvent,
638
- });
639
- const { finalizeMarkdownPreview, preparePreviewDelivery } =
640
- Outbound.createTelegramOutboundTextPreviewRuntime({
641
- finalizeMarkdownPreview: previewRuntime.finalizeMarkdown,
642
- preparePreviewDelivery: previewRuntime.prepareDelivery,
643
- execCommand: CommandTemplates.execCommandTemplate,
644
- getHandlers: configStore.getOutboundHandlers,
645
- recordRuntimeEvent,
646
- });
647
-
648
- // --- Model And Menu Setup ---
649
-
650
- const modelSwitchController =
651
- Model.createTelegramModelSwitchControllerRuntime({
652
- isIdle,
653
- getPendingModelSwitch: pendingModelSwitchStore.get,
654
- setPendingModelSwitch: pendingModelSwitchStore.set,
655
- getActiveTurn: activeTurnRuntime.get,
656
- getAbortHandler: abort.getHandler,
657
- hasAbortHandler: abort.hasHandler,
658
- getActiveToolExecutions: lifecycle.getActiveToolExecutions,
659
- allocateItemOrder: queue.allocateItemOrder,
660
- allocateControlOrder: queue.allocateControlOrder,
661
- appendQueuedItem: queueMutationRuntime.append,
662
- updateStatus,
663
- });
664
- const getQueueItemCount =
665
- Queue.createTelegramQueueItemCountGetter(telegramQueueStore);
666
- const getPromptTemplateCommands =
667
- PromptTemplates.createTelegramPromptTemplateCommandGetter({
668
- getCommands,
669
- getReservedCommandNames: Commands.getTelegramReservedCommandNames,
670
- });
671
- const menuActions = Menu.createTelegramMenuActionRuntimeWithStateBuilder({
672
- runtime: modelMenuRuntime,
673
- createSettingsManager: Pi.createSettingsManager,
674
- getActiveModel: currentModelRuntime.get,
675
- getThinkingLevel,
676
- getQueueItemCount,
677
- buildStatusHtml: Commands.createTelegramAppMenuHtmlBuilder({
678
- buildStatusHtml: Status.createTelegramStatusHtmlBuilder({
679
- getActiveModel: currentModelRuntime.get,
680
- isCompactionInProgress: lifecycle.isCompactionInProgress,
681
- getBridgeStatusLineState: statusRuntime.getStatusState,
682
- }),
683
- getPromptTemplateCommands,
684
- }),
685
- storeModelMenuState: modelMenuRuntime.storeState,
686
- isIdle,
687
- canOfferInFlightModelSwitch: modelSwitchController.canOfferInFlightSwitch,
688
- sendTextReply,
689
- editInteractiveMessage,
690
- sendInteractiveMessage,
691
- sectionRegistry,
692
-
693
- // Menu/status UI uses this to reflect whether the active Telegram turn expects voice delivery.
694
- isVoiceReplyActive: function () {
695
- const turn = activeTurnRuntime.get();
696
- return Voice.isVoiceTurn(turn);
697
- },
698
- });
699
-
700
- // --- Queue And Settings Menus ---
701
-
702
- const getQueueMenuState = Menu.createTelegramModelMenuStateBuilder({
703
- runtime: modelMenuRuntime,
704
- createSettingsManager: Pi.createSettingsManager,
705
- getActiveModel: currentModelRuntime.get,
706
- });
707
- const queueMenuRuntime = MenuQueue.createTelegramQueueMenuRuntime({
708
- telegramQueueStore,
709
- queueMutationRuntime,
710
- sendInteractiveMessage,
711
- editInteractiveMessage,
712
- answerCallbackQuery,
713
- getModelMenuState: getQueueMenuState,
714
- getStoredModelMenuState: modelMenuRuntime.getState,
715
- storeModelMenuState: modelMenuRuntime.storeState,
716
- updateStatusMessage: menuActions.updateStatusMessage,
717
- updateStatus,
718
- });
719
- const settingsMenuRuntime = MenuSettings.createTelegramSettingsMenuRuntime(
720
- {
721
- reloadConfig: configStore.load,
722
- getModelMenuState: getQueueMenuState,
723
- getStoredModelMenuState: modelMenuRuntime.getState,
724
- storeModelMenuState: modelMenuRuntime.storeState,
725
- editInteractiveMessage,
726
- sendInteractiveMessage,
727
- answerCallbackQuery,
728
- ...configControls,
729
- },
730
- sectionRegistry,
731
- );
732
-
733
- // --- Polling ---
734
-
735
- const foreignOwnedUpdateForwarder =
736
- telegramBusFollowerClients.foreignOwnedUpdateForwarder;
737
- const followerTargetController = telegramBusFollowerClients.targetController;
738
- const restoreFollowerThreadTarget =
739
- Bus.createTelegramBusFollowerThreadRestoreHandler({
740
- followerRegistry: telegramBusFollowerRegistry,
741
- followerTargetController,
742
- onRestored() {
743
- telegramSyncStateRuntime.markSliceFresh(
744
- Sync.TELEGRAM_SYNC_SLICE_TARGET_BINDINGS,
745
- {
746
- nowMs: Date.now(),
747
- action: "follower-thread-restore",
748
- },
749
- );
750
- },
751
- });
752
- const observedThreadTargetBinding =
753
- Polling.createTelegramThreadTargetObservationBinding<Pi.ExtensionContext>();
754
- const topicLifecycleSync =
755
- Sync.createTelegramObservedTopicLifecycleSyncHandler({
756
- topicTargetStore: threadStore,
757
- isBusEnabled: isTelegramBusRuntimeEnabled,
758
- callApi: callTelegramApi,
759
- isTopicProvisioningActive: telegramProvisioningActivity.isActive,
760
- getCurrentLeaderEpoch,
761
- getThreadReconciliationMachineState: threadReconciliationRuntime.getState,
762
- recordThreadReconciliationPlan,
763
- assertExecutionCurrent(message) {
764
- Updates.assertTelegramUpdateExecutionCurrent(message);
765
- },
766
- getSyncState: telegramSyncStateRuntime.getState,
767
- setSyncState: telegramSyncStateRuntime.setState,
768
- recordEvent: recordRuntimeEvent,
769
- });
770
- const inboundBusProjectionRuntime =
771
- Routing.createTelegramInboundBusProjectionRuntime({
772
- instanceId: telegramInstanceId,
773
- listFollowers: telegramBusFollowerRegistry.list,
774
- listThreadRecords: threadStore.list,
775
- getLeaderTarget: telegramBusLeaderState.getTarget,
776
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
777
- getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
778
- getCurrentIdentity: getCurrentInstanceThreadIdentity,
779
- });
780
- const inboundRouteRuntime = Routing.createTelegramInboundRouteRuntime({
781
- configStore,
782
- callApi: callTelegramApi,
783
- getCurrentInstanceId() {
784
- return telegramInstanceId;
785
- },
786
- getAdmissionScope: getTelegramUpdateAdmissionScope,
787
- getAdmissionJournalBinding: getTelegramQueueJournalBinding,
788
- getMessageOwnership: messageOwnershipRuntime.store.get,
789
- recordMessageOwnership: messageOwnershipRuntime.recordRouted,
790
- ...inboundBusProjectionRuntime,
791
- getCurrentLeaderEpoch,
792
- setCurrentLeaderIdentity: telegramBusLeaderState.set,
793
- getThreadReconciliationMachineState: threadReconciliationRuntime.getState,
794
- recordThreadReconciliationPlan,
795
- handleTelegramTopicLifecycleUpdate: topicLifecycleSync,
796
- handleTelegramThreadTargetObserved(_target, ctx) {
797
- return observedThreadTargetBinding.handle(ctx);
798
- },
799
- foreignOwnedUpdateForwarder,
800
- replaceFollowerThreadTarget: restoreFollowerThreadTarget,
801
- bridgeRuntime,
802
- activeTurnRuntime,
803
- mediaGroupRuntime,
804
- textGroupRuntime,
805
- telegramQueueStore,
806
- queueMutationRuntime,
807
- modelMenuRuntime,
808
- currentModelRuntime,
809
- modelSwitchController,
810
- menuActions,
811
- updateSettingsMenuMessage: settingsMenuRuntime.updateSettingsMenuMessage,
812
- openQueueMenu: queueMenuRuntime.openQueueMenu,
813
- queueMenuCallbackHandler: queueMenuRuntime.handleCallbackQuery,
814
- openSettingsMenu: settingsMenuRuntime.openSettingsMenu,
815
- settingsMenuCallbackHandler: settingsMenuRuntime.handleCallbackQuery,
816
- sectionRegistry,
817
- buttonActionStore,
818
- invokeBoundButtonAction: invokeGenerativeAppBoundButtonAction,
819
- inboundHandlerRuntime,
820
- threadStore,
821
- updateStatus,
822
- isContextActive: telegramSessionContextStore.isCurrent,
823
- dispatchNextQueuedTelegramTurn,
824
- requestDeferredDispatchNextQueuedTelegramTurn:
825
- deferredQueueDispatchRuntime.request,
826
- hasDeferredDispatchContext: deferredQueueDispatchRuntime.isBound,
827
- startTypingLoop: promptDispatchRuntime.startTypingLoop,
828
- stopTypingLoop: typing.stop,
829
- answerCallbackQuery,
830
- editInteractiveMessage,
831
- editMessageReplyMarkup: editTelegramMessageReplyMarkup,
832
- sendInteractiveMessage,
833
- deleteMessage: deleteTelegramMessage,
834
- answerGuestQuery,
835
- sendTextReply,
836
- setMyCommands,
837
- getCommands,
838
- downloadFile: downloadTelegramBridgeFile,
839
- resolveTimeLine: timeInjectionRuntime.resolveLine,
840
- getThinkingLevel,
841
- setThinkingLevel,
842
- persistScopedModelPatterns: Pi.createScopedModelPatternPersister({
843
- createSettingsManager: Pi.createSettingsManager,
844
- clearCachedModelMenuInputs: modelMenuRuntime.clearCachedInputs,
845
- }),
846
- setModel,
847
- sendUserMessage,
848
- isIdle,
849
- hasPendingMessages,
850
- compact,
851
- recordRuntimeEvent,
852
- });
853
- const queueHandoffReconciliationBinding =
854
- Updates.createTelegramQueueHandoffReconciliationBinding<Pi.ExtensionContext>(
855
- function (error) {
856
- recordRuntimeEvent("inbound-worker", error, {
857
- phase: "queue-handoff-reconcile",
858
- });
859
- },
860
- );
861
- const staleTopicApiErrorRecoveryDeps = {
862
- topicTargetStore: threadStore,
863
- getSyncState: telegramSyncStateRuntime.getState,
864
- setSyncState: telegramSyncStateRuntime.setState,
865
- recordEvent: recordRuntimeEvent,
866
- };
867
- const recoverStaleTelegramTopicApiError =
868
- Sync.createTelegramStaleTopicApiErrorRecoveryRuntime(
869
- staleTopicApiErrorRecoveryDeps,
870
- );
871
- const {
872
- owner: updateWorkerOwnerRuntime,
873
- leader: updateAdmissionLifecycleRuntime,
874
- follower: followerAdmissionLifecycleRuntime,
875
- } = Updates.createTelegramUpdateAdmissionRuntimeAssembly<
876
- Parameters<typeof inboundRouteRuntime.handleUpdate>[0] &
877
- Journal.TelegramJournaledUpdate,
878
- Pi.ExtensionContext
879
- >({
880
- runtimeBinding: updateAdmissionRuntimeBinding,
881
- owner: {
882
- instanceId: telegramInstanceId,
883
- processId: telegramProcessId,
884
- processBirthId: telegramQueueProcessBirthId,
885
- getSessionGeneration: telegramSessionContextStore.getGeneration,
886
- isContextCurrent: telegramSessionContextStore.isCurrent,
887
- dispatchNext: dispatchNextQueuedTelegramTurn,
888
- requestQueueHandoffReconciliation:
889
- queueHandoffReconciliationBinding.request,
890
- },
891
- worker: {
892
- defaultHandle: inboundRouteRuntime.handleUpdate,
893
- onStateChange: runtimeDiagnostics.scheduleSnapshotPersist,
894
- settleTerminalExecutionFailure(error) {
895
- return Sync.settleStaleTelegramTopicExecutionFailure(
896
- error,
897
- staleTopicApiErrorRecoveryDeps,
898
- );
899
- },
900
- },
901
- leader: {
902
- resolveBinding: resolveTelegramUpdateJournalBinding,
903
- hasAuthority: lockRuntime.owns,
904
- },
905
- follower: {
906
- resolveBinding: resolveTelegramFollowerJournalBinding,
907
- isRegistered: telegramBusFollowerRegistrationState.isRegistered,
908
- getGeneration: telegramBusFollowerRegistrationState.getGeneration,
909
- prepareUpdateForExecution(update) {
910
- return BusFollower.prepareTelegramBusFollowerJournaledUpdateForExecution(
911
- update,
912
- textGroupRuntime.prepareForwardedMessage,
913
- );
914
- },
915
- },
916
- recordRuntimeEvent,
917
- });
918
- const queueHandoffStagingRuntime =
919
- Queue.createTelegramQueueHandoffStagingRuntime({
920
- liveStore: telegramQueueStore,
921
- createControlExecution:
922
- Updates.createTelegramQueueHandoffControlExecutionFactory({
923
- isContextCurrent: telegramSessionContextStore.isCurrent,
924
- showStatus: menuActions.sendStatusMessage,
925
- openModelMenu: menuActions.openModelMenu,
926
- }),
927
- });
928
- const acceptStagedQueueHandoff =
929
- Updates.createTelegramQueueHandoffRecipientRuntime({
930
- staging: queueHandoffStagingRuntime,
931
- getRecipientOwner: updateWorkerOwnerRuntime.getQueueOwnerIdentity,
932
- getLifecycleForBinding:
933
- updateAdmissionRuntimeBinding.getLifecycleForJournalBinding,
934
- isTransportStampActive: telegramTransportStampRuntime.isActive,
935
- dispatchNext: dispatchNextQueuedTelegramTurn,
936
- });
937
- const followerDurableAdmissionRuntime =
938
- BusFollower.createTelegramBusFollowerDurableAdmissionRuntime({
939
- journal: {
940
- appendBatch(updates) {
941
- return followerAdmissionLifecycleRuntime.appendBatch(updates);
942
- },
943
- },
944
- signalWorker() {
945
- followerAdmissionLifecycleRuntime.signal();
946
- },
947
- });
948
- const promoteTelegramBusFollowerToLeader: BusFollower.TelegramBusFollowerPromotionHandler<Pi.ExtensionContext> =
949
- BusFollower.createTelegramBusFollowerPromotionHandler<Pi.ExtensionContext>({
950
- topicTargetStore: threadStore,
951
- instanceId: telegramInstanceId,
952
- getActiveProfileName: configStore.getActiveProfileName,
953
- async startLeader(ctx, election, onAcquired): Promise<boolean> {
954
- const result = await lockedPollingRuntime.start(ctx, {
955
- election,
956
- onAcquired,
957
- });
958
- return result.ok;
959
- },
960
- recordRuntimeEvent,
961
- });
962
- const agentMessageRuntime = AgentMessages.createTelegramAgentMessageRuntime({
963
- instanceId: telegramInstanceId,
964
- getAllowedChatId: configStore.getAllowedUserId,
965
- getLeaderTarget: telegramBusLeaderState.getTarget,
966
- getLeaderThreadName() {
967
- return findCurrentThreadRecord()?.threadName;
968
- },
969
- followerRegistry: telegramBusFollowerRegistry,
970
- getContext: telegramSessionContextStore.get,
971
- handleUpdate: inboundRouteRuntime.handleUpdate,
972
- });
973
- const agentMessageToolRoutingRuntime =
974
- Bindings.createTelegramAgentMessageToolRoutingRuntime({
975
- ownsLeader: lockRuntime.owns,
976
- ownsDirectDelivery: ownsTelegramDirectDelivery,
977
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
978
- getSourceTarget: proactivePushTargetGetter,
979
- getSourceThreadName() {
980
- return findCurrentThreadRecord()?.threadName;
981
- },
982
- local: agentMessageRuntime,
983
- follower: telegramBusFollowerClients.agentMessages,
984
- });
985
- const telegramBusFollowerAssembly: BusFollower.TelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext> =
986
- BusFollower.createTelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext>({
987
- instanceId: telegramInstanceId,
988
- registrationState: telegramBusFollowerRegistrationState,
989
- recordRuntimeEvent,
990
- receiver: {
991
- socketPath: getTelegramBusFollowerSocketPath,
992
- getContext: telegramSessionContextStore.get,
993
- getAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
994
- getRecipientBindingKey: getTelegramManualFollowerProfileKey,
995
- durableAdmission: followerDurableAdmissionRuntime,
996
- handleQueueHandoff: acceptStagedQueueHandoff,
997
- },
998
- targetReplacement: {
999
- topicTargetStore: threadStore,
1000
- getManualFollowerProfileKey: getTelegramManualFollowerProfileKey,
1001
- manualFollowerOwnerId: telegramManualFollowerOwnerId,
1002
- getSyncState: telegramSyncStateRuntime.getState,
1003
- setSyncState: telegramSyncStateRuntime.setState,
1004
- updateStatus,
1005
- },
1006
- recovery: {
1007
- getLeaderState: lockRuntime.getState,
1008
- setLifecyclePhase: telegramBusFollowerControlState.setLifecyclePhase,
1009
- updateStatus,
1010
- promoteToLeader: promoteTelegramBusFollowerToLeader,
1011
- getActiveContext: telegramSessionContextStore.get,
1012
- },
1013
- registration: {
1014
- protocolIdentity: telegramBusProtocolIdentity,
1015
- getFollowerBusSocketPath: getTelegramBusFollowerSocketPath,
1016
- getLeaderSocketPath: getTelegramBusSocketPath,
1017
- isContextActive: telegramSessionContextStore.isCurrent,
1018
- createRequestId: telegramBusFollowerClients.createRequestId,
1019
- setActiveAuthSecret:
1020
- telegramBusFollowerControlState.setActiveAuthSecret,
1021
- getProfileKey: getTelegramManualFollowerProfileKey,
1022
- getProcessBirthId() {
1023
- return telegramQueueProcessBirthId;
1024
- },
1025
- getSessionGeneration: telegramSessionContextStore.getGeneration,
1026
- async onRegistered(ctx) {
1027
- await followerAdmissionLifecycleRuntime.onTransportChanged(ctx);
1028
- queueHandoffReconciliationBinding.request(ctx);
1029
- },
1030
- },
1031
- });
1032
- const telegramBusFollowerRegistration =
1033
- telegramBusFollowerAssembly.registration;
1034
- const { admission: pollingAdmissionRuntime } =
1035
- Polling.createTelegramDurablePollingRuntimeAssembly<
1036
- TelegramApi.TelegramUpdate,
1037
- Pi.ExtensionContext
1038
- >({
1039
- state: pollingControllerState,
1040
- canStart(ctx) {
1041
- return telegramSessionContextStore.isCurrent(ctx) && lockRuntime.owns(ctx);
1042
- },
1043
- onPersistentConflict(ctx, count): Promise<void> {
1044
- return lockedPollingRuntime.onPersistentConflict(ctx, count);
1045
- },
1046
- getConfig: configStore.get,
1047
- hasBotToken: configStore.hasBotToken,
1048
- deleteWebhook,
1049
- getUpdates,
1050
- persistConfig: persistTelegramConfigWithSync,
1051
- prepareUpdateBatch: textGroupRuntime.prepareUpdateBatch,
1052
- journal: {
1053
- appendBatch(updates, acceptedThroughUpdateId) {
1054
- return updateAdmissionLifecycleRuntime.appendBatch(
1055
- updates as Journal.TelegramJournaledUpdate[],
1056
- acceptedThroughUpdateId,
1057
- );
1058
- },
1059
- getAcceptedThroughUpdateId() {
1060
- return resolveTelegramUpdateJournalBinding()?.journal.read()
1061
- .acceptedThroughUpdateId;
1062
- },
1063
- async prepareCursorCutover() {
1064
- const binding = resolveTelegramUpdateJournalBinding();
1065
- if (!binding) {
1066
- throw new Error("Telegram update journal binding is unavailable.");
1067
- }
1068
- await Polling.cutOverTelegramPollingCursor({
1069
- getLegacyCursor: configStore.getLegacyPollingCursor,
1070
- readJournal: binding.journal.read,
1071
- publishJournalCursor(acceptedThroughUpdateId) {
1072
- binding.journal.appendBatch([], acceptedThroughUpdateId);
1073
- },
1074
- async removeLegacyCursor() {
1075
- configStore.removeLegacyPollingCursor();
1076
- await persistTelegramConfigWithSync();
1077
- },
1078
- });
1079
- },
1080
- getEntryCount: updateAdmissionLifecycleRuntime.getJournalEntryCount,
1081
- signalWorker: updateAdmissionLifecycleRuntime.signal,
1082
- getBootstrapEntryCount() {
1083
- return (
1084
- resolveTelegramUpdateJournalBinding()?.journal.read().entries
1085
- .length ?? 0
1086
- );
1087
- },
1088
- onSessionStart: updateAdmissionLifecycleRuntime.onSessionStart,
1089
- },
1090
- stopTypingLoop: typing.stop,
1091
- updateStatus,
1092
- onPollingStateChange: runtimeDiagnostics.scheduleSnapshotPersist,
1093
- recordRuntimeEvent,
1094
- });
1095
- const authorizeFollowerApiCall = Bus.createTelegramFollowerApiCallAuthorizer({
1096
- isMessageOwned: messageOwnershipRuntime.isOwnedByFollower,
1097
- });
1098
- const telegramBusLeaderRuntime =
1099
- BusLeader.createTelegramBusLeaderRuntimeAssembly<Pi.ExtensionContext>({
1100
- runtime: {
1101
- socketPath: getTelegramBusSocketPath,
1102
- commitEndpointPublication(commit) {
1103
- return lockRuntime.commitIfOwned(commit);
1104
- },
1105
- followerRegistry: telegramBusFollowerRegistry,
1106
- authSecret: telegramBusAuthSecret,
1107
- protocolIdentity: telegramBusProtocolIdentity,
1108
- startPolling: pollingAdmissionRuntime.start,
1109
- stopPolling: pollingAdmissionRuntime.stop,
1110
- authorizeFollowerApiCall,
1111
- resolveAgentTarget(follower, selector) {
1112
- return agentMessageRuntime.resolveTarget(selector, follower.target);
1113
- },
1114
- routeAgentMessage(follower, message) {
1115
- return agentMessageRuntime.route({
1116
- sourceTarget: follower.target,
1117
- sourceThreadName: follower.threadName,
1118
- message,
1119
- });
1120
- },
1121
- isFollowerProcessAlive: Locks.isProcessAlive,
1122
- shouldCleanupConfirmedDeadFollower:
1123
- configControls.resolveAutomaticThreadCleanupEnabled,
1124
- recordFollowerMessageOwnership(record) {
1125
- messageOwnershipRuntime.recordFollower(record);
1126
- },
1127
- },
1128
- getAllowedUserId: configStore.getAllowedUserId,
1129
- instanceId: telegramInstanceId,
1130
- getCwd: Pi.getExtensionContextCwd,
1131
- getTelegramProfile: configStore.getActiveProfileName,
1132
- shouldForceFreshUnnamed:
1133
- telegramThreadCapabilityState.shouldForceFreshLeaderThread,
1134
- topicTargetStore: threadStore,
1135
- callApi(method, body) {
1136
- return directTelegramApiRuntime.call(method, body);
1137
- },
1138
- callMultipart: directTelegramApiRuntime.callMultipart,
1139
- downloadFile: directTelegramApiRuntime.downloadFile,
1140
- recoverStaleTargetError: recoverStaleTelegramTopicApiError,
1141
- getCurrentLeaderEpoch,
1142
- getThreadReconciliationMachineState: threadReconciliationRuntime.getState,
1143
- recordThreadReconciliationPlan,
1144
- getSyncState: telegramSyncStateRuntime.getState,
1145
- setSyncState: telegramSyncStateRuntime.setState,
1146
- setLeaderTarget: telegramBusLeaderState.set,
1147
- onProvisioningStart: telegramProvisioningActivity.start,
1148
- onProvisioningEnd: telegramProvisioningActivity.end,
1149
- recordRuntimeEvent,
1150
- });
1151
- queueHandoffReconciliationBinding.set(
1152
- Updates.createTelegramQueueHandoffReconciliationRuntimeAssembly({
1153
- ownsDirect: lockRuntime.owns,
1154
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
1155
- isBusEnabled: isTelegramBusRuntimeEnabled,
1156
- canHandoffWithLeader() {
1157
- return Bus.hasTelegramBusCapability(
1158
- telegramBusFollowerRegistrationState.getLeaderProtocol(),
1159
- Bus.TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF,
1160
- );
1161
- },
1162
- listFollowers: telegramBusFollowerRegistry.list,
1163
- createRecipientJournalResolver:
1164
- telegramJournalBindingRuntime.createRecipientResolver,
1165
- queueStore: telegramQueueStore,
1166
- admission: updateAdmissionRuntimeBinding,
1167
- createHandoffToken: Journal.createTelegramUpdateQueueHandoffToken,
1168
- createRequestId: telegramBusFollowerClients.createRequestId,
1169
- donorInstanceId: telegramInstanceId,
1170
- authSecret: telegramBusAuthSecret,
1171
- stageThroughFollower: telegramBusFollowerClients.queueHandoff,
1172
- routeThroughLeader: telegramBusLeaderRuntime.routeQueueHandoff,
1173
- recordRuntimeEvent,
1174
- }),
1175
- );
1176
- const telegramLeaderHealthRuntime = Sync.createTelegramLeaderHealthRuntime({
1177
- callGetMe() {
1178
- return directTelegramApiRuntime.call("getMe", {});
1179
- },
1180
- getSyncState: telegramSyncStateRuntime.getState,
1181
- setSyncState: telegramSyncStateRuntime.setState,
1182
- recordEvent: recordRuntimeEvent,
1183
- });
1184
- const telegramThreadCapabilityRuntime =
1185
- Polling.createTelegramThreadCapabilityOrchestration<
1186
- Pi.ExtensionContext,
1187
- Locks.TelegramLockEntry
1188
- >({
1189
- state: telegramThreadCapabilityState,
1190
- getAllowedUserId: configStore.getAllowedUserId,
1191
- callApi: callTelegramApi,
1192
- topicTargetStore: threadStore,
1193
- isBusRuntimeEnabled: isTelegramBusRuntimeEnabled,
1194
- ownsLock: lockRuntime.owns,
1195
- isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
1196
- startClassicPolling: pollingAdmissionRuntime.start,
1197
- stopClassicPolling: pollingAdmissionRuntime.stop,
1198
- startBusLeaderPolling: telegramBusLeaderRuntime.startPolling,
1199
- stopBusLeaderPolling: telegramBusLeaderRuntime.stopPolling,
1200
- startLeaderHealth: telegramLeaderHealthRuntime.start,
1201
- stopLeaderHealth: telegramLeaderHealthRuntime.stop,
1202
- registerFollowerWithLeader:
1203
- telegramBusFollowerRegistration.registerWithLeader,
1204
- stopFollowerRegistration: telegramBusFollowerRegistration.stop,
1205
- isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
1206
- updateStatus,
1207
- recordEvent: recordRuntimeEvent,
1208
- });
1209
- const telegramThreadCapabilityMonitor =
1210
- telegramThreadCapabilityRuntime.monitor;
1211
- observedThreadTargetBinding.set(
1212
- telegramThreadCapabilityRuntime.observeTarget,
1213
- );
1214
- const threadAwarePollingPorts = telegramThreadCapabilityRuntime.pollingPorts;
1215
- const lockedPollingRuntime = Locks.createTelegramLockedPollingRuntime({
1216
- lock: lockRuntime,
1217
- transportMonitor: telegramThreadCapabilityMonitor,
1218
- hasBotToken: configStore.hasBotToken,
1219
- canStartPolling: Pi.canStartPollingInExtensionContext,
1220
- isContextCurrent: telegramSessionContextStore.isCurrent,
1221
- formatStartBlockedMessage: Pi.formatPollingStartBlockedByRunMode,
1222
- startPolling: threadAwarePollingPorts.startPolling,
1223
- stopPolling: threadAwarePollingPorts.stopPolling,
1224
- registerFollowerWithOwner:
1225
- threadAwarePollingPorts.registerFollowerWithOwner,
1226
- stopFollowerRegistration: threadAwarePollingPorts.stopFollowerRegistration,
1227
- onTransportAvailabilityChanged() {
1228
- modelContextAvailabilityRuntime.reconcile();
1229
- const ctx = telegramSessionContextStore.get();
1230
- if (ctx) queueHandoffReconciliationBinding.request(ctx);
1231
- },
1232
- updateStatus,
1233
- recordRuntimeEvent,
1234
- });
1235
- const {
1236
- disconnect: disconnectTelegramAndDeleteCurrentThread,
1237
- cleanupForSessionRestart: cleanupTelegramThreadForSessionRestart,
1238
- } = Sync.createTelegramThreadDisconnectAssembly({
1239
- instanceId: telegramInstanceId,
1240
- getCurrentThreadRecord: findCurrentThreadRecord,
1241
- topicTargetStore: threadStore,
1242
- callApi: callTelegramApi,
1243
- getCurrentLeaderEpoch,
1244
- getLeaderTarget: telegramBusLeaderState.getTarget,
1245
- clearLeaderTarget: telegramBusLeaderState.clear,
1246
- disconnectFollowerThread:
1247
- telegramBusFollowerRegistration.disconnectFromLeader,
1248
- getSyncState: telegramSyncStateRuntime.getState,
1249
- setSyncState: telegramSyncStateRuntime.setState,
1250
- stopPolling: lockedPollingRuntime.stop,
1251
- suspendPolling: lockedPollingRuntime.suspend,
1252
- recordRuntimeEvent,
1253
- });
1254
- const telegramBridgeSessionLifecycleDeps =
1255
- Lifecycle.createTelegramBridgeSessionLifecycleDeps({
1256
- contextStore: telegramSessionContextStore,
1257
- queue: {
1258
- getCurrentModel: getContextModel,
1259
- loadConfig: configStore.load,
1260
- setQueuedItems: telegramQueueStore.setQueuedItems,
1261
- setCurrentModel: currentModelRuntime.set,
1262
- setPendingModelSwitch: pendingModelSwitchStore.set,
1263
- syncCounters: queue.syncCounters,
1264
- syncFlags: lifecycle.syncFlags,
1265
- bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
1266
- prepareTempDir,
1267
- updateStatus,
1268
- unbindDeferredDispatchContext: deferredQueueDispatchRuntime.unbind,
1269
- discardQueuedItems: queueMutationRuntime.clear,
1270
- clearModelMenuState: modelMenuRuntime.clear,
1271
- getActiveTurnChatId: activeTurnRuntime.getChatId,
1272
- getActiveTurnTarget: activeTurnRuntime.getTarget,
1273
- clearPreview: previewRuntime.clear,
1274
- clearActiveTurn: activeTurnRuntime.clear,
1275
- clearAbort: abort.clearHandler,
1276
- recordRuntimeEvent,
1277
- },
1278
- follower: {
1279
- registrationState: telegramBusFollowerRegistrationState,
1280
- registrationRuntime: telegramBusFollowerRegistration,
1281
- instanceId: telegramInstanceId,
1282
- suspendPolling: lockedPollingRuntime.suspend,
1283
- isLeader: lockRuntime.owns,
1284
- getLeaderBinding: currentInstanceThreadRuntime.getRestorationIdentity,
1285
- getActiveContext: telegramSessionContextStore.get,
1286
- getActiveProfileName: configStore.getActiveProfileName,
1287
- getLeaderState: lockRuntime.getState,
1288
- updateStatus,
1289
- recordRuntimeEvent,
1290
- },
1291
- services: {
1292
- mediaGroup: {
1293
- resume: mediaGroupRuntime.resume,
1294
- suspend: mediaGroupRuntime.suspend,
1295
- },
1296
- textGroup: {
1297
- resume: textGroupRuntime.resume,
1298
- suspend: textGroupRuntime.suspend,
1299
- },
1300
- delivery: deliveryLifecycleRuntime,
1301
- polling: lockedPollingRuntime,
1302
- inboundWorker: {
1303
- onSessionShutdown: updateAdmissionRuntimeBinding.onSessionShutdown,
1304
- },
1305
- capabilityMonitor: telegramThreadCapabilityMonitor,
1306
- queueWatchdog: queueDispatchWatchdogRuntime,
1307
- },
1308
- });
1309
- const sessionLifecycleRuntime =
1310
- Lifecycle.createTelegramBridgeSessionLifecycleAssembly(
1311
- telegramBridgeSessionLifecycleDeps,
1312
- );
1313
-
1314
- // --- Extension API Bindings ---
1315
-
1316
- Bindings.registerTelegramCommandsAndTools({
1317
- pi,
1318
- agentDir: Paths.resolveAgentDir(),
1319
- configStore,
1320
- persistConfig: persistTelegramConfigWithSync,
1321
- setup,
1322
- activeTurnRuntime,
1323
- lockedPollingRuntime,
1324
- stopPolling: disconnectTelegramAndDeleteCurrentThread,
1325
- recoverPollingStart: Recovery.createTelegramPollingStartRecoveryHandler({
1326
- getOwnersPath: Paths.resolveTelegramOwnersPath,
1327
- getStatePaths() {
1328
- return [
1329
- Threads.getTelegramTopicTargetsPath(
1330
- undefined,
1331
- configStore.getActiveProfileName(),
1332
- ),
1333
- ];
1334
- },
1335
- suspendPolling: lockedPollingRuntime.suspend,
1336
- releaseOwnership: lockRuntime.release,
1337
- recordRuntimeEvent,
1338
- }),
1339
- getDisconnectThreadName() {
1340
- const record = findCurrentThreadRecord();
1341
- if (!record?.target.threadId) return undefined;
1342
- return record.threadName ?? "current Telegram thread";
1343
- },
1344
- onTransportChanged() {
1345
- deliveryLifecycleRuntime.onSessionStart();
1346
- activityVerbosityRuntime.reset();
1347
- modelContextAvailabilityRuntime.reconcile();
1348
- },
1349
- getStatusLines,
1350
- buttonActionStore,
1351
- sendMarkdownReply,
1352
- callMultipart,
1353
- getDefaultChatId: proactivePushChatIdGetter,
1354
- getDefaultTarget: proactivePushTargetGetter,
1355
- ...agentMessageToolRoutingRuntime,
1356
- updateStatus,
1357
- recordRuntimeEvent,
1358
- });
1359
-
1360
- // --- Lifecycle Hooks ---
1361
-
1362
- Bindings.registerTelegramLifecycleRuntimeHooks({
1363
- pi,
1364
- sessionLifecycleRuntime: {
1365
- ...sessionLifecycleRuntime,
1366
- onModelSelect: currentModelRuntime.onModelSelect,
1367
- },
1368
- activityRuntime,
1369
- activityVerbosityRuntime,
1370
- assistantOutputRuntime,
1371
- publicationRuntime,
1372
- configStore,
1373
- abort,
1374
- typing,
1375
- lifecycle,
1376
- activeTurnRuntime,
1377
- telegramQueueStore,
1378
- modelSwitchController,
1379
- previewRuntime,
1380
- promptDispatchRuntime,
1381
- deferredQueueDispatchRuntime,
1382
- modelContextAvailabilityRuntime,
1383
- disconnectOnQuit: cleanupTelegramThreadForSessionRestart,
1384
- resolveAutomaticThreadCleanupEnabled:
1385
- configControls.resolveAutomaticThreadCleanupEnabled,
1386
- buttonActionStore,
1387
- callMultipart,
1388
- sendChatAction,
1389
- sendRecordVoiceAction,
1390
- sendMarkdownReply,
1391
- sendTextReply,
1392
- dispatchNextQueuedTelegramTurn,
1393
- onPromptHandedOff(turn, ctx) {
1394
- updateAdmissionRuntimeBinding
1395
- .getSettlement()
1396
- ?.onPromptHandedOff(turn, ctx);
1397
- },
1398
- answerGuestQuery,
1399
- deleteMessage: deleteTelegramMessage,
1400
- sendGuestReply,
1401
- finalizeMarkdownPreview,
1402
- preparePreviewDelivery,
1403
- proactivePushTargetGetter,
1404
- getAssistantRenderingMode: configControls.getAssistantRenderingMode,
1405
- recordMessageOwnership: messageOwnershipRuntime.recordLocal,
1406
- canSendAgentActivity(ctx) {
1407
- return (
1408
- lockOwnershipGuard.ownsContext(ctx) ||
1409
- telegramBusFollowerRegistrationState.isRegistered()
1410
- );
1411
- },
1412
- isSessionContextActive(ctx) {
1413
- return telegramSessionContextStore.isCurrent(ctx);
1414
- },
1415
- isTurnTransportActive(turn) {
1416
- return telegramTransportStampRuntime.isActive(turn.transportStamp);
1417
- },
1418
- updateStatus,
1419
- recordRuntimeEvent,
1420
- });
1421
- }
7
+ export { default } from "./lib/extension.ts";