@llblab/pi-telegram 0.17.4 → 0.18.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/AGENTS.md +67 -32
- package/BACKLOG.md +59 -14
- package/CHANGELOG.md +40 -15
- package/README.md +63 -35
- package/docs/README.md +3 -1
- package/docs/architecture.md +55 -23
- package/docs/callback-namespaces.md +1 -1
- package/docs/inbound.md +1 -1
- package/docs/locks.md +0 -2
- package/docs/multi-instance-bus.md +483 -0
- package/docs/outbound.md +4 -3
- package/docs/public-api.md +12 -10
- package/docs/sections.md +2 -2
- package/docs/ui-style.md +76 -0
- package/index.ts +789 -32
- package/lib/bindings.ts +68 -12
- package/lib/bus-api.ts +314 -0
- package/lib/bus-follower.ts +853 -0
- package/lib/bus-leader.ts +915 -0
- package/lib/bus.ts +866 -0
- package/lib/command-templates.ts +9 -11
- package/lib/commands.ts +133 -47
- package/lib/config.ts +53 -5
- package/lib/lifecycle.ts +23 -7
- package/lib/locks.ts +230 -66
- package/lib/media.ts +30 -2
- package/lib/menu-model.ts +48 -17
- package/lib/menu-queue.ts +51 -20
- package/lib/menu-settings.ts +9 -5
- package/lib/menu-status.ts +3 -0
- package/lib/menu-thinking.ts +3 -0
- package/lib/menu.ts +67 -26
- package/lib/outbound-attachments.ts +102 -17
- package/lib/outbound-buttons.ts +6 -2
- package/lib/outbound-voice.ts +31 -11
- package/lib/outbound.ts +6 -4
- package/lib/ownership.ts +119 -0
- package/lib/pi.ts +26 -3
- package/lib/polling.ts +477 -7
- package/lib/preview.ts +141 -88
- package/lib/prompt-templates.ts +3 -3
- package/lib/prompts.ts +80 -30
- package/lib/queue.ts +193 -91
- package/lib/rendering.ts +0 -25
- package/lib/replies.ts +187 -55
- package/lib/routing.ts +1673 -9
- package/lib/runtime-log.ts +123 -0
- package/lib/runtime.ts +84 -12
- package/lib/sections.ts +28 -21
- package/lib/setup.ts +1 -1
- package/lib/status.ts +532 -9
- package/lib/sync.ts +618 -0
- package/lib/target.ts +49 -0
- package/lib/telegram-api.ts +405 -40
- package/lib/text-groups.ts +5 -1
- package/lib/thread-reconciler.ts +915 -0
- package/lib/threads.ts +2205 -0
- package/lib/turns.ts +48 -3
- package/lib/updates.ts +355 -32
- package/package.json +24 -2
- package/screenshot.png +0 -0
- package/docs/telegram-bot-api-rich-messages.md +0 -890
package/index.ts
CHANGED
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as Bindings from "./lib/bindings.ts";
|
|
8
|
+
import * as BusApi from "./lib/bus-api.ts";
|
|
9
|
+
import * as Bus from "./lib/bus.ts";
|
|
10
|
+
import * as BusFollower from "./lib/bus-follower.ts";
|
|
11
|
+
import * as BusLeader from "./lib/bus-leader.ts";
|
|
8
12
|
import * as CommandTemplates from "./lib/command-templates.ts";
|
|
9
13
|
import * as Commands from "./lib/commands.ts";
|
|
10
14
|
import * as Config from "./lib/config.ts";
|
|
15
|
+
import * as Threads from "./lib/threads.ts";
|
|
11
16
|
import * as Inbound from "./lib/inbound.ts";
|
|
12
17
|
import * as Lifecycle from "./lib/lifecycle.ts";
|
|
13
18
|
import * as Locks from "./lib/locks.ts";
|
|
@@ -17,6 +22,7 @@ import * as MenuSettings from "./lib/menu-settings.ts";
|
|
|
17
22
|
import * as Menu from "./lib/menu.ts";
|
|
18
23
|
import * as Model from "./lib/model.ts";
|
|
19
24
|
import * as Outbound from "./lib/outbound.ts";
|
|
25
|
+
import * as Ownership from "./lib/ownership.ts";
|
|
20
26
|
import * as Pi from "./lib/pi.ts";
|
|
21
27
|
import * as Polling from "./lib/polling.ts";
|
|
22
28
|
import * as Preview from "./lib/preview.ts";
|
|
@@ -25,10 +31,13 @@ import * as Queue from "./lib/queue.ts";
|
|
|
25
31
|
import * as Replies from "./lib/replies.ts";
|
|
26
32
|
import * as Routing from "./lib/routing.ts";
|
|
27
33
|
import * as Runtime from "./lib/runtime.ts";
|
|
34
|
+
import * as RuntimeLog from "./lib/runtime-log.ts";
|
|
28
35
|
import * as Sections from "./lib/sections.ts";
|
|
29
36
|
import * as Status from "./lib/status.ts";
|
|
37
|
+
import * as Sync from "./lib/sync.ts";
|
|
30
38
|
import * as TelegramApi from "./lib/telegram-api.ts";
|
|
31
39
|
import * as TextGroups from "./lib/text-groups.ts";
|
|
40
|
+
import * as ThreadReconciler from "./lib/thread-reconciler.ts";
|
|
32
41
|
import * as TimeInjection from "./lib/time-injection.ts";
|
|
33
42
|
import * as Updates from "./lib/updates.ts";
|
|
34
43
|
import * as Voice from "./lib/voice.ts";
|
|
@@ -47,6 +56,26 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
47
56
|
setThinkingLevel,
|
|
48
57
|
} = piRuntime;
|
|
49
58
|
const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
|
|
59
|
+
const telegramInstanceId = `${process.pid}:${Date.now()}`;
|
|
60
|
+
const telegramManualFollowerOwnerId = String(process.pid);
|
|
61
|
+
const telegramManualFollowerProfileKey = `manual:${telegramManualFollowerOwnerId}`;
|
|
62
|
+
const telegramBusAuthSecret = Bus.createTelegramBusAuthSecret();
|
|
63
|
+
let telegramActiveBusAuthSecret: string | undefined;
|
|
64
|
+
let telegramBusLifecycleOverridePhase:
|
|
65
|
+
| Status.TelegramBridgeBusLifecyclePhase
|
|
66
|
+
| undefined;
|
|
67
|
+
const telegramBusSocketPath = Bus.getTelegramBusSocketPath();
|
|
68
|
+
const telegramBusFollowerSocketPath =
|
|
69
|
+
Bus.getTelegramBusFollowerSocketPath(telegramInstanceId);
|
|
70
|
+
let telegramBusRequestSequence = 0;
|
|
71
|
+
const telegramBusFollowerRegistry = Bus.createTelegramBusFollowerRegistry();
|
|
72
|
+
const telegramBusFollowerRegistrationState =
|
|
73
|
+
BusFollower.createTelegramBusFollowerRegistrationState();
|
|
74
|
+
let telegramBusLeaderTarget: Config.TelegramProactivePushTarget | undefined;
|
|
75
|
+
let telegramBusLeaderSlot: string | undefined;
|
|
76
|
+
let telegramTopicModeUnavailable = false;
|
|
77
|
+
let telegramTopicProvisioningCount = 0;
|
|
78
|
+
const messageOwnershipStore = Ownership.createTelegramMessageOwnershipStore();
|
|
50
79
|
const { abort, lifecycle, queue, setup, typing } = bridgeRuntime;
|
|
51
80
|
let configStoreForRedaction: Config.TelegramConfigStore | undefined;
|
|
52
81
|
const runtimeEvents = Status.createTelegramRuntimeEventRecorder({
|
|
@@ -54,14 +83,46 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
54
83
|
return configStoreForRedaction?.getBotToken();
|
|
55
84
|
},
|
|
56
85
|
});
|
|
57
|
-
const
|
|
86
|
+
const runtimeJsonlLog = RuntimeLog.createTelegramRuntimeJsonlLog();
|
|
87
|
+
runtimeJsonlLog.reset("extension-start", {
|
|
88
|
+
instanceId: telegramInstanceId,
|
|
89
|
+
pid: process.pid,
|
|
90
|
+
});
|
|
91
|
+
let scheduleRuntimeDiagnosticsSnapshotPersist = function () {};
|
|
92
|
+
const recordRuntimeEvent = function (
|
|
93
|
+
category: string,
|
|
94
|
+
error: unknown,
|
|
95
|
+
details?: Record<string, unknown>,
|
|
96
|
+
) {
|
|
97
|
+
runtimeEvents.record(category, error, details);
|
|
98
|
+
const latestEvent = runtimeEvents.getEvents().at(-1);
|
|
99
|
+
if (latestEvent) runtimeJsonlLog.record(latestEvent);
|
|
100
|
+
scheduleRuntimeDiagnosticsSnapshotPersist();
|
|
101
|
+
};
|
|
58
102
|
const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
|
|
59
103
|
configStoreForRedaction = configStore;
|
|
104
|
+
const isTelegramBusConfigured = function (): boolean {
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
107
|
+
const isTelegramBusRuntimeEnabled = function (): boolean {
|
|
108
|
+
return isTelegramBusConfigured() && !telegramTopicModeUnavailable;
|
|
109
|
+
};
|
|
60
110
|
Config.bindGlobalTelegramConfigRuntime(configStore);
|
|
61
111
|
const configControls = Config.createTelegramConfigControls(configStore);
|
|
62
|
-
const
|
|
112
|
+
const threadStore = Threads.createTelegramTopicTargetStore({
|
|
113
|
+
path: Threads.getTelegramTopicTargetsPath(),
|
|
114
|
+
});
|
|
115
|
+
const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
|
|
116
|
+
instanceId: telegramInstanceId,
|
|
117
|
+
busSecret: telegramBusAuthSecret,
|
|
118
|
+
staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
|
|
119
|
+
});
|
|
63
120
|
const lockOwnershipGuard =
|
|
64
121
|
Locks.createTelegramLockOwnershipGuard(lockRuntime);
|
|
122
|
+
const getCurrentLeaderEpoch = function (): number | undefined {
|
|
123
|
+
const state = lockRuntime.getState();
|
|
124
|
+
return state.kind === "active-here" ? state.lock.leaderEpoch : undefined;
|
|
125
|
+
};
|
|
65
126
|
const telegramSessionContextStore =
|
|
66
127
|
Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>();
|
|
67
128
|
const ownsTelegramDirectDelivery =
|
|
@@ -75,6 +136,17 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
75
136
|
getActiveTurnChatId: activeTurnRuntime.getChatId,
|
|
76
137
|
getAllowedUserId: configStore.getAllowedUserId,
|
|
77
138
|
});
|
|
139
|
+
const proactivePushTargetGetter =
|
|
140
|
+
Config.createTelegramProactivePushTargetGetter({
|
|
141
|
+
getActiveTurnTarget: activeTurnRuntime.getTarget,
|
|
142
|
+
getAssignedTarget() {
|
|
143
|
+
return (
|
|
144
|
+
telegramBusFollowerRegistrationState.getTarget() ??
|
|
145
|
+
telegramBusLeaderTarget
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
getAllowedUserId: configStore.getAllowedUserId,
|
|
149
|
+
});
|
|
78
150
|
const buttonActionStore = Outbound.createTelegramButtonActionStore();
|
|
79
151
|
const pendingModelSwitchStore =
|
|
80
152
|
Model.createPendingModelSwitchStore<
|
|
@@ -108,26 +180,151 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
108
180
|
recordRuntimeEvent,
|
|
109
181
|
});
|
|
110
182
|
const pollingControllerState = Polling.createTelegramPollingControllerState();
|
|
183
|
+
let telegramSyncState = Sync.createUnknownTelegramSyncState();
|
|
184
|
+
const threadReconciliationRuntime =
|
|
185
|
+
ThreadReconciler.createThreadReconciliationRuntime({
|
|
186
|
+
recordRuntimeEvent,
|
|
187
|
+
scheduleSnapshotPersist() {
|
|
188
|
+
scheduleRuntimeDiagnosticsSnapshotPersist();
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
const recordThreadReconciliationPlan = threadReconciliationRuntime.recordPlan;
|
|
192
|
+
const markTelegramConfigSyncChange = function (action: string) {
|
|
193
|
+
telegramSyncState = Sync.markTelegramConfigSyncChange(
|
|
194
|
+
telegramSyncState,
|
|
195
|
+
action,
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
const persistTelegramConfigWithSync = async function () {
|
|
199
|
+
await configStore.persist();
|
|
200
|
+
markTelegramConfigSyncChange("config-persist");
|
|
201
|
+
};
|
|
202
|
+
const getCurrentThreadRecord = function ():
|
|
203
|
+
| Threads.TelegramTopicTargetRecord
|
|
204
|
+
| undefined {
|
|
205
|
+
return Threads.findCurrentTelegramInstanceThreadRecord({
|
|
206
|
+
records: threadStore.list(),
|
|
207
|
+
instanceId: telegramInstanceId,
|
|
208
|
+
preferredTarget:
|
|
209
|
+
activeTurnRuntime.getTarget() ??
|
|
210
|
+
telegramBusFollowerRegistrationState.getTarget() ??
|
|
211
|
+
telegramBusLeaderTarget,
|
|
212
|
+
});
|
|
213
|
+
};
|
|
111
214
|
const statusRuntime = Status.createTelegramBridgeStatusRuntime<
|
|
112
215
|
Pi.ExtensionContext,
|
|
113
216
|
Queue.TelegramQueueItem<Pi.ExtensionContext>
|
|
114
217
|
>({
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
218
|
+
getConfig: configStore.get,
|
|
219
|
+
isPollingActive: Polling.createTelegramPollingActivityReader(
|
|
220
|
+
pollingControllerState,
|
|
221
|
+
),
|
|
222
|
+
getActiveSourceMessageIds: activeTurnRuntime.getSourceMessageIds,
|
|
223
|
+
hasActiveTurn: activeTurnRuntime.has,
|
|
224
|
+
hasDispatchPending: lifecycle.hasDispatchPending,
|
|
225
|
+
isCompactionInProgress: lifecycle.isCompactionInProgress,
|
|
226
|
+
getActiveToolExecutions: lifecycle.getActiveToolExecutions,
|
|
227
|
+
hasPendingModelSwitch: pendingModelSwitchStore.has,
|
|
228
|
+
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
229
|
+
formatQueuedStatus: Queue.formatQueuedTelegramItemsStatus,
|
|
230
|
+
getRecentRuntimeEvents: runtimeEvents.getEvents,
|
|
231
|
+
getRuntimeLockState: lockRuntime.getStatusLabel,
|
|
232
|
+
getBusRole() {
|
|
233
|
+
if (threadStore.getBotState().threadMode === "disabled") return undefined;
|
|
234
|
+
if (pollingStartedWithTelegramBus) return "leader";
|
|
235
|
+
if (telegramBusFollowerRegistrationState.isRegistered())
|
|
236
|
+
return "follower";
|
|
237
|
+
return undefined;
|
|
238
|
+
},
|
|
239
|
+
getBusLifecyclePhase() {
|
|
240
|
+
return telegramBusLifecycleOverridePhase;
|
|
241
|
+
},
|
|
242
|
+
getBotThreadMode() {
|
|
243
|
+
return threadStore.getBotState();
|
|
244
|
+
},
|
|
245
|
+
getBusFollowers() {
|
|
246
|
+
return Threads.listTelegramThreadStatusFollowers({
|
|
247
|
+
followers: telegramBusFollowerRegistry.list(),
|
|
248
|
+
records: threadStore.list(),
|
|
249
|
+
});
|
|
250
|
+
},
|
|
251
|
+
getTopicTargets() {
|
|
252
|
+
return Threads.listTelegramThreadStatusTargets(threadStore.list());
|
|
253
|
+
},
|
|
254
|
+
getThreadReservations() {
|
|
255
|
+
return Threads.listTelegramThreadStatusReservations(
|
|
256
|
+
threadStore.listReservations(),
|
|
257
|
+
);
|
|
258
|
+
},
|
|
259
|
+
getTopicSyncObservations() {
|
|
260
|
+
return Threads.listTelegramThreadStatusObservations(
|
|
261
|
+
threadStore.listSyncObservations(),
|
|
262
|
+
);
|
|
263
|
+
},
|
|
264
|
+
getSyncState() {
|
|
265
|
+
return telegramSyncState;
|
|
266
|
+
},
|
|
267
|
+
getThreadReconciliationState() {
|
|
268
|
+
return threadReconciliationRuntime.getState();
|
|
269
|
+
},
|
|
270
|
+
getInstanceSlot() {
|
|
271
|
+
if (threadStore.getBotState().threadMode === "disabled") return undefined;
|
|
272
|
+
return (
|
|
273
|
+
getCurrentThreadRecord()?.slot ??
|
|
274
|
+
telegramBusFollowerRegistrationState.getSlot() ??
|
|
275
|
+
telegramBusLeaderSlot
|
|
276
|
+
);
|
|
277
|
+
},
|
|
278
|
+
getInstanceThreadName() {
|
|
279
|
+
if (threadStore.getBotState().threadMode === "disabled") return undefined;
|
|
280
|
+
return (
|
|
281
|
+
getCurrentThreadRecord()?.threadName ??
|
|
282
|
+
telegramBusFollowerRegistrationState.getThreadName()
|
|
283
|
+
);
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const { updateStatus: updateStatusLine } = statusRuntime;
|
|
287
|
+
let lastRuntimeLogScopeKey: string | undefined;
|
|
288
|
+
const updateRuntimeLogScope = function (reason: string) {
|
|
289
|
+
const scope = Status.createTelegramRuntimeLogScope({
|
|
290
|
+
state: statusRuntime.getStatusState(),
|
|
291
|
+
instanceId: telegramInstanceId,
|
|
292
|
+
});
|
|
293
|
+
const scopeKey = JSON.stringify(scope);
|
|
294
|
+
if (scopeKey === lastRuntimeLogScopeKey) return;
|
|
295
|
+
lastRuntimeLogScopeKey = scopeKey;
|
|
296
|
+
runtimeJsonlLog.resetIfScopeChanged(scopeKey, reason, scope);
|
|
297
|
+
};
|
|
298
|
+
const updateStatus = function (ctx: Pi.ExtensionContext) {
|
|
299
|
+
updateStatusLine(ctx);
|
|
300
|
+
updateRuntimeLogScope("status-scope-change");
|
|
301
|
+
};
|
|
302
|
+
const persistCurrentStatusSnapshot = function () {
|
|
303
|
+
threadStore.setStatusSnapshot(
|
|
304
|
+
Status.createTelegramStatusSnapshot(statusRuntime.getStatusState()),
|
|
305
|
+
);
|
|
306
|
+
return threadStore.persist();
|
|
307
|
+
};
|
|
308
|
+
scheduleRuntimeDiagnosticsSnapshotPersist =
|
|
309
|
+
Status.createTelegramRuntimeDiagnosticsSnapshotScheduler({
|
|
310
|
+
persistSnapshot: persistCurrentStatusSnapshot,
|
|
311
|
+
recordError(error) {
|
|
312
|
+
runtimeEvents.record("telegram", error, {
|
|
313
|
+
phase: "runtime-diagnostics-snapshot-persist",
|
|
314
|
+
});
|
|
315
|
+
},
|
|
129
316
|
});
|
|
130
|
-
const
|
|
317
|
+
const getStatusLines = function (
|
|
318
|
+
options?: Status.TelegramBridgeStatusLineOptions,
|
|
319
|
+
): string[] {
|
|
320
|
+
const state = statusRuntime.getStatusState();
|
|
321
|
+
void persistCurrentStatusSnapshot().catch(function (error) {
|
|
322
|
+
recordRuntimeEvent("telegram", error, {
|
|
323
|
+
phase: "status-snapshot-persist",
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
return Status.buildTelegramBridgeStatusLines(state, options);
|
|
327
|
+
};
|
|
131
328
|
const inboundHandlerRuntime = Inbound.createTelegramInboundHandlerRuntime({
|
|
132
329
|
getHandlers: configStore.getInboundHandlers,
|
|
133
330
|
execCommand: CommandTemplates.execCommandTemplate,
|
|
@@ -137,7 +334,37 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
137
334
|
|
|
138
335
|
// --- Telegram API ---
|
|
139
336
|
|
|
337
|
+
const directTelegramApiRuntime =
|
|
338
|
+
TelegramApi.createDefaultTelegramBridgeApiRuntime({
|
|
339
|
+
getBotToken: configStore.getBotToken,
|
|
340
|
+
recordRuntimeEvent,
|
|
341
|
+
});
|
|
342
|
+
const telegramApiRuntime = BusApi.createTelegramBusAwareApiRuntime({
|
|
343
|
+
directRuntime: directTelegramApiRuntime,
|
|
344
|
+
ownsDirect() {
|
|
345
|
+
return (
|
|
346
|
+
lockRuntime.owns() ||
|
|
347
|
+
!telegramBusFollowerRegistrationState.isRegistered()
|
|
348
|
+
);
|
|
349
|
+
},
|
|
350
|
+
getDefaultTarget: proactivePushTargetGetter,
|
|
351
|
+
callFollowerApi: BusFollower.createTelegramBusFollowerApiCaller({
|
|
352
|
+
socketPath: telegramBusSocketPath,
|
|
353
|
+
instanceId: telegramInstanceId,
|
|
354
|
+
createRequestId() {
|
|
355
|
+
telegramBusRequestSequence += 1;
|
|
356
|
+
return Bus.createTelegramBusRequestId({
|
|
357
|
+
instanceId: telegramInstanceId,
|
|
358
|
+
sequence: telegramBusRequestSequence,
|
|
359
|
+
});
|
|
360
|
+
},
|
|
361
|
+
getAuthSecret() {
|
|
362
|
+
return telegramActiveBusAuthSecret;
|
|
363
|
+
},
|
|
364
|
+
}),
|
|
365
|
+
});
|
|
140
366
|
const {
|
|
367
|
+
call: callTelegramApi,
|
|
141
368
|
callMultipart,
|
|
142
369
|
deleteWebhook,
|
|
143
370
|
getUpdates,
|
|
@@ -155,10 +382,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
155
382
|
answerGuestQuery,
|
|
156
383
|
deleteMessage: deleteTelegramMessage,
|
|
157
384
|
prepareTempDir,
|
|
158
|
-
} =
|
|
159
|
-
getBotToken: configStore.getBotToken,
|
|
160
|
-
recordRuntimeEvent,
|
|
161
|
-
});
|
|
385
|
+
} = telegramApiRuntime;
|
|
162
386
|
|
|
163
387
|
// --- Message Delivery ---
|
|
164
388
|
|
|
@@ -171,6 +395,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
171
395
|
typing,
|
|
172
396
|
getDefaultChatId: proactivePushChatIdGetter,
|
|
173
397
|
sendTypingAction,
|
|
398
|
+
sendAggregateTypingAction:
|
|
399
|
+
BusApi.createTelegramAggregateTypingActionSender(telegramApiRuntime),
|
|
174
400
|
updateStatus,
|
|
175
401
|
recordRuntimeEvent,
|
|
176
402
|
});
|
|
@@ -189,6 +415,14 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
189
415
|
// --- Reply Runtime & Preview ---
|
|
190
416
|
|
|
191
417
|
const replyRuntime = Replies.createTelegramRenderedMessageDeliveryRuntime({
|
|
418
|
+
recordOwnership(input) {
|
|
419
|
+
messageOwnershipStore.record({
|
|
420
|
+
chatId: input.chatId,
|
|
421
|
+
messageId: input.messageId,
|
|
422
|
+
target: input.target,
|
|
423
|
+
instanceId: telegramInstanceId,
|
|
424
|
+
});
|
|
425
|
+
},
|
|
192
426
|
sendMessage,
|
|
193
427
|
sendRichMessage,
|
|
194
428
|
editMessage: editTelegramMessageText,
|
|
@@ -218,15 +452,17 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
218
452
|
...promptDispatchRuntime,
|
|
219
453
|
sendUserMessage,
|
|
220
454
|
}).dispatchNext;
|
|
455
|
+
const nativeMarkdownDraftSender =
|
|
456
|
+
TelegramApi.createTelegramNativeMarkdownDraftSender({
|
|
457
|
+
sendMessageDraft,
|
|
458
|
+
sendRichMessageDraft,
|
|
459
|
+
});
|
|
221
460
|
const previewRuntime = Preview.createTelegramAssistantPreviewRuntime({
|
|
222
461
|
getActiveTurn: activeTurnRuntime.get,
|
|
223
462
|
isAssistantMessage: Replies.isAssistantAgentMessage,
|
|
224
463
|
getMessageText: Replies.getAgentMessageText,
|
|
225
464
|
getDefaultReplyToMessageId: activeTurnRuntime.getReplyToMessageId,
|
|
226
|
-
sendDraft:
|
|
227
|
-
sendMessageDraft,
|
|
228
|
-
sendRichMessageDraft,
|
|
229
|
-
}),
|
|
465
|
+
sendDraft: nativeMarkdownDraftSender,
|
|
230
466
|
sendMarkdownReply,
|
|
231
467
|
recordRuntimeEvent,
|
|
232
468
|
...replyTransport,
|
|
@@ -272,6 +508,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
272
508
|
buildStatusHtml: Status.createTelegramStatusHtmlBuilder({
|
|
273
509
|
getActiveModel: currentModelRuntime.get,
|
|
274
510
|
isCompactionInProgress: lifecycle.isCompactionInProgress,
|
|
511
|
+
getBridgeStatusLineState: statusRuntime.getStatusState,
|
|
275
512
|
}),
|
|
276
513
|
getPromptTemplateCommands,
|
|
277
514
|
}),
|
|
@@ -324,8 +561,103 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
324
561
|
|
|
325
562
|
// --- Polling ---
|
|
326
563
|
|
|
564
|
+
const foreignOwnedUpdateForwarder =
|
|
565
|
+
Bus.createTelegramBusForeignOwnedUpdateForwarder<
|
|
566
|
+
Pi.ExtensionContext,
|
|
567
|
+
Updates.TelegramMessageReactionUpdated,
|
|
568
|
+
Routing.TelegramRoutedCallbackQuery,
|
|
569
|
+
Routing.TelegramRoutedMessage
|
|
570
|
+
>({
|
|
571
|
+
socketPath: telegramBusSocketPath,
|
|
572
|
+
createRequestId() {
|
|
573
|
+
telegramBusRequestSequence += 1;
|
|
574
|
+
return Bus.createTelegramBusRequestId({
|
|
575
|
+
instanceId: telegramInstanceId,
|
|
576
|
+
sequence: telegramBusRequestSequence,
|
|
577
|
+
});
|
|
578
|
+
},
|
|
579
|
+
getAuthSecret() {
|
|
580
|
+
return telegramBusAuthSecret;
|
|
581
|
+
},
|
|
582
|
+
timeoutMs: 30_000,
|
|
583
|
+
});
|
|
584
|
+
const followerTargetController =
|
|
585
|
+
Bus.createTelegramBusFollowerTargetController({
|
|
586
|
+
socketPath: telegramBusSocketPath,
|
|
587
|
+
createRequestId() {
|
|
588
|
+
telegramBusRequestSequence += 1;
|
|
589
|
+
return Bus.createTelegramBusRequestId({
|
|
590
|
+
instanceId: telegramInstanceId,
|
|
591
|
+
sequence: telegramBusRequestSequence,
|
|
592
|
+
});
|
|
593
|
+
},
|
|
594
|
+
getAuthSecret() {
|
|
595
|
+
return telegramBusAuthSecret;
|
|
596
|
+
},
|
|
597
|
+
timeoutMs: 30_000,
|
|
598
|
+
});
|
|
599
|
+
const restoreFollowerThreadTarget =
|
|
600
|
+
Bus.createTelegramBusFollowerThreadRestoreHandler({
|
|
601
|
+
followerRegistry: telegramBusFollowerRegistry,
|
|
602
|
+
followerTargetController,
|
|
603
|
+
onRestored() {
|
|
604
|
+
telegramSyncState = Sync.markTelegramSyncSliceFresh(
|
|
605
|
+
telegramSyncState,
|
|
606
|
+
"target-bindings",
|
|
607
|
+
{ nowMs: Date.now(), action: "follower-thread-restore" },
|
|
608
|
+
);
|
|
609
|
+
},
|
|
610
|
+
});
|
|
611
|
+
let observeTelegramThreadTarget: Polling.TelegramThreadTargetObservationHandler<
|
|
612
|
+
Pi.ExtensionContext
|
|
613
|
+
> = async function noopTelegramThreadTargetObservation() {};
|
|
614
|
+
const topicLifecycleSync = Sync.createTelegramObservedTopicLifecycleSyncHandler({
|
|
615
|
+
topicTargetStore: threadStore,
|
|
616
|
+
isBusEnabled: isTelegramBusRuntimeEnabled,
|
|
617
|
+
callApi: callTelegramApi,
|
|
618
|
+
isTopicProvisioningActive() {
|
|
619
|
+
return telegramTopicProvisioningCount > 0;
|
|
620
|
+
},
|
|
621
|
+
getCurrentLeaderEpoch,
|
|
622
|
+
getThreadReconciliationMachineState() {
|
|
623
|
+
return threadReconciliationRuntime.getState();
|
|
624
|
+
},
|
|
625
|
+
recordThreadReconciliationPlan,
|
|
626
|
+
getSyncState() {
|
|
627
|
+
return telegramSyncState;
|
|
628
|
+
},
|
|
629
|
+
setSyncState(state) {
|
|
630
|
+
telegramSyncState = state;
|
|
631
|
+
},
|
|
632
|
+
recordEvent: recordRuntimeEvent,
|
|
633
|
+
});
|
|
327
634
|
const inboundRouteRuntime = Routing.createTelegramInboundRouteRuntime({
|
|
328
635
|
configStore,
|
|
636
|
+
callApi: callTelegramApi,
|
|
637
|
+
getCurrentInstanceId() {
|
|
638
|
+
return telegramInstanceId;
|
|
639
|
+
},
|
|
640
|
+
getMessageOwnership: messageOwnershipStore.get,
|
|
641
|
+
getTargetOwnership(target) {
|
|
642
|
+
return telegramBusFollowerRegistry.getByTarget(target);
|
|
643
|
+
},
|
|
644
|
+
getLiveThreadTargets() {
|
|
645
|
+
return Bus.listTelegramBusLiveThreadTargets({
|
|
646
|
+
leaderTarget: telegramBusLeaderTarget,
|
|
647
|
+
followers: telegramBusFollowerRegistry.list(),
|
|
648
|
+
});
|
|
649
|
+
},
|
|
650
|
+
getCurrentLeaderEpoch,
|
|
651
|
+
getThreadReconciliationMachineState() {
|
|
652
|
+
return threadReconciliationRuntime.getState();
|
|
653
|
+
},
|
|
654
|
+
recordThreadReconciliationPlan,
|
|
655
|
+
handleTelegramTopicLifecycleUpdate: topicLifecycleSync,
|
|
656
|
+
handleTelegramThreadTargetObserved(_target, ctx) {
|
|
657
|
+
return observeTelegramThreadTarget(ctx);
|
|
658
|
+
},
|
|
659
|
+
foreignOwnedUpdateForwarder,
|
|
660
|
+
replaceFollowerThreadTarget: restoreFollowerThreadTarget,
|
|
329
661
|
bridgeRuntime,
|
|
330
662
|
activeTurnRuntime,
|
|
331
663
|
mediaGroupRuntime,
|
|
@@ -344,6 +676,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
344
676
|
sectionRegistry,
|
|
345
677
|
buttonActionStore,
|
|
346
678
|
inboundHandlerRuntime,
|
|
679
|
+
threadStore,
|
|
347
680
|
updateStatus,
|
|
348
681
|
dispatchNextQueuedTelegramTurn,
|
|
349
682
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
@@ -373,13 +706,123 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
373
706
|
compact,
|
|
374
707
|
recordRuntimeEvent,
|
|
375
708
|
});
|
|
709
|
+
const telegramBusForwardedUpdateReceiver =
|
|
710
|
+
BusFollower.createTelegramBusForwardedUpdateReceiverRuntime<
|
|
711
|
+
Pi.ExtensionContext,
|
|
712
|
+
Updates.TelegramMessageReactionUpdated,
|
|
713
|
+
Routing.TelegramRoutedCallbackQuery,
|
|
714
|
+
Routing.TelegramRoutedMessage
|
|
715
|
+
>({
|
|
716
|
+
socketPath: telegramBusFollowerSocketPath,
|
|
717
|
+
instanceId: telegramInstanceId,
|
|
718
|
+
getContext() {
|
|
719
|
+
return telegramSessionContextStore.get();
|
|
720
|
+
},
|
|
721
|
+
getAuthSecret() {
|
|
722
|
+
return telegramActiveBusAuthSecret;
|
|
723
|
+
},
|
|
724
|
+
handleForwardedCallback(query, ctx) {
|
|
725
|
+
return inboundRouteRuntime.handleUpdate({ callback_query: query }, ctx);
|
|
726
|
+
},
|
|
727
|
+
handleForwardedReaction(reactionUpdate, ctx) {
|
|
728
|
+
return inboundRouteRuntime.handleAuthorizedReactionUpdate(
|
|
729
|
+
reactionUpdate,
|
|
730
|
+
ctx,
|
|
731
|
+
);
|
|
732
|
+
},
|
|
733
|
+
handleForwardedMessage(message, ctx) {
|
|
734
|
+
return inboundRouteRuntime.handleUpdate(
|
|
735
|
+
{ message: message as never },
|
|
736
|
+
ctx,
|
|
737
|
+
);
|
|
738
|
+
},
|
|
739
|
+
async handleForwardedEditedMessage(message, ctx) {
|
|
740
|
+
return inboundRouteRuntime.handleUpdate(
|
|
741
|
+
{ edited_message: message as never },
|
|
742
|
+
ctx,
|
|
743
|
+
);
|
|
744
|
+
},
|
|
745
|
+
handleReplaceTarget:
|
|
746
|
+
BusFollower.createTelegramBusFollowerTargetReplacementHandler({
|
|
747
|
+
topicTargetStore: threadStore,
|
|
748
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
749
|
+
instanceId: telegramInstanceId,
|
|
750
|
+
manualFollowerProfileKey: telegramManualFollowerProfileKey,
|
|
751
|
+
manualFollowerOwnerId: telegramManualFollowerOwnerId,
|
|
752
|
+
getSyncState() {
|
|
753
|
+
return telegramSyncState;
|
|
754
|
+
},
|
|
755
|
+
setSyncState(state) {
|
|
756
|
+
telegramSyncState = state;
|
|
757
|
+
},
|
|
758
|
+
updateStatus,
|
|
759
|
+
recordRuntimeEvent,
|
|
760
|
+
}),
|
|
761
|
+
recordRuntimeEvent,
|
|
762
|
+
});
|
|
763
|
+
let telegramBusFollowerRegistration: BusFollower.TelegramBusFollowerRegistrationRuntime<Pi.ExtensionContext>;
|
|
764
|
+
const telegramFollowerHeartbeatRecovery =
|
|
765
|
+
BusFollower.createTelegramBusFollowerHeartbeatRecoveryHandler<Pi.ExtensionContext>(
|
|
766
|
+
{
|
|
767
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
768
|
+
getRegistrationRuntime() {
|
|
769
|
+
return telegramBusFollowerRegistration;
|
|
770
|
+
},
|
|
771
|
+
getLeaderState() {
|
|
772
|
+
return lockRuntime.getState();
|
|
773
|
+
},
|
|
774
|
+
setLifecyclePhase(phase) {
|
|
775
|
+
telegramBusLifecycleOverridePhase = phase;
|
|
776
|
+
},
|
|
777
|
+
updateStatus,
|
|
778
|
+
async promoteToLeader(ctx) {
|
|
779
|
+
await lockedPollingRuntime.start(ctx, { force: true });
|
|
780
|
+
},
|
|
781
|
+
sleep(ms) {
|
|
782
|
+
return Polling.sleepTelegramPollingRetry(ms);
|
|
783
|
+
},
|
|
784
|
+
promotionGraceMs: BusFollower.TELEGRAM_BUS_FOLLOWER_PROMOTION_GRACE_MS,
|
|
785
|
+
recordRuntimeEvent,
|
|
786
|
+
},
|
|
787
|
+
);
|
|
788
|
+
telegramBusFollowerRegistration =
|
|
789
|
+
BusFollower.createTelegramBusFollowerRegistrationRuntime<Pi.ExtensionContext>(
|
|
790
|
+
{
|
|
791
|
+
instanceId: telegramInstanceId,
|
|
792
|
+
followerBusSocketPath: telegramBusFollowerSocketPath,
|
|
793
|
+
startReceiving: telegramBusForwardedUpdateReceiver.start,
|
|
794
|
+
stopReceiving: telegramBusForwardedUpdateReceiver.stop,
|
|
795
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
796
|
+
createRequestId() {
|
|
797
|
+
telegramBusRequestSequence += 1;
|
|
798
|
+
return Bus.createTelegramBusRequestId({
|
|
799
|
+
instanceId: telegramInstanceId,
|
|
800
|
+
sequence: telegramBusRequestSequence,
|
|
801
|
+
});
|
|
802
|
+
},
|
|
803
|
+
getLeaderAuthSecret(owner) {
|
|
804
|
+
return owner.busSecret;
|
|
805
|
+
},
|
|
806
|
+
setActiveAuthSecret(secret) {
|
|
807
|
+
telegramActiveBusAuthSecret = secret;
|
|
808
|
+
},
|
|
809
|
+
getThreadName() {
|
|
810
|
+
return undefined;
|
|
811
|
+
},
|
|
812
|
+
getProfileKey() {
|
|
813
|
+
return telegramManualFollowerProfileKey;
|
|
814
|
+
},
|
|
815
|
+
onHeartbeatFailure: telegramFollowerHeartbeatRecovery,
|
|
816
|
+
recordRuntimeEvent,
|
|
817
|
+
},
|
|
818
|
+
);
|
|
376
819
|
const pollingRuntime = Polling.createTelegramPollingControllerRuntime({
|
|
377
820
|
state: pollingControllerState,
|
|
378
821
|
getConfig: configStore.get,
|
|
379
822
|
hasBotToken: configStore.hasBotToken,
|
|
380
823
|
deleteWebhook,
|
|
381
824
|
getUpdates,
|
|
382
|
-
persistConfig:
|
|
825
|
+
persistConfig: persistTelegramConfigWithSync,
|
|
383
826
|
handleUpdate: Updates.createTelegramUpdateHandle({
|
|
384
827
|
defaultHandle: inboundRouteRuntime.handleUpdate,
|
|
385
828
|
}),
|
|
@@ -387,16 +830,292 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
387
830
|
updateStatus,
|
|
388
831
|
recordRuntimeEvent,
|
|
389
832
|
});
|
|
833
|
+
const recoverStaleTelegramTopicApiError = function (
|
|
834
|
+
apiBody: unknown,
|
|
835
|
+
error: unknown,
|
|
836
|
+
) {
|
|
837
|
+
return Sync.recoverStaleTelegramTopicApiError(apiBody, error, {
|
|
838
|
+
topicTargetStore: threadStore,
|
|
839
|
+
getSyncState() {
|
|
840
|
+
return telegramSyncState;
|
|
841
|
+
},
|
|
842
|
+
setSyncState(state) {
|
|
843
|
+
telegramSyncState = state;
|
|
844
|
+
},
|
|
845
|
+
recordEvent: recordRuntimeEvent,
|
|
846
|
+
});
|
|
847
|
+
};
|
|
848
|
+
const telegramBusLeaderRuntime = BusLeader.createTelegramBusLeaderRuntime({
|
|
849
|
+
socketPath: telegramBusSocketPath,
|
|
850
|
+
followerRegistry: telegramBusFollowerRegistry,
|
|
851
|
+
authSecret: telegramBusAuthSecret,
|
|
852
|
+
startPolling: pollingRuntime.start,
|
|
853
|
+
stopPolling: pollingRuntime.stop,
|
|
854
|
+
authorizeFollowerApiCall: Bus.isTelegramFollowerApiCallAllowed,
|
|
855
|
+
provisionLeaderTarget:
|
|
856
|
+
BusLeader.createTelegramBusLeaderTargetProvisioner<Pi.ExtensionContext>({
|
|
857
|
+
getAllowedUserId: configStore.getAllowedUserId,
|
|
858
|
+
instanceId: telegramInstanceId,
|
|
859
|
+
getCwd(ctx) {
|
|
860
|
+
return typeof ctx.cwd === "string" ? ctx.cwd : undefined;
|
|
861
|
+
},
|
|
862
|
+
shouldForceFreshUnnamed() {
|
|
863
|
+
return forceFreshLeaderThreadOnNextStart;
|
|
864
|
+
},
|
|
865
|
+
topicTargetStore: threadStore,
|
|
866
|
+
callApi(method, body) {
|
|
867
|
+
return directTelegramApiRuntime.call(method, body);
|
|
868
|
+
},
|
|
869
|
+
getCurrentLeaderEpoch,
|
|
870
|
+
getThreadReconciliationMachineState() {
|
|
871
|
+
return threadReconciliationRuntime.getState();
|
|
872
|
+
},
|
|
873
|
+
recordThreadReconciliationPlan,
|
|
874
|
+
getSyncState() {
|
|
875
|
+
return telegramSyncState;
|
|
876
|
+
},
|
|
877
|
+
setSyncState(state) {
|
|
878
|
+
telegramSyncState = state;
|
|
879
|
+
},
|
|
880
|
+
setLeaderTarget(input) {
|
|
881
|
+
telegramBusLeaderTarget = input.target;
|
|
882
|
+
telegramBusLeaderSlot = input.slot;
|
|
883
|
+
},
|
|
884
|
+
onProvisioningStart() {
|
|
885
|
+
telegramTopicProvisioningCount += 1;
|
|
886
|
+
},
|
|
887
|
+
onProvisioningEnd() {
|
|
888
|
+
telegramTopicProvisioningCount = Math.max(
|
|
889
|
+
0,
|
|
890
|
+
telegramTopicProvisioningCount - 1,
|
|
891
|
+
);
|
|
892
|
+
},
|
|
893
|
+
recordRuntimeEvent,
|
|
894
|
+
}),
|
|
895
|
+
onFollowerPruned: BusLeader.createTelegramBusFollowerPruneHandler({
|
|
896
|
+
topicTargetStore: threadStore,
|
|
897
|
+
callApi(method, body) {
|
|
898
|
+
return directTelegramApiRuntime.call(method, body);
|
|
899
|
+
},
|
|
900
|
+
getCurrentLeaderEpoch,
|
|
901
|
+
getSyncState() {
|
|
902
|
+
return telegramSyncState;
|
|
903
|
+
},
|
|
904
|
+
setSyncState(state) {
|
|
905
|
+
telegramSyncState = state;
|
|
906
|
+
},
|
|
907
|
+
recordRuntimeEvent,
|
|
908
|
+
}),
|
|
909
|
+
provisionFollowerTarget:
|
|
910
|
+
BusLeader.createTelegramBusFollowerTargetProvisioner({
|
|
911
|
+
getAllowedUserId: configStore.getAllowedUserId,
|
|
912
|
+
topicTargetStore: threadStore,
|
|
913
|
+
callApi(method, body) {
|
|
914
|
+
return directTelegramApiRuntime.call(method, body);
|
|
915
|
+
},
|
|
916
|
+
getCurrentLeaderEpoch,
|
|
917
|
+
getSyncState() {
|
|
918
|
+
return telegramSyncState;
|
|
919
|
+
},
|
|
920
|
+
setSyncState(state) {
|
|
921
|
+
telegramSyncState = state;
|
|
922
|
+
},
|
|
923
|
+
onProvisioningStart() {
|
|
924
|
+
telegramTopicProvisioningCount += 1;
|
|
925
|
+
},
|
|
926
|
+
onProvisioningEnd() {
|
|
927
|
+
telegramTopicProvisioningCount = Math.max(
|
|
928
|
+
0,
|
|
929
|
+
telegramTopicProvisioningCount - 1,
|
|
930
|
+
);
|
|
931
|
+
},
|
|
932
|
+
recordRuntimeEvent,
|
|
933
|
+
}),
|
|
934
|
+
restoreFollowerRegistry:
|
|
935
|
+
BusLeader.createTelegramBusFollowerRegistryRestoreHandler({
|
|
936
|
+
topicTargetStore: threadStore,
|
|
937
|
+
followerRegistry: telegramBusFollowerRegistry,
|
|
938
|
+
recordRuntimeEvent,
|
|
939
|
+
}),
|
|
940
|
+
callApi: BusLeader.createTelegramBusLeaderApiProxy({
|
|
941
|
+
call: directTelegramApiRuntime.call,
|
|
942
|
+
callMultipart: directTelegramApiRuntime.callMultipart,
|
|
943
|
+
downloadFile: directTelegramApiRuntime.downloadFile,
|
|
944
|
+
recoverStaleTargetError: recoverStaleTelegramTopicApiError,
|
|
945
|
+
}),
|
|
946
|
+
recordRuntimeEvent,
|
|
947
|
+
});
|
|
948
|
+
let pollingStartedWithTelegramBus = false;
|
|
949
|
+
let forceFreshLeaderThreadOnNextStart = false;
|
|
950
|
+
const telegramLeaderHealthRuntime = Sync.createTelegramLeaderHealthRuntime({
|
|
951
|
+
callGetMe() {
|
|
952
|
+
return directTelegramApiRuntime.call("getMe", {});
|
|
953
|
+
},
|
|
954
|
+
getSyncState() {
|
|
955
|
+
return telegramSyncState;
|
|
956
|
+
},
|
|
957
|
+
setSyncState(state) {
|
|
958
|
+
telegramSyncState = state;
|
|
959
|
+
},
|
|
960
|
+
recordEvent: recordRuntimeEvent,
|
|
961
|
+
});
|
|
962
|
+
const telegramThreadCapabilityMonitor =
|
|
963
|
+
Polling.createTelegramThreadCapabilityMonitor<Pi.ExtensionContext>({
|
|
964
|
+
getAllowedUserId: configStore.getAllowedUserId,
|
|
965
|
+
callApi: callTelegramApi,
|
|
966
|
+
topicTargetStore: threadStore,
|
|
967
|
+
isBusConfigured: isTelegramBusConfigured,
|
|
968
|
+
ownsLock(ctx) {
|
|
969
|
+
return lockRuntime.owns(ctx);
|
|
970
|
+
},
|
|
971
|
+
getPollingStartedWithTelegramBus() {
|
|
972
|
+
return pollingStartedWithTelegramBus;
|
|
973
|
+
},
|
|
974
|
+
setPollingStartedWithTelegramBus(started) {
|
|
975
|
+
pollingStartedWithTelegramBus = started;
|
|
976
|
+
},
|
|
977
|
+
setTopicModeUnavailable(unavailable) {
|
|
978
|
+
telegramTopicModeUnavailable = unavailable;
|
|
979
|
+
},
|
|
980
|
+
stopFollowerRegistration: telegramBusFollowerRegistration.stop,
|
|
981
|
+
startClassicPolling(ctx) {
|
|
982
|
+
return pollingRuntime.start(ctx);
|
|
983
|
+
},
|
|
984
|
+
stopClassicPolling() {
|
|
985
|
+
return pollingRuntime.stop();
|
|
986
|
+
},
|
|
987
|
+
startBusPolling(ctx) {
|
|
988
|
+
return telegramBusLeaderRuntime.startPolling(ctx);
|
|
989
|
+
},
|
|
990
|
+
stopBusPolling() {
|
|
991
|
+
return telegramBusLeaderRuntime.stopPolling();
|
|
992
|
+
},
|
|
993
|
+
startLeaderHealth: telegramLeaderHealthRuntime.start,
|
|
994
|
+
stopLeaderHealth: telegramLeaderHealthRuntime.stop,
|
|
995
|
+
isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
|
|
996
|
+
updateStatus,
|
|
997
|
+
recordEvent: recordRuntimeEvent,
|
|
998
|
+
});
|
|
999
|
+
observeTelegramThreadTarget = Polling.createTelegramThreadTargetObservationHandler({
|
|
1000
|
+
getAllowedUserId: configStore.getAllowedUserId,
|
|
1001
|
+
callApi: callTelegramApi,
|
|
1002
|
+
topicTargetStore: threadStore,
|
|
1003
|
+
isBusConfigured: isTelegramBusConfigured,
|
|
1004
|
+
ownsLock(ctx) {
|
|
1005
|
+
return lockRuntime.owns(ctx);
|
|
1006
|
+
},
|
|
1007
|
+
getPollingStartedWithTelegramBus() {
|
|
1008
|
+
return pollingStartedWithTelegramBus;
|
|
1009
|
+
},
|
|
1010
|
+
setPollingStartedWithTelegramBus(started) {
|
|
1011
|
+
pollingStartedWithTelegramBus = started;
|
|
1012
|
+
},
|
|
1013
|
+
setTopicModeUnavailable(unavailable) {
|
|
1014
|
+
telegramTopicModeUnavailable = unavailable;
|
|
1015
|
+
},
|
|
1016
|
+
stopFollowerRegistration: telegramBusFollowerRegistration.stop,
|
|
1017
|
+
startClassicPolling(ctx) {
|
|
1018
|
+
return pollingRuntime.start(ctx);
|
|
1019
|
+
},
|
|
1020
|
+
stopClassicPolling() {
|
|
1021
|
+
return pollingRuntime.stop();
|
|
1022
|
+
},
|
|
1023
|
+
startBusPolling(ctx) {
|
|
1024
|
+
return telegramBusLeaderRuntime.startPolling(ctx);
|
|
1025
|
+
},
|
|
1026
|
+
stopBusPolling() {
|
|
1027
|
+
return telegramBusLeaderRuntime.stopPolling();
|
|
1028
|
+
},
|
|
1029
|
+
startLeaderHealth: telegramLeaderHealthRuntime.start,
|
|
1030
|
+
stopLeaderHealth: telegramLeaderHealthRuntime.stop,
|
|
1031
|
+
isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
|
|
1032
|
+
updateStatus,
|
|
1033
|
+
recordEvent: recordRuntimeEvent,
|
|
1034
|
+
});
|
|
1035
|
+
const threadAwarePollingPorts =
|
|
1036
|
+
Polling.createTelegramThreadAwarePollingPorts<
|
|
1037
|
+
Pi.ExtensionContext,
|
|
1038
|
+
Locks.TelegramLockEntry
|
|
1039
|
+
>({
|
|
1040
|
+
getAllowedUserId: configStore.getAllowedUserId,
|
|
1041
|
+
callApi: callTelegramApi,
|
|
1042
|
+
topicTargetStore: threadStore,
|
|
1043
|
+
isBusConfigured: isTelegramBusConfigured,
|
|
1044
|
+
isBusRuntimeEnabled: isTelegramBusRuntimeEnabled,
|
|
1045
|
+
isTopicModeUnavailableError: Threads.isTelegramTopicModeUnavailableError,
|
|
1046
|
+
getPollingStartedWithTelegramBus() {
|
|
1047
|
+
return pollingStartedWithTelegramBus;
|
|
1048
|
+
},
|
|
1049
|
+
setPollingStartedWithTelegramBus(started) {
|
|
1050
|
+
pollingStartedWithTelegramBus = started;
|
|
1051
|
+
},
|
|
1052
|
+
setForceFreshLeaderThreadOnNextStart(forceFresh) {
|
|
1053
|
+
forceFreshLeaderThreadOnNextStart = forceFresh;
|
|
1054
|
+
},
|
|
1055
|
+
setTopicModeUnavailable(unavailable) {
|
|
1056
|
+
telegramTopicModeUnavailable = unavailable;
|
|
1057
|
+
},
|
|
1058
|
+
startClassicPolling(ctx) {
|
|
1059
|
+
return pollingRuntime.start(ctx);
|
|
1060
|
+
},
|
|
1061
|
+
stopClassicPolling() {
|
|
1062
|
+
return pollingRuntime.stop();
|
|
1063
|
+
},
|
|
1064
|
+
startBusLeaderPolling(ctx) {
|
|
1065
|
+
return telegramBusLeaderRuntime.startPolling(ctx);
|
|
1066
|
+
},
|
|
1067
|
+
stopBusLeaderPolling() {
|
|
1068
|
+
return telegramBusLeaderRuntime.stopPolling();
|
|
1069
|
+
},
|
|
1070
|
+
startLeaderHealth: telegramLeaderHealthRuntime.start,
|
|
1071
|
+
stopLeaderHealth: telegramLeaderHealthRuntime.stop,
|
|
1072
|
+
registerFollowerWithLeader(ctx, owner) {
|
|
1073
|
+
return telegramBusFollowerRegistration.registerWithLeader(ctx, owner);
|
|
1074
|
+
},
|
|
1075
|
+
stopFollowerRegistration: telegramBusFollowerRegistration.stop,
|
|
1076
|
+
recordEvent: recordRuntimeEvent,
|
|
1077
|
+
});
|
|
390
1078
|
const lockedPollingRuntime = Locks.createTelegramLockedPollingRuntime({
|
|
391
1079
|
lock: lockRuntime,
|
|
392
1080
|
hasBotToken: configStore.hasBotToken,
|
|
393
1081
|
canStartPolling: Pi.canStartPollingInExtensionContext,
|
|
394
1082
|
formatStartBlockedMessage: Pi.formatPollingStartBlockedByRunMode,
|
|
395
|
-
startPolling:
|
|
396
|
-
stopPolling:
|
|
1083
|
+
startPolling: threadAwarePollingPorts.startPolling,
|
|
1084
|
+
stopPolling: threadAwarePollingPorts.stopPolling,
|
|
1085
|
+
registerFollowerWithOwner: threadAwarePollingPorts.registerFollowerWithOwner,
|
|
1086
|
+
stopFollowerRegistration: threadAwarePollingPorts.stopFollowerRegistration,
|
|
397
1087
|
updateStatus,
|
|
398
1088
|
recordRuntimeEvent,
|
|
399
1089
|
});
|
|
1090
|
+
const disconnectTelegramAndDeleteCurrentThread =
|
|
1091
|
+
Sync.createTelegramManualThreadDisconnectHandler({
|
|
1092
|
+
instanceId: telegramInstanceId,
|
|
1093
|
+
getCurrentThreadRecord,
|
|
1094
|
+
topicTargetStore: threadStore,
|
|
1095
|
+
callApi: callTelegramApi,
|
|
1096
|
+
getLeaderTarget() {
|
|
1097
|
+
return telegramBusLeaderTarget;
|
|
1098
|
+
},
|
|
1099
|
+
clearLeaderTarget() {
|
|
1100
|
+
telegramBusLeaderTarget = undefined;
|
|
1101
|
+
telegramBusLeaderSlot = undefined;
|
|
1102
|
+
},
|
|
1103
|
+
getSyncState() {
|
|
1104
|
+
return telegramSyncState;
|
|
1105
|
+
},
|
|
1106
|
+
setSyncState(state) {
|
|
1107
|
+
telegramSyncState = state;
|
|
1108
|
+
},
|
|
1109
|
+
stopPolling: lockedPollingRuntime.stop,
|
|
1110
|
+
recordRuntimeEvent,
|
|
1111
|
+
});
|
|
1112
|
+
const suspendTelegramForSessionReplacement =
|
|
1113
|
+
BusFollower.createTelegramBusFollowerSessionReplacementSuspender({
|
|
1114
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
1115
|
+
instanceId: telegramInstanceId,
|
|
1116
|
+
suspendPolling: lockedPollingRuntime.suspend,
|
|
1117
|
+
recordRuntimeEvent,
|
|
1118
|
+
});
|
|
400
1119
|
const queueSessionLifecycle = Queue.createTelegramSessionLifecycleRuntime({
|
|
401
1120
|
getCurrentModel: getContextModel,
|
|
402
1121
|
loadConfig: configStore.load,
|
|
@@ -415,37 +1134,66 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
415
1134
|
}),
|
|
416
1135
|
clearModelMenuState: modelMenuRuntime.clear,
|
|
417
1136
|
getActiveTurnChatId: activeTurnRuntime.getChatId,
|
|
1137
|
+
getActiveTurnTarget: activeTurnRuntime.getTarget,
|
|
418
1138
|
clearPreview: previewRuntime.clear,
|
|
419
1139
|
clearActiveTurn: activeTurnRuntime.clear,
|
|
420
1140
|
clearAbort: abort.clearHandler,
|
|
421
|
-
stopPolling:
|
|
1141
|
+
stopPolling: suspendTelegramForSessionReplacement,
|
|
422
1142
|
recordRuntimeEvent,
|
|
423
1143
|
});
|
|
424
1144
|
const baseSessionLifecycleRuntime = Lifecycle.appendTelegramLifecycleHooks(
|
|
425
1145
|
queueSessionLifecycle,
|
|
426
1146
|
{
|
|
427
|
-
|
|
1147
|
+
async onSessionStart(event, ctx) {
|
|
1148
|
+
await lockedPollingRuntime.onSessionStart(event, ctx);
|
|
1149
|
+
telegramThreadCapabilityMonitor.start(ctx);
|
|
1150
|
+
},
|
|
1151
|
+
async onSessionShutdown() {
|
|
1152
|
+
telegramThreadCapabilityMonitor.stop();
|
|
1153
|
+
},
|
|
428
1154
|
},
|
|
429
1155
|
);
|
|
430
|
-
const
|
|
1156
|
+
const sessionLifecycleWithContext = Lifecycle.appendTelegramLifecycleHooks(
|
|
431
1157
|
baseSessionLifecycleRuntime,
|
|
432
1158
|
Lifecycle.createTelegramSessionContextTracker(telegramSessionContextStore),
|
|
433
1159
|
);
|
|
1160
|
+
const sessionLifecycleRuntime = Lifecycle.appendTelegramLifecycleHooks(
|
|
1161
|
+
sessionLifecycleWithContext,
|
|
1162
|
+
{
|
|
1163
|
+
onSessionStart: BusFollower.createTelegramBusFollowerSessionRefreshHook({
|
|
1164
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
1165
|
+
registrationRuntime: telegramBusFollowerRegistration,
|
|
1166
|
+
getLeaderState() {
|
|
1167
|
+
return lockRuntime.getState();
|
|
1168
|
+
},
|
|
1169
|
+
updateStatus,
|
|
1170
|
+
recordRuntimeEvent,
|
|
1171
|
+
}),
|
|
1172
|
+
},
|
|
1173
|
+
);
|
|
434
1174
|
|
|
435
1175
|
// --- Extension API Bindings ---
|
|
436
1176
|
|
|
437
1177
|
Bindings.registerTelegramCommandsAndTools({
|
|
438
1178
|
pi,
|
|
439
1179
|
configStore,
|
|
1180
|
+
persistConfig: persistTelegramConfigWithSync,
|
|
440
1181
|
setup,
|
|
441
1182
|
activeTurnRuntime,
|
|
442
1183
|
lockedPollingRuntime,
|
|
1184
|
+
stopPolling: disconnectTelegramAndDeleteCurrentThread,
|
|
443
1185
|
getStatusLines,
|
|
444
1186
|
buttonActionStore,
|
|
445
1187
|
sendMarkdownReply,
|
|
446
1188
|
callMultipart,
|
|
447
1189
|
getDefaultChatId: proactivePushChatIdGetter,
|
|
448
|
-
|
|
1190
|
+
getDefaultTarget: proactivePushTargetGetter,
|
|
1191
|
+
canSendDirect() {
|
|
1192
|
+
return (
|
|
1193
|
+
ownsTelegramDirectDelivery() ||
|
|
1194
|
+
telegramBusFollowerRegistrationState.isRegistered()
|
|
1195
|
+
);
|
|
1196
|
+
},
|
|
449
1197
|
updateStatus,
|
|
450
1198
|
recordRuntimeEvent,
|
|
451
1199
|
});
|
|
@@ -475,12 +1223,21 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
475
1223
|
sendRecordVoiceAction,
|
|
476
1224
|
sendMarkdownReply,
|
|
477
1225
|
sendTextReply,
|
|
1226
|
+
editInteractiveMessage,
|
|
1227
|
+
deleteMessage: deleteTelegramMessage,
|
|
478
1228
|
dispatchNextQueuedTelegramTurn,
|
|
479
1229
|
answerGuestQuery,
|
|
480
1230
|
sendGuestReply,
|
|
481
1231
|
finalizeMarkdownPreview,
|
|
482
1232
|
proactivePushChatIdGetter,
|
|
1233
|
+
proactivePushTargetGetter,
|
|
483
1234
|
isProactivePushEnabled: configControls.isProactivePushEnabled,
|
|
1235
|
+
canSendProactivePush(ctx) {
|
|
1236
|
+
return (
|
|
1237
|
+
lockOwnershipGuard.ownsContext(ctx) ||
|
|
1238
|
+
telegramBusFollowerRegistrationState.isRegistered()
|
|
1239
|
+
);
|
|
1240
|
+
},
|
|
484
1241
|
updateStatus,
|
|
485
1242
|
recordRuntimeEvent,
|
|
486
1243
|
});
|