@llblab/pi-telegram 0.52.2 → 0.53.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.
- package/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +15 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +65 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +252 -127
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +128 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +516 -301
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/lib/extension.ts
CHANGED
|
@@ -12,7 +12,6 @@ import * as BusLeader from "./bus-leader.ts";
|
|
|
12
12
|
import * as BusTransport from "./bus-transport.ts";
|
|
13
13
|
import * as Bus from "./bus.ts";
|
|
14
14
|
import * as ChannelPosts from "./channel-posts.ts";
|
|
15
|
-
import * as ThreadCleanupManager from "./thread-cleanup-manager.ts";
|
|
16
15
|
import * as CommandTemplates from "./command-templates.ts";
|
|
17
16
|
import * as Commands from "./commands.ts";
|
|
18
17
|
import * as Config from "./config.ts";
|
|
@@ -33,6 +32,7 @@ import * as Paths from "./paths.ts";
|
|
|
33
32
|
import * as Pi from "./pi.ts";
|
|
34
33
|
import * as Polling from "./polling.ts";
|
|
35
34
|
import * as Preview from "./preview.ts";
|
|
35
|
+
import * as ProcessIdentity from "./process-identity.ts";
|
|
36
36
|
import * as PromptTemplates from "./prompt-templates.ts";
|
|
37
37
|
import * as Prompts from "./prompts.ts";
|
|
38
38
|
import * as Queue from "./queue.ts";
|
|
@@ -44,12 +44,13 @@ import * as Sections from "./sections.ts";
|
|
|
44
44
|
import * as Skills from "./skills.ts";
|
|
45
45
|
import * as Status from "./status.ts";
|
|
46
46
|
import * as Sync from "./sync.ts";
|
|
47
|
+
import * as Targets from "./target.ts";
|
|
47
48
|
import * as TelegramApi from "./telegram-api.ts";
|
|
48
49
|
import * as TextGroups from "./text-groups.ts";
|
|
49
|
-
import * as
|
|
50
|
-
import * as Targets from "./target.ts";
|
|
50
|
+
import * as ThreadCleanupManager from "./thread-cleanup-manager.ts";
|
|
51
51
|
import * as ThreadDisplay from "./thread-display.ts";
|
|
52
52
|
import * as ThreadNaming from "./thread-naming.ts";
|
|
53
|
+
import * as ThreadReconciler from "./thread-reconciler.ts";
|
|
53
54
|
import * as Threads from "./threads.ts";
|
|
54
55
|
import * as TimeInjection from "./time-injection.ts";
|
|
55
56
|
import * as Updates from "./updates.ts";
|
|
@@ -69,6 +70,11 @@ const telegramBusProtocolIdentity =
|
|
|
69
70
|
Bus.TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT,
|
|
70
71
|
Bus.TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT,
|
|
71
72
|
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE,
|
|
73
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE,
|
|
74
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY,
|
|
75
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE,
|
|
76
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
|
|
77
|
+
Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
|
|
72
78
|
]);
|
|
73
79
|
|
|
74
80
|
// --- Extension Runtime ---
|
|
@@ -88,7 +94,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
88
94
|
} = piRuntime;
|
|
89
95
|
const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
|
|
90
96
|
const runtimeDiagnostics =
|
|
91
|
-
Logging.createTelegramRuntimeDiagnosticsRuntime<Pi.ExtensionContext>({
|
|
97
|
+
Logging.createTelegramRuntimeDiagnosticsRuntime<Pi.ExtensionContext>({
|
|
98
|
+
sharedFile: true,
|
|
99
|
+
});
|
|
92
100
|
const runtimeEvents = runtimeDiagnostics.events;
|
|
93
101
|
const recordRuntimeEvent = runtimeDiagnostics.recordRuntimeEvent;
|
|
94
102
|
const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
|
|
@@ -161,11 +169,18 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
161
169
|
getBotToken: configStore.getBotToken,
|
|
162
170
|
getBotId: getTelegramBotId,
|
|
163
171
|
});
|
|
164
|
-
const telegramLeaderJournalPath =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
const telegramLeaderJournalPath =
|
|
173
|
+
Locks.createTelegramLeaderJournalPathResolver({
|
|
174
|
+
getNamedJournalPath() {
|
|
175
|
+
return lockRuntime.getJournalPath();
|
|
176
|
+
},
|
|
177
|
+
getSessionId() {
|
|
178
|
+
return Pi.getExtensionContextSessionId(
|
|
179
|
+
telegramSessionContextStore.get(),
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
getProfileName: configStore.getActiveProfileName,
|
|
183
|
+
});
|
|
169
184
|
const telegramJournalBindingRuntime =
|
|
170
185
|
Journal.createTelegramUpdateJournalBindingRuntime({
|
|
171
186
|
base: {
|
|
@@ -201,9 +216,13 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
201
216
|
getLeaderJournalPath: telegramLeaderJournalPath.resolve,
|
|
202
217
|
getRuntimeDir: Paths.resolveTelegramTempDir,
|
|
203
218
|
getFollowerJournalPath(bindingKey, profileName, sessionId) {
|
|
204
|
-
if (sessionId !== undefined)
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
if (sessionId !== undefined)
|
|
220
|
+
return Paths.resolveTelegramSessionJournalPath(
|
|
221
|
+
sessionId,
|
|
222
|
+
bindingKey,
|
|
223
|
+
undefined,
|
|
224
|
+
profileName,
|
|
225
|
+
);
|
|
207
226
|
return Paths.resolveTelegramFollowerJournalPath(
|
|
208
227
|
bindingKey,
|
|
209
228
|
undefined,
|
|
@@ -228,7 +247,6 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
228
247
|
telegramJournalBindingRuntime.getActiveRecoveryKey;
|
|
229
248
|
const isTelegramBusRuntimeEnabled =
|
|
230
249
|
telegramThreadCapabilityState.isBusRuntimeEnabled;
|
|
231
|
-
Config.bindGlobalTelegramConfigRuntime(configStore);
|
|
232
250
|
const configControls = Config.createTelegramConfigControls(configStore);
|
|
233
251
|
const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
|
|
234
252
|
key: Locks.createTelegramLockKeyResolver(configStore),
|
|
@@ -237,7 +255,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
237
255
|
busSecret: telegramBusAuthSecret,
|
|
238
256
|
staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
|
|
239
257
|
legacyLocksPath: Paths.resolveLegacyTelegramOwnersPath(),
|
|
240
|
-
createJournalPath() {
|
|
258
|
+
createJournalPath() {
|
|
259
|
+
return telegramLeaderJournalPath.createJournalPath();
|
|
260
|
+
},
|
|
241
261
|
});
|
|
242
262
|
const telegramSessionContextStore =
|
|
243
263
|
Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>({
|
|
@@ -245,8 +265,11 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
245
265
|
return ctx.sessionManager ?? ctx.cwd;
|
|
246
266
|
},
|
|
247
267
|
});
|
|
248
|
-
const captureTelegramStateAuthority =
|
|
249
|
-
|
|
268
|
+
const captureTelegramStateAuthority =
|
|
269
|
+
Locks.createTelegramOwnedStateAuthorityCapture(
|
|
270
|
+
lockRuntime,
|
|
271
|
+
telegramSessionContextStore,
|
|
272
|
+
);
|
|
250
273
|
const threadStore = Threads.createTelegramTopicTargetStore({
|
|
251
274
|
path: Paths.resolveTelegramStatePath,
|
|
252
275
|
telegramProfile: function () {
|
|
@@ -261,11 +284,12 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
261
284
|
return workspaceAdmissionRuntime.resolve()?.listReservedSlots() ?? [];
|
|
262
285
|
},
|
|
263
286
|
});
|
|
264
|
-
const resolveWorkspaceRestoreStore =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
287
|
+
const resolveWorkspaceRestoreStore =
|
|
288
|
+
Threads.createTelegramWorkspaceRestoreResolver({
|
|
289
|
+
getProfileName: configStore.getActiveProfileName,
|
|
290
|
+
getBotToken: configStore.getBotToken,
|
|
291
|
+
threadStore,
|
|
292
|
+
});
|
|
269
293
|
runtimeDiagnostics.bindStorage({
|
|
270
294
|
getBotToken: configStore.getBotToken,
|
|
271
295
|
getProfileName: configStore.getActiveProfileName,
|
|
@@ -283,12 +307,15 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
283
307
|
instanceId: telegramInstanceId,
|
|
284
308
|
isRegisteredFor(target) {
|
|
285
309
|
const registered = telegramBusFollowerRegistrationState.getTarget();
|
|
286
|
-
return
|
|
310
|
+
return (
|
|
311
|
+
telegramBusFollowerRegistrationState.isRegistered() &&
|
|
287
312
|
registered?.chatId === target.chatId &&
|
|
288
|
-
registered.threadId === target.threadId
|
|
313
|
+
registered.threadId === target.threadId
|
|
314
|
+
);
|
|
289
315
|
},
|
|
290
316
|
async requestSessionReplacement(operation, intent) {
|
|
291
|
-
const request =
|
|
317
|
+
const request =
|
|
318
|
+
telegramBusFollowerRegistration.requestSessionReplacement;
|
|
292
319
|
if (!request) return false;
|
|
293
320
|
return await request(operation, intent);
|
|
294
321
|
},
|
|
@@ -298,10 +325,15 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
298
325
|
{ text: html, parseMode: "html", replyMarkup: { inline_keyboard: [] } },
|
|
299
326
|
{ scope: { kind: "target", target } },
|
|
300
327
|
);
|
|
301
|
-
return delivery.ok
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
return delivery.ok
|
|
329
|
+
? { ok: true }
|
|
330
|
+
: {
|
|
331
|
+
ok: false,
|
|
332
|
+
retryable:
|
|
333
|
+
delivery.reason === "runtime-unavailable" ||
|
|
334
|
+
delivery.reason === "target-unavailable" ||
|
|
335
|
+
delivery.reason === "transport-retryable",
|
|
336
|
+
};
|
|
305
337
|
},
|
|
306
338
|
handoffTtlMs: Threads.TELEGRAM_LEADER_SESSION_HANDOFF_TTL_MS,
|
|
307
339
|
recordRuntimeEvent,
|
|
@@ -346,11 +378,10 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
346
378
|
const proactivePushChatIdGetter =
|
|
347
379
|
Config.createTelegramProactivePushChatIdGetter(proactivePushTargetGetter);
|
|
348
380
|
const buttonActionStore = Outbound.createTelegramButtonActionStore();
|
|
349
|
-
const planGenerativeAppOutput =
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
);
|
|
381
|
+
const planGenerativeAppOutput = Outbound.createTelegramOutboundReplyPlanner(
|
|
382
|
+
buttonActionStore,
|
|
383
|
+
configControls.getAssistantRenderingMode,
|
|
384
|
+
);
|
|
354
385
|
const pendingModelSwitchStore =
|
|
355
386
|
Model.createPendingModelSwitchStore<
|
|
356
387
|
Model.ScopedTelegramModel<ActivePiModel>
|
|
@@ -388,100 +419,127 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
388
419
|
);
|
|
389
420
|
const telegramApiTargetActivityRuntime =
|
|
390
421
|
TelegramApi.createTelegramApiTargetActivityRuntime();
|
|
422
|
+
// Retirement protection and dead-owner reclamation must inspect the same journal sources.
|
|
423
|
+
const workspaceRetirementJournalPorts = {
|
|
424
|
+
getActiveTurnTarget: activeTurnRuntime.getTarget,
|
|
425
|
+
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
426
|
+
resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
|
|
427
|
+
createFollowerJournalResolver:
|
|
428
|
+
telegramJournalBindingRuntime.createLegacyRecipientResolver,
|
|
429
|
+
createSessionJournalResolver:
|
|
430
|
+
telegramJournalBindingRuntime.createRecipientResolver,
|
|
431
|
+
createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
|
|
432
|
+
discoverFollowerJournals() {
|
|
433
|
+
return Journal.discoverTelegramRecipientJournalPaths({
|
|
434
|
+
directory: Paths.resolveTelegramTempDir(),
|
|
435
|
+
profileName: configStore.getActiveProfileName(),
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
};
|
|
391
439
|
const workspaceProtectionObserver =
|
|
392
440
|
WorkspaceRetirement.createTelegramWorkspaceProtectionObserver({
|
|
393
441
|
listFollowers: telegramBusFollowerRegistry.list,
|
|
394
|
-
|
|
395
|
-
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
396
|
-
resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
|
|
397
|
-
createFollowerJournalResolver:
|
|
398
|
-
telegramJournalBindingRuntime.createLegacyRecipientResolver,
|
|
399
|
-
createSessionJournalResolver:
|
|
400
|
-
telegramJournalBindingRuntime.createRecipientResolver,
|
|
442
|
+
...workspaceRetirementJournalPorts,
|
|
401
443
|
withJournalReference(binding, operation) {
|
|
402
|
-
if (!binding.recoveryKey)
|
|
403
|
-
|
|
444
|
+
if (!binding.recoveryKey)
|
|
445
|
+
throw new Error(
|
|
446
|
+
"Telegram workspace journal reference identity is unavailable.",
|
|
447
|
+
);
|
|
448
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
449
|
+
{
|
|
450
|
+
referenceClass: "workspace-retirement",
|
|
451
|
+
recoveryKey: binding.recoveryKey,
|
|
452
|
+
},
|
|
453
|
+
operation,
|
|
404
454
|
);
|
|
405
|
-
return telegramJournalReferenceRegistry.withReference({
|
|
406
|
-
referenceClass: "workspace-retirement",
|
|
407
|
-
recoveryKey: binding.recoveryKey,
|
|
408
|
-
}, operation);
|
|
409
|
-
},
|
|
410
|
-
discoverFollowerJournals() {
|
|
411
|
-
return Journal.discoverTelegramRecipientJournalPaths({
|
|
412
|
-
directory: Paths.resolveTelegramTempDir(),
|
|
413
|
-
profileName: configStore.getActiveProfileName(),
|
|
414
|
-
});
|
|
415
455
|
},
|
|
416
|
-
createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
|
|
417
456
|
inspectJournalNamespace() {
|
|
418
457
|
const botToken = configStore.getBotToken();
|
|
419
|
-
if (!botToken)
|
|
458
|
+
if (!botToken)
|
|
459
|
+
throw new Error(
|
|
460
|
+
"Telegram namespace inspection requires current bot identity.",
|
|
461
|
+
);
|
|
420
462
|
return Journal.inspectTelegramSessionJournalNamespace({
|
|
421
463
|
directory: Paths.resolveTelegramTempDir(),
|
|
422
464
|
profile: configStore.getActiveProfileName() ?? "default",
|
|
423
|
-
pollingPath: telegramLeaderJournalPath.resolve(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
465
|
+
pollingPath: telegramLeaderJournalPath.resolve(
|
|
466
|
+
configStore.getActiveProfileName(),
|
|
467
|
+
),
|
|
468
|
+
botIdentity: Journal.createTelegramUpdateJournalBotIdentity({
|
|
469
|
+
botToken,
|
|
470
|
+
botId: getTelegramBotId(),
|
|
471
|
+
}),
|
|
472
|
+
limits: {
|
|
473
|
+
maxDirectoryEntries: 10_000,
|
|
474
|
+
maxFiles: 4096,
|
|
475
|
+
maxBytes: 64 * 1024 * 1024,
|
|
476
|
+
maxEntries: 10_000,
|
|
477
|
+
maxWork: 100_000,
|
|
478
|
+
},
|
|
427
479
|
});
|
|
428
480
|
},
|
|
429
481
|
getJournalWriterProtection(journalBindingKey) {
|
|
430
|
-
const owner =
|
|
431
|
-
journalBindingKey
|
|
432
|
-
);
|
|
482
|
+
const owner =
|
|
483
|
+
Threads.getTelegramThreadOwnerFromProfileKey(journalBindingKey);
|
|
433
484
|
if (owner.kind !== "manual-follower") return "unknown";
|
|
434
|
-
const liveness =
|
|
435
|
-
|
|
436
|
-
|
|
485
|
+
const liveness =
|
|
486
|
+
ProcessIdentity.getTelegramProcessBirthIdentityLiveness(
|
|
487
|
+
owner.instanceId,
|
|
488
|
+
);
|
|
437
489
|
return liveness === "alive"
|
|
438
490
|
? "protected"
|
|
439
|
-
: liveness === "dead"
|
|
491
|
+
: liveness === "dead"
|
|
492
|
+
? "clear"
|
|
493
|
+
: "unknown";
|
|
440
494
|
},
|
|
441
495
|
getDeliveryAuthorityProtection(binding) {
|
|
442
|
-
const retainedRestore = resolveWorkspaceRestoreStore()
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
496
|
+
const retainedRestore = resolveWorkspaceRestoreStore()
|
|
497
|
+
?.list()
|
|
498
|
+
.some(function (intent) {
|
|
499
|
+
return (
|
|
500
|
+
intent.request.binding.bindingKey === binding.bindingKey ||
|
|
501
|
+
Targets.areTelegramTargetsEqual(
|
|
502
|
+
intent.request.binding.target,
|
|
503
|
+
binding.target,
|
|
504
|
+
) ||
|
|
505
|
+
Targets.areTelegramTargetsEqual(
|
|
506
|
+
intent.request.target,
|
|
507
|
+
binding.target,
|
|
508
|
+
)
|
|
509
|
+
);
|
|
510
|
+
});
|
|
511
|
+
return retainedRestore ||
|
|
512
|
+
telegramApiTargetActivityRuntime.hasPendingTarget(binding.target)
|
|
448
513
|
? "protected"
|
|
449
514
|
: "clear";
|
|
450
515
|
},
|
|
451
516
|
});
|
|
452
|
-
const captureWorkspaceExternalProtection =
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
517
|
+
const captureWorkspaceExternalProtection =
|
|
518
|
+
workspaceProtectionObserver.capture;
|
|
519
|
+
const pruneWorkspaceJournalEvidence =
|
|
520
|
+
WorkspaceRetirement.createTelegramWorkspaceJournalEvidencePruner({
|
|
521
|
+
store: threadStore,
|
|
522
|
+
getAdmission: workspaceAdmissionRuntime.resolve,
|
|
523
|
+
getLeaderEpoch: getCurrentLeaderEpoch,
|
|
524
|
+
runExclusive: telegramWorkspaceOperationRuntime.runExclusive,
|
|
525
|
+
protection: workspaceProtectionObserver,
|
|
526
|
+
});
|
|
460
527
|
const reclaimTelegramWorkspaceDeadOwnerQueue =
|
|
461
528
|
WorkspaceRetirement.createTelegramWorkspaceDeadOwnerQueueReclaimer({
|
|
462
529
|
getExternalProtection: captureWorkspaceExternalProtection,
|
|
463
|
-
|
|
464
|
-
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
465
|
-
resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
|
|
466
|
-
createFollowerJournalResolver:
|
|
467
|
-
telegramJournalBindingRuntime.createLegacyRecipientResolver,
|
|
468
|
-
createSessionJournalResolver:
|
|
469
|
-
telegramJournalBindingRuntime.createRecipientResolver,
|
|
470
|
-
discoverFollowerJournals() {
|
|
471
|
-
return Journal.discoverTelegramRecipientJournalPaths({
|
|
472
|
-
directory: Paths.resolveTelegramTempDir(),
|
|
473
|
-
profileName: configStore.getActiveProfileName(),
|
|
474
|
-
});
|
|
475
|
-
},
|
|
476
|
-
createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
|
|
530
|
+
...workspaceRetirementJournalPorts,
|
|
477
531
|
withJournalReference(binding, operation) {
|
|
478
|
-
if (!binding.recoveryKey)
|
|
479
|
-
|
|
532
|
+
if (!binding.recoveryKey)
|
|
533
|
+
throw new Error(
|
|
534
|
+
"Telegram workspace journal reference identity is unavailable.",
|
|
535
|
+
);
|
|
536
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
537
|
+
{
|
|
538
|
+
referenceClass: "workspace-retirement",
|
|
539
|
+
recoveryKey: binding.recoveryKey,
|
|
540
|
+
},
|
|
541
|
+
operation,
|
|
480
542
|
);
|
|
481
|
-
return telegramJournalReferenceRegistry.withReference({
|
|
482
|
-
referenceClass: "workspace-retirement",
|
|
483
|
-
recoveryKey: binding.recoveryKey,
|
|
484
|
-
}, operation);
|
|
485
543
|
},
|
|
486
544
|
getRecoveryOwner() {
|
|
487
545
|
return {
|
|
@@ -491,11 +549,14 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
491
549
|
sessionGeneration: telegramSessionContextStore.getGeneration(),
|
|
492
550
|
};
|
|
493
551
|
},
|
|
494
|
-
getQueueOwnerLiveness:
|
|
552
|
+
getQueueOwnerLiveness: ProcessIdentity.getTelegramProcessLiveness,
|
|
495
553
|
onMutationError(error) {
|
|
496
|
-
recordRuntimeEvent(
|
|
497
|
-
|
|
498
|
-
|
|
554
|
+
recordRuntimeEvent(
|
|
555
|
+
"Telegram dead-owner queue reclamation was refused.",
|
|
556
|
+
{
|
|
557
|
+
error: error instanceof Error ? error.message : String(error),
|
|
558
|
+
},
|
|
559
|
+
);
|
|
499
560
|
},
|
|
500
561
|
isBindingCurrent(binding) {
|
|
501
562
|
return WorkspaceRetirement.isCurrentTelegramWorkspaceBinding(
|
|
@@ -506,7 +567,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
506
567
|
});
|
|
507
568
|
const inactiveThreadCleanupReviewRuntime =
|
|
508
569
|
ThreadCleanupManager.createTelegramInactiveThreadCleanupReviewRuntime({
|
|
509
|
-
getProfileName() {
|
|
570
|
+
getProfileName() {
|
|
571
|
+
return configStore.getActiveProfileName() ?? "default";
|
|
572
|
+
},
|
|
510
573
|
listBindings: threadStore.listWorkspaceBindings,
|
|
511
574
|
getProtection: captureWorkspaceExternalProtection,
|
|
512
575
|
listReservations: threadStore.listReservations,
|
|
@@ -515,11 +578,20 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
515
578
|
getWorkStore() {
|
|
516
579
|
const profileName = configStore.getActiveProfileName() ?? "default";
|
|
517
580
|
const botToken = configStore.getBotToken();
|
|
518
|
-
if (!botToken)
|
|
581
|
+
if (!botToken)
|
|
582
|
+
throw new Error(
|
|
583
|
+
"Telegram Thread cleanup review requires an active bot token.",
|
|
584
|
+
);
|
|
519
585
|
return ThreadCleanupManager.createTelegramThreadCleanupWorkStore({
|
|
520
|
-
...Paths.resolveTelegramServiceJournalStorage(
|
|
586
|
+
...Paths.resolveTelegramServiceJournalStorage(
|
|
587
|
+
"thread-cleanup",
|
|
588
|
+
undefined,
|
|
589
|
+
profileName,
|
|
590
|
+
),
|
|
521
591
|
profileName,
|
|
522
|
-
tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({
|
|
592
|
+
tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({
|
|
593
|
+
botToken,
|
|
594
|
+
}).tokenSha256,
|
|
523
595
|
});
|
|
524
596
|
},
|
|
525
597
|
runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
|
|
@@ -553,7 +625,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
553
625
|
instanceId: telegramInstanceId,
|
|
554
626
|
listRecords: threadStore.list,
|
|
555
627
|
listWorkspaceBindings: threadStore.listWorkspaceBindings,
|
|
556
|
-
getFollowerDisplayTitle:
|
|
628
|
+
getFollowerDisplayTitle:
|
|
629
|
+
telegramBusFollowerRegistrationState.getDisplayTitle,
|
|
557
630
|
getActiveTurnTarget: activeTurnRuntime.getTarget,
|
|
558
631
|
getFollowerTarget: telegramBusFollowerRegistrationState.getTarget,
|
|
559
632
|
isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
|
|
@@ -599,7 +672,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
599
672
|
registry: telegramJournalReferenceRegistry,
|
|
600
673
|
resolveBinding: resolveTelegramUpdateJournalBinding,
|
|
601
674
|
referenceClass: "polling-cursor",
|
|
602
|
-
operation(binding) {
|
|
675
|
+
operation(binding) {
|
|
676
|
+
return binding.journal.read().acceptedThroughUpdateId;
|
|
677
|
+
},
|
|
603
678
|
});
|
|
604
679
|
},
|
|
605
680
|
getActiveSourceMessageIds: activeTurnRuntime.getSourceMessageIds,
|
|
@@ -652,7 +727,10 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
652
727
|
getBotToken: configStore.getBotToken,
|
|
653
728
|
getBotScope() {
|
|
654
729
|
const config = configStore.get();
|
|
655
|
-
return
|
|
730
|
+
return (
|
|
731
|
+
config.botUsername ??
|
|
732
|
+
(config.botId === undefined ? undefined : String(config.botId))
|
|
733
|
+
);
|
|
656
734
|
},
|
|
657
735
|
recordRuntimeEvent,
|
|
658
736
|
targetActivity: telegramApiTargetActivityRuntime,
|
|
@@ -686,8 +764,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
686
764
|
telegramBusFollowerRegistrationState.waitForGeneration,
|
|
687
765
|
getForwardCommentBatchPosition:
|
|
688
766
|
textGroupRuntime.getPreparedForwardingPosition,
|
|
689
|
-
validateForwardOwnership:
|
|
690
|
-
|
|
767
|
+
validateForwardOwnership: Bus.createTelegramBusForwardOwnershipValidator(
|
|
768
|
+
telegramBusFollowerRegistry,
|
|
769
|
+
),
|
|
691
770
|
recordRuntimeEvent,
|
|
692
771
|
});
|
|
693
772
|
const telegramApiRuntime = BusApi.createTelegramBusAwareApiRuntime({
|
|
@@ -733,11 +812,12 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
733
812
|
editGuestInlineMessage: telegramApiRuntime.editGuestInlineMessage,
|
|
734
813
|
});
|
|
735
814
|
// Rotate the guest placeholder frames until the final replacement stops them.
|
|
736
|
-
const guestPlaceholderRuntime =
|
|
737
|
-
|
|
815
|
+
const guestPlaceholderRuntime = Replies.createTelegramGuestPlaceholderRuntime(
|
|
816
|
+
{
|
|
738
817
|
editGuestInlineMessage: telegramApiRuntime.editGuestInlineMessage,
|
|
739
818
|
recordRuntimeEvent,
|
|
740
|
-
}
|
|
819
|
+
},
|
|
820
|
+
);
|
|
741
821
|
|
|
742
822
|
const promptDispatchRuntime = Runtime.createTelegramPromptDispatchRuntime({
|
|
743
823
|
lifecycle,
|
|
@@ -751,7 +831,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
751
831
|
const epoch = getCurrentLeaderEpoch();
|
|
752
832
|
return epoch === undefined ? undefined : `direct:${epoch}`;
|
|
753
833
|
}
|
|
754
|
-
if (!telegramBusFollowerRegistrationState.isRegistered())
|
|
834
|
+
if (!telegramBusFollowerRegistrationState.isRegistered())
|
|
835
|
+
return undefined;
|
|
755
836
|
const generation = telegramBusFollowerRegistrationState.getGeneration();
|
|
756
837
|
return generation ? `follower:${generation}` : undefined;
|
|
757
838
|
},
|
|
@@ -952,15 +1033,21 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
952
1033
|
getCommands,
|
|
953
1034
|
getReservedCommandNames: Commands.getTelegramReservedCommandNames,
|
|
954
1035
|
});
|
|
955
|
-
const
|
|
1036
|
+
const getQueueMenuState = Menu.createTelegramModelMenuStateBuilder({
|
|
956
1037
|
runtime: modelMenuRuntime,
|
|
957
1038
|
createSettingsManager: Pi.createSettingsManager,
|
|
958
1039
|
getActiveModel: currentModelRuntime.get,
|
|
1040
|
+
});
|
|
1041
|
+
const menuActionPorts = {
|
|
1042
|
+
getModelMenuState: getQueueMenuState,
|
|
1043
|
+
getActiveModel: currentModelRuntime.get,
|
|
959
1044
|
getThinkingLevel,
|
|
960
1045
|
getQueueItemCount,
|
|
961
1046
|
getPendingCancellationCount() {
|
|
962
|
-
return getCurrentLeaderEpoch() === undefined
|
|
963
|
-
|
|
1047
|
+
return getCurrentLeaderEpoch() === undefined
|
|
1048
|
+
? 0
|
|
1049
|
+
: (updateAdmissionRuntimeBinding.getActive()?.getState()
|
|
1050
|
+
?.abandoningClaimCount ?? 0);
|
|
964
1051
|
},
|
|
965
1052
|
buildStatusHtml: Commands.createTelegramAppMenuHtmlBuilder({
|
|
966
1053
|
buildStatusHtml: Status.createTelegramStatusHtmlBuilder({
|
|
@@ -973,9 +1060,6 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
973
1060
|
storeModelMenuState: modelMenuRuntime.storeState,
|
|
974
1061
|
isIdle,
|
|
975
1062
|
canOfferInFlightModelSwitch: modelSwitchController.canOfferInFlightSwitch,
|
|
976
|
-
sendTextReply,
|
|
977
|
-
editInteractiveMessage,
|
|
978
|
-
sendInteractiveMessage,
|
|
979
1063
|
sectionRegistry,
|
|
980
1064
|
|
|
981
1065
|
// Menu/status UI uses this to reflect whether the active Telegram turn expects voice delivery.
|
|
@@ -983,15 +1067,15 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
983
1067
|
const turn = activeTurnRuntime.get();
|
|
984
1068
|
return Voice.isVoiceTurn(turn);
|
|
985
1069
|
},
|
|
1070
|
+
};
|
|
1071
|
+
const menuActions = Menu.createTelegramMenuActionRuntime({
|
|
1072
|
+
...menuActionPorts,
|
|
1073
|
+
sendTextReply,
|
|
1074
|
+
editInteractiveMessage,
|
|
1075
|
+
sendInteractiveMessage,
|
|
986
1076
|
});
|
|
987
1077
|
|
|
988
1078
|
// --- Queue And Settings Menus ---
|
|
989
|
-
|
|
990
|
-
const getQueueMenuState = Menu.createTelegramModelMenuStateBuilder({
|
|
991
|
-
runtime: modelMenuRuntime,
|
|
992
|
-
createSettingsManager: Pi.createSettingsManager,
|
|
993
|
-
getActiveModel: currentModelRuntime.get,
|
|
994
|
-
});
|
|
995
1079
|
const queueMenuRuntime = MenuQueue.createTelegramQueueMenuRuntime({
|
|
996
1080
|
telegramQueueStore,
|
|
997
1081
|
queueMutationRuntime,
|
|
@@ -1008,20 +1092,32 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1008
1092
|
const threadDisplaySettingsRuntime =
|
|
1009
1093
|
ThreadDisplay.createTelegramThreadDisplaySettingsRuntime({
|
|
1010
1094
|
getTarget() {
|
|
1011
|
-
return
|
|
1012
|
-
|
|
1095
|
+
return (
|
|
1096
|
+
telegramBusFollowerRegistrationState.getTarget() ??
|
|
1097
|
+
telegramBusLeaderState.getTarget()
|
|
1098
|
+
);
|
|
1099
|
+
},
|
|
1100
|
+
getBinding(target) {
|
|
1101
|
+
return threadStore.getWorkspaceBindingByTarget(target);
|
|
1013
1102
|
},
|
|
1014
|
-
getBinding(target) { return threadStore.getWorkspaceBindingByTarget(target); },
|
|
1015
1103
|
apply(mode) {
|
|
1016
1104
|
return ThreadDisplay.applyTelegramThreadDisplaySetting(mode, {
|
|
1017
1105
|
getProfileKey: configStore.getActiveProfileName,
|
|
1018
|
-
ownsLeader() {
|
|
1019
|
-
|
|
1020
|
-
|
|
1106
|
+
ownsLeader() {
|
|
1107
|
+
return getCurrentLeaderEpoch() !== undefined;
|
|
1108
|
+
},
|
|
1109
|
+
getLeaderSetter() {
|
|
1110
|
+
return telegramBusLeaderRuntime.setThreadDisplayMode;
|
|
1111
|
+
},
|
|
1112
|
+
getFollowerSetter() {
|
|
1113
|
+
return telegramBusFollowerRegistration.setThreadDisplayMode;
|
|
1114
|
+
},
|
|
1021
1115
|
reloadConfig: configStore.load,
|
|
1022
1116
|
});
|
|
1023
1117
|
},
|
|
1024
|
-
reset(target) {
|
|
1118
|
+
reset(target) {
|
|
1119
|
+
return telegramThreadDisplayNameResetBinding.reset(target);
|
|
1120
|
+
},
|
|
1025
1121
|
});
|
|
1026
1122
|
const settingsMenuRuntime = MenuSettings.createTelegramSettingsMenuRuntime(
|
|
1027
1123
|
{
|
|
@@ -1036,12 +1132,14 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1036
1132
|
reviewInactiveThreads: inactiveThreadCleanupReviewRuntime.review,
|
|
1037
1133
|
getThreadDisplayMode() {
|
|
1038
1134
|
return threadStore.getBotState().threadMode === "enabled"
|
|
1039
|
-
? Config.resolveTelegramThreadDisplayMode(configStore.get())
|
|
1135
|
+
? Config.resolveTelegramThreadDisplayMode(configStore.get())
|
|
1136
|
+
: undefined;
|
|
1040
1137
|
},
|
|
1041
1138
|
isThreadDisplayCustom: threadDisplaySettingsRuntime.isCustom,
|
|
1042
1139
|
async setThreadDisplayMode(mode) {
|
|
1043
|
-
try {
|
|
1044
|
-
|
|
1140
|
+
try {
|
|
1141
|
+
await threadDisplaySettingsRuntime.setMode(mode);
|
|
1142
|
+
} catch (error) {
|
|
1045
1143
|
recordRuntimeEvent("bus", error, { phase: "thread-display-setting" });
|
|
1046
1144
|
throw error;
|
|
1047
1145
|
}
|
|
@@ -1054,12 +1152,59 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1054
1152
|
|
|
1055
1153
|
const foreignOwnedUpdateForwarder =
|
|
1056
1154
|
telegramBusFollowerClients.foreignOwnedUpdateForwarder;
|
|
1057
|
-
const workspaceRestoreFollowerController =
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1155
|
+
const workspaceRestoreFollowerController =
|
|
1156
|
+
Bus.createTelegramBusWorkspaceRestoreController({
|
|
1157
|
+
getFollower: telegramBusFollowerRegistry.get,
|
|
1158
|
+
localProtocolIdentity: telegramBusProtocolIdentity,
|
|
1159
|
+
createRequestId: telegramBusFollowerClients.createRequestId,
|
|
1160
|
+
getAuthSecret() {
|
|
1161
|
+
return telegramBusAuthSecret;
|
|
1162
|
+
},
|
|
1163
|
+
});
|
|
1164
|
+
const liveRebindFollowerController =
|
|
1165
|
+
Bus.createTelegramBusLiveRebindController({
|
|
1166
|
+
getFollower: telegramBusFollowerRegistry.get,
|
|
1167
|
+
localProtocolIdentity: telegramBusProtocolIdentity,
|
|
1168
|
+
createRequestId: telegramBusFollowerClients.createRequestId,
|
|
1169
|
+
getAuthSecret() {
|
|
1170
|
+
return telegramBusAuthSecret;
|
|
1171
|
+
},
|
|
1172
|
+
});
|
|
1173
|
+
const liveFollowerPorts = {
|
|
1174
|
+
getJournalBindingKey(follower: Bus.TelegramBusFollowerView) {
|
|
1175
|
+
if (!follower.profileKey || !follower.sessionId) return undefined;
|
|
1176
|
+
return telegramJournalBindingRuntime.createRecipientResolver(
|
|
1177
|
+
follower.profileKey,
|
|
1178
|
+
follower.sessionId,
|
|
1179
|
+
)()?.recoveryKey;
|
|
1180
|
+
},
|
|
1181
|
+
isSelectedCommandAvailable(
|
|
1182
|
+
follower: Readonly<Bus.TelegramBusFollowerView>,
|
|
1183
|
+
name: string,
|
|
1184
|
+
): boolean {
|
|
1185
|
+
return (
|
|
1186
|
+
Bus.hasTelegramBusSharedCapabilities(
|
|
1187
|
+
telegramBusProtocolIdentity,
|
|
1188
|
+
follower.protocol,
|
|
1189
|
+
[
|
|
1190
|
+
...Bus.TELEGRAM_BUS_LIVE_REBIND_CAPABILITIES,
|
|
1191
|
+
...Bus.TELEGRAM_BUS_HELD_COMMAND_CAPABILITIES,
|
|
1192
|
+
],
|
|
1193
|
+
) &&
|
|
1194
|
+
typeof selectedMenuDelivery === "function" &&
|
|
1195
|
+
typeof inboundRouteRuntime.prepareHeldCommand === "function" &&
|
|
1196
|
+
typeof inboundRouteRuntime.canPrepareHeldCommand === "function" &&
|
|
1197
|
+
inboundRouteRuntime.canPrepareHeldCommand(name, {
|
|
1198
|
+
showStatus: menuActions.sendStatusMessage,
|
|
1199
|
+
sendTextReply,
|
|
1200
|
+
}) &&
|
|
1201
|
+
!!captureTelegramStateAuthority() &&
|
|
1202
|
+
!!workspaceAdmissionRuntime.resolve() &&
|
|
1203
|
+
!!resolveWorkspaceRestoreStore()
|
|
1204
|
+
);
|
|
1205
|
+
},
|
|
1206
|
+
run: liveRebindFollowerController,
|
|
1207
|
+
};
|
|
1063
1208
|
const observedThreadTargetBinding =
|
|
1064
1209
|
Polling.createTelegramThreadTargetObservationBinding<Pi.ExtensionContext>();
|
|
1065
1210
|
const topicLifecycleSync =
|
|
@@ -1093,6 +1238,18 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1093
1238
|
Commands.createTelegramThreadDisplayNameRenameBinding();
|
|
1094
1239
|
const telegramThreadDisplayNameResetBinding =
|
|
1095
1240
|
Commands.createTelegramThreadDisplayNameResetBinding();
|
|
1241
|
+
const observeLiveTargetWork = Bindings.createTelegramLiveTargetWorkObserver({
|
|
1242
|
+
queue: telegramQueueStore,
|
|
1243
|
+
activeTurn: activeTurnRuntime,
|
|
1244
|
+
lifecycle: bridgeRuntime.lifecycle,
|
|
1245
|
+
isIdle,
|
|
1246
|
+
hasPendingMessages,
|
|
1247
|
+
hasPendingControl: pendingModelSwitchStore.has,
|
|
1248
|
+
publication: publicationRuntime,
|
|
1249
|
+
activity: activityRuntime,
|
|
1250
|
+
delivery: deliveryLifecycleRuntime,
|
|
1251
|
+
api: telegramApiTargetActivityRuntime,
|
|
1252
|
+
});
|
|
1096
1253
|
const inboundRouteRuntime = Routing.createTelegramInboundRouteRuntime({
|
|
1097
1254
|
configStore,
|
|
1098
1255
|
callApi: callTelegramApi,
|
|
@@ -1101,6 +1258,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1101
1258
|
},
|
|
1102
1259
|
getAdmissionScope: getTelegramUpdateAdmissionScope,
|
|
1103
1260
|
getAdmissionJournalBinding: getTelegramQueueJournalBinding,
|
|
1261
|
+
beginCommandEffectWork: publicationRuntime.beginWork,
|
|
1104
1262
|
getMessageOwnership: messageOwnershipRuntime.getForwardOwnership,
|
|
1105
1263
|
recordMessageOwnership: messageOwnershipRuntime.recordRouted,
|
|
1106
1264
|
...inboundBusProjectionRuntime,
|
|
@@ -1118,86 +1276,201 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1118
1276
|
getSessionId: Pi.getExtensionContextSessionId,
|
|
1119
1277
|
getCwd: Pi.getExtensionContextCwd,
|
|
1120
1278
|
getLeaderIdentity: telegramBusLeaderState.getIdentity,
|
|
1279
|
+
observeLeaderWork: observeLiveTargetWork,
|
|
1121
1280
|
followerRegistry: telegramBusFollowerRegistry,
|
|
1122
1281
|
runFollower: workspaceRestoreFollowerController,
|
|
1282
|
+
liveFollower: liveFollowerPorts,
|
|
1123
1283
|
},
|
|
1124
1284
|
getWorkspaceRestoreStore: resolveWorkspaceRestoreStore,
|
|
1125
1285
|
captureWorkspaceExternalProtection,
|
|
1126
1286
|
inspectRestoreSourceCompletion(expected) {
|
|
1127
|
-
return telegramJournalReferenceRegistry.withReference(
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1287
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
1288
|
+
{
|
|
1289
|
+
referenceClass: "operator-disposition",
|
|
1290
|
+
recoveryKey: expected.journalBindingKey,
|
|
1291
|
+
},
|
|
1292
|
+
function () {
|
|
1293
|
+
const observed =
|
|
1294
|
+
telegramJournalBindingRuntime.inspectSourceCompletion(
|
|
1295
|
+
expected.journalBindingKey,
|
|
1296
|
+
{
|
|
1297
|
+
updateId: expected.updateId,
|
|
1298
|
+
sourceSha256: expected.sourceSha256,
|
|
1299
|
+
completionSha256: expected.completionSha256,
|
|
1300
|
+
},
|
|
1301
|
+
);
|
|
1302
|
+
return (
|
|
1303
|
+
observed && {
|
|
1304
|
+
journalBindingKey: expected.journalBindingKey,
|
|
1305
|
+
...observed,
|
|
1306
|
+
}
|
|
1307
|
+
);
|
|
1308
|
+
},
|
|
1309
|
+
);
|
|
1133
1310
|
},
|
|
1134
1311
|
inspectRestoreQueuedReceipt(expected) {
|
|
1135
|
-
return telegramJournalReferenceRegistry.withReference(
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1312
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
1313
|
+
{
|
|
1314
|
+
referenceClass: "operator-disposition",
|
|
1315
|
+
recoveryKey: expected.journalBindingKey,
|
|
1316
|
+
},
|
|
1317
|
+
function () {
|
|
1318
|
+
return telegramJournalBindingRuntime.inspectQueuedReceipt(
|
|
1319
|
+
expected.journalBindingKey,
|
|
1320
|
+
{
|
|
1321
|
+
queueKind: expected.queueKind,
|
|
1322
|
+
receiptId: expected.receiptId,
|
|
1323
|
+
sourceUpdateIds: [...expected.sourceUpdateIds],
|
|
1324
|
+
queueOwner: { ...expected.queueOwner },
|
|
1325
|
+
},
|
|
1326
|
+
);
|
|
1327
|
+
},
|
|
1328
|
+
);
|
|
1140
1329
|
},
|
|
1141
1330
|
inspectRestoreSourceAbandonment(updateId, journalBindingKey) {
|
|
1142
|
-
return telegramJournalReferenceRegistry.withReference(
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1331
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
1332
|
+
{
|
|
1333
|
+
referenceClass: "operator-disposition",
|
|
1334
|
+
recoveryKey: journalBindingKey,
|
|
1335
|
+
},
|
|
1336
|
+
function () {
|
|
1337
|
+
return telegramJournalBindingRuntime.inspectSourceAbandonment(
|
|
1338
|
+
journalBindingKey,
|
|
1339
|
+
updateId,
|
|
1340
|
+
);
|
|
1341
|
+
},
|
|
1342
|
+
);
|
|
1148
1343
|
},
|
|
1149
1344
|
inspectRoutingInputGroupExpiry(input) {
|
|
1150
|
-
return telegramJournalReferenceRegistry.withReference(
|
|
1151
|
-
|
|
1345
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
1346
|
+
{
|
|
1347
|
+
referenceClass: "operator-disposition",
|
|
1348
|
+
recoveryKey: input.journalBindingKey,
|
|
1349
|
+
},
|
|
1350
|
+
function () {
|
|
1351
|
+
return telegramJournalBindingRuntime.inspectSourceGroupExpiry(
|
|
1352
|
+
input.journalBindingKey,
|
|
1353
|
+
input.updateIds,
|
|
1354
|
+
);
|
|
1355
|
+
},
|
|
1356
|
+
);
|
|
1152
1357
|
},
|
|
1153
|
-
inspectTemporaryThreadSources(
|
|
1358
|
+
inspectTemporaryThreadSources(
|
|
1359
|
+
target,
|
|
1360
|
+
requiredJournalBindingKeys,
|
|
1361
|
+
ownInputs,
|
|
1362
|
+
) {
|
|
1154
1363
|
const binding = resolveTelegramUpdateJournalBinding();
|
|
1155
1364
|
const botToken = configStore.getBotToken();
|
|
1156
1365
|
if (!binding || !botToken) return undefined;
|
|
1157
1366
|
const profile = configStore.getActiveProfileName() ?? "default";
|
|
1158
|
-
const botIdentity = Journal.createTelegramUpdateJournalBotIdentity({
|
|
1367
|
+
const botIdentity = Journal.createTelegramUpdateJournalBotIdentity({
|
|
1368
|
+
botToken,
|
|
1369
|
+
botId: getTelegramBotId(),
|
|
1370
|
+
});
|
|
1159
1371
|
return configStore.withSourceSerialization(function () {
|
|
1160
1372
|
return Journal.isTelegramThreadCleanupJournalNamespaceClear({
|
|
1161
|
-
directory: Paths.resolveTelegramTempDir(),
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1373
|
+
directory: Paths.resolveTelegramTempDir(),
|
|
1374
|
+
profile,
|
|
1375
|
+
botIdentity,
|
|
1376
|
+
pollingPath: telegramLeaderJournalPath.resolve(
|
|
1377
|
+
configStore.getActiveProfileName(),
|
|
1378
|
+
),
|
|
1379
|
+
requiredJournalBindingKeys: [
|
|
1380
|
+
binding.recoveryKey,
|
|
1381
|
+
...requiredJournalBindingKeys,
|
|
1382
|
+
],
|
|
1383
|
+
...(target.threadId !== undefined && ownInputs
|
|
1384
|
+
? {
|
|
1385
|
+
cleanup: {
|
|
1386
|
+
target: { chatId: target.chatId, threadId: target.threadId },
|
|
1387
|
+
ownInputs,
|
|
1388
|
+
},
|
|
1389
|
+
}
|
|
1390
|
+
: {}),
|
|
1391
|
+
limits: {
|
|
1392
|
+
maxDirectoryEntries: 10_000,
|
|
1393
|
+
maxFiles: 4096,
|
|
1394
|
+
maxBytes: 64 * 1024 * 1024,
|
|
1395
|
+
maxEntries: 10_000,
|
|
1396
|
+
maxWork: 100_000,
|
|
1397
|
+
},
|
|
1166
1398
|
withSourceReference(path, operation) {
|
|
1167
|
-
return telegramJournalReferenceRegistry.withReference(
|
|
1168
|
-
|
|
1399
|
+
return telegramJournalReferenceRegistry.withReference(
|
|
1400
|
+
{
|
|
1401
|
+
referenceClass: "operator-disposition",
|
|
1402
|
+
recoveryKey: Journal.createTelegramUpdateJournalBindingKey({
|
|
1403
|
+
path,
|
|
1404
|
+
profileName: profile,
|
|
1405
|
+
botIdentity,
|
|
1406
|
+
}),
|
|
1407
|
+
},
|
|
1408
|
+
operation,
|
|
1409
|
+
);
|
|
1169
1410
|
},
|
|
1170
|
-
})
|
|
1411
|
+
})
|
|
1412
|
+
? []
|
|
1413
|
+
: undefined;
|
|
1171
1414
|
});
|
|
1172
1415
|
},
|
|
1173
1416
|
hasWorkspaceRestoreAuthority() {
|
|
1174
|
-
return
|
|
1417
|
+
return (
|
|
1418
|
+
lockRuntime.owns() &&
|
|
1419
|
+
Bus.hasTelegramBusCapability(
|
|
1420
|
+
telegramBusProtocolIdentity,
|
|
1421
|
+
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE,
|
|
1422
|
+
)
|
|
1423
|
+
);
|
|
1424
|
+
},
|
|
1425
|
+
hasWorkspaceLiveRebindAuthority() {
|
|
1426
|
+
return (
|
|
1427
|
+
lockRuntime.owns() &&
|
|
1428
|
+
Bus.hasTelegramBusCapability(
|
|
1429
|
+
telegramBusProtocolIdentity,
|
|
1430
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE,
|
|
1431
|
+
) &&
|
|
1432
|
+
Bus.hasTelegramBusCapability(
|
|
1433
|
+
telegramBusProtocolIdentity,
|
|
1434
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY,
|
|
1435
|
+
) &&
|
|
1436
|
+
Bus.hasTelegramBusCapability(
|
|
1437
|
+
telegramBusProtocolIdentity,
|
|
1438
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE,
|
|
1439
|
+
)
|
|
1440
|
+
);
|
|
1175
1441
|
},
|
|
1176
1442
|
getSessionGeneration: telegramSessionContextStore.getGeneration,
|
|
1177
1443
|
bridgeRuntime,
|
|
1178
1444
|
requestNewSession(source) {
|
|
1179
|
-
const updateId =
|
|
1445
|
+
const updateId =
|
|
1446
|
+
Updates.getTelegramUpdateExecutionFence(source)?.updateId;
|
|
1180
1447
|
if (updateId === undefined) {
|
|
1181
|
-
throw new Error(
|
|
1448
|
+
throw new Error(
|
|
1449
|
+
"Telegram session replacement requires durable update authority.",
|
|
1450
|
+
);
|
|
1182
1451
|
}
|
|
1183
|
-
const callbackMessage =
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
typeof messageTarget.message_id === "number"
|
|
1193
|
-
? {
|
|
1194
|
-
chatId: messageTarget.chat.id,
|
|
1195
|
-
messageId: messageTarget.message_id,
|
|
1196
|
-
...(typeof messageTarget.message_thread_id === "number"
|
|
1197
|
-
? { threadId: messageTarget.message_thread_id }
|
|
1198
|
-
: {}),
|
|
1452
|
+
const callbackMessage =
|
|
1453
|
+
source && typeof source === "object" && "message" in source
|
|
1454
|
+
? (source as { message?: unknown }).message
|
|
1455
|
+
: source;
|
|
1456
|
+
const messageTarget = callbackMessage as
|
|
1457
|
+
| {
|
|
1458
|
+
chat?: { id?: unknown };
|
|
1459
|
+
message_id?: unknown;
|
|
1460
|
+
message_thread_id?: unknown;
|
|
1199
1461
|
}
|
|
1200
|
-
|
|
1462
|
+
| undefined;
|
|
1463
|
+
const target =
|
|
1464
|
+
typeof messageTarget?.chat?.id === "number" &&
|
|
1465
|
+
typeof messageTarget.message_id === "number"
|
|
1466
|
+
? {
|
|
1467
|
+
chatId: messageTarget.chat.id,
|
|
1468
|
+
messageId: messageTarget.message_id,
|
|
1469
|
+
...(typeof messageTarget.message_thread_id === "number"
|
|
1470
|
+
? { threadId: messageTarget.message_thread_id }
|
|
1471
|
+
: {}),
|
|
1472
|
+
}
|
|
1473
|
+
: undefined;
|
|
1201
1474
|
if (!target) {
|
|
1202
1475
|
throw new Error("Telegram session replacement target is unavailable.");
|
|
1203
1476
|
}
|
|
@@ -1214,6 +1487,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1214
1487
|
currentModelRuntime,
|
|
1215
1488
|
modelSwitchController,
|
|
1216
1489
|
menuActions,
|
|
1490
|
+
isVoiceReplyActive: menuActionPorts.isVoiceReplyActive,
|
|
1217
1491
|
updateSettingsMenuMessage: settingsMenuRuntime.updateSettingsMenuMessage,
|
|
1218
1492
|
openQueueMenu: queueMenuRuntime.openQueueMenu,
|
|
1219
1493
|
queueMenuCallbackHandler: queueMenuRuntime.handleCallbackQuery,
|
|
@@ -1226,6 +1500,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1226
1500
|
inboundHandlerRuntime,
|
|
1227
1501
|
threadStore,
|
|
1228
1502
|
runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
|
|
1503
|
+
liveRebindCleanupSchedule: Routing.TELEGRAM_LIVE_REBIND_CLEANUP_SCHEDULE,
|
|
1229
1504
|
updateStatus,
|
|
1230
1505
|
isContextActive: telegramSessionContextStore.isCurrent,
|
|
1231
1506
|
dispatchNextQueuedTelegramTurn,
|
|
@@ -1246,6 +1521,24 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1246
1521
|
startGuestPlaceholder: guestPlaceholderRuntime.start,
|
|
1247
1522
|
sendTextReply,
|
|
1248
1523
|
setMyCommands,
|
|
1524
|
+
captureThreadNameRecipientAuthority(target, ctx) {
|
|
1525
|
+
// Ordinary follower/classic dialogs keep their unguarded policy; closures are leader-local only.
|
|
1526
|
+
if (
|
|
1527
|
+
telegramBusFollowerRegistrationState.isRegistered() ||
|
|
1528
|
+
target.threadId === undefined
|
|
1529
|
+
)
|
|
1530
|
+
return undefined;
|
|
1531
|
+
return Threads.createTelegramWorkspaceThreadRenameRecipient({
|
|
1532
|
+
store: threadStore,
|
|
1533
|
+
instanceId: telegramInstanceId,
|
|
1534
|
+
target,
|
|
1535
|
+
assertAuthority() {
|
|
1536
|
+
if (telegramSessionContextStore.get() !== ctx)
|
|
1537
|
+
throw new Error("Telegram Thread name dialog lost Pi context.");
|
|
1538
|
+
},
|
|
1539
|
+
getAuthority: threadNameRecipientAuthority.getAuthority,
|
|
1540
|
+
}).assertAuthority;
|
|
1541
|
+
},
|
|
1249
1542
|
validateThreadName(threadName) {
|
|
1250
1543
|
return ThreadNaming.getTelegramManualThreadDisplayNameValidationError(
|
|
1251
1544
|
threadName,
|
|
@@ -1300,7 +1593,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1300
1593
|
runtimeBinding: updateAdmissionRuntimeBinding,
|
|
1301
1594
|
acquireSourceReference(role, binding) {
|
|
1302
1595
|
return telegramJournalReferenceRegistry.acquire({
|
|
1303
|
-
referenceClass:
|
|
1596
|
+
referenceClass:
|
|
1597
|
+
role === "leader" ? "leader-lifecycle" : "follower-lifecycle",
|
|
1304
1598
|
recoveryKey: binding.recoveryKey,
|
|
1305
1599
|
});
|
|
1306
1600
|
},
|
|
@@ -1321,28 +1615,46 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1321
1615
|
},
|
|
1322
1616
|
worker: {
|
|
1323
1617
|
expireRoutingInput: inboundRouteRuntime.expireRoutingInput,
|
|
1324
|
-
beforeQueueReceiptPublished:
|
|
1618
|
+
beforeQueueReceiptPublished:
|
|
1619
|
+
inboundRouteRuntime.beforeQueueReceiptPublished,
|
|
1325
1620
|
onQueueReceiptCompleted: inboundRouteRuntime.onQueueReceiptCompleted,
|
|
1326
1621
|
defaultHandle: inboundRouteRuntime.handleUpdate,
|
|
1327
1622
|
async shouldReviewHistoricalInput(entry, ctx, signal) {
|
|
1328
1623
|
if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
|
|
1329
1624
|
throw new Error("Workspace Restore source generation ended.");
|
|
1330
1625
|
}
|
|
1331
|
-
const verdict = await inboundRouteRuntime.shouldReviewHistoricalInput(
|
|
1626
|
+
const verdict = await inboundRouteRuntime.shouldReviewHistoricalInput(
|
|
1627
|
+
entry,
|
|
1628
|
+
ctx,
|
|
1629
|
+
signal,
|
|
1630
|
+
);
|
|
1332
1631
|
if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
|
|
1333
1632
|
throw new Error("Workspace Restore source generation ended.");
|
|
1334
1633
|
}
|
|
1335
1634
|
if (verdict === "retain") return verdict;
|
|
1336
1635
|
const journalBindingKey = getTelegramQueueJournalBinding();
|
|
1337
|
-
if (
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1636
|
+
if (
|
|
1637
|
+
journalBindingKey &&
|
|
1638
|
+
resolveWorkspaceRestoreStore()
|
|
1639
|
+
?.list()
|
|
1640
|
+
.some(function (intent) {
|
|
1641
|
+
return (
|
|
1642
|
+
intent.request.source.journalBindingKey === journalBindingKey &&
|
|
1643
|
+
intent.request.source.updateIds.includes(entry.updateId)
|
|
1644
|
+
);
|
|
1645
|
+
})
|
|
1646
|
+
)
|
|
1647
|
+
return true;
|
|
1341
1648
|
return verdict;
|
|
1342
1649
|
},
|
|
1343
1650
|
spendHistoricalInput: true,
|
|
1344
1651
|
onHeldSourcesPrepared(input): Promise<void> {
|
|
1345
|
-
return inboundRouteRuntime
|
|
1652
|
+
return inboundRouteRuntime
|
|
1653
|
+
.forgetPreviousWorld(
|
|
1654
|
+
input,
|
|
1655
|
+
lockedPollingRuntime.captureTransportAuthority,
|
|
1656
|
+
)
|
|
1657
|
+
.then(function () {});
|
|
1346
1658
|
},
|
|
1347
1659
|
async shouldHoldPendingInput(entry, ctx, signal) {
|
|
1348
1660
|
if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
|
|
@@ -1367,9 +1679,11 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1367
1679
|
isRegistered: telegramBusFollowerRegistrationState.isRegistered,
|
|
1368
1680
|
getGeneration: telegramBusFollowerRegistrationState.getGeneration,
|
|
1369
1681
|
prepareBinding(binding) {
|
|
1370
|
-
telegramJournalBindingRuntime.prepareActiveFollowerSuccession(
|
|
1371
|
-
|
|
1372
|
-
|
|
1682
|
+
telegramJournalBindingRuntime.prepareActiveFollowerSuccession(
|
|
1683
|
+
function () {
|
|
1684
|
+
return binding.hasAuthority?.() === true;
|
|
1685
|
+
},
|
|
1686
|
+
);
|
|
1373
1687
|
},
|
|
1374
1688
|
prepareUpdateForExecution(update) {
|
|
1375
1689
|
return BusFollower.prepareTelegramBusFollowerJournaledUpdateForExecution(
|
|
@@ -1450,32 +1764,159 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1450
1764
|
local: agentMessageRuntime,
|
|
1451
1765
|
follower: telegramBusFollowerClients.agentMessages,
|
|
1452
1766
|
});
|
|
1453
|
-
const
|
|
1767
|
+
const followerWorkspaceTargetContextPorts = {
|
|
1768
|
+
isContextCurrent: telegramSessionContextStore.isCurrent,
|
|
1769
|
+
getSessionId: Pi.getExtensionContextSessionId,
|
|
1770
|
+
getCwd: Pi.getExtensionContextCwd,
|
|
1771
|
+
getGeneration: telegramSessionContextStore.getGeneration,
|
|
1772
|
+
getProfileBindingKey() {
|
|
1773
|
+
return workspaceAdmissionRuntime.resolve()?.getProfileKey();
|
|
1774
|
+
},
|
|
1775
|
+
getOperatorUserId: configStore.getAllowedUserId,
|
|
1776
|
+
getLeaderState: lockRuntime.getState,
|
|
1777
|
+
getAuthenticatedSecret: telegramBusFollowerControlState.getActiveAuthSecret,
|
|
1778
|
+
getLeaderProtocol: telegramBusFollowerRegistrationState.getLeaderProtocol,
|
|
1779
|
+
};
|
|
1780
|
+
const followerWorkspaceTargetOwnerPorts = {
|
|
1454
1781
|
instanceId: telegramInstanceId,
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1782
|
+
readRestoreIntent(operationId: string, profileBindingKey: string) {
|
|
1783
|
+
if (
|
|
1784
|
+
profileBindingKey !==
|
|
1785
|
+
workspaceAdmissionRuntime.resolve()?.getProfileKey()
|
|
1786
|
+
)
|
|
1787
|
+
return undefined;
|
|
1788
|
+
return resolveWorkspaceRestoreStore()
|
|
1789
|
+
?.list()
|
|
1790
|
+
.find(function (intent) {
|
|
1791
|
+
return intent.request.operationId === operationId;
|
|
1792
|
+
});
|
|
1793
|
+
},
|
|
1794
|
+
readLiveRebindIntent(operationId: string, profileBindingKey: string) {
|
|
1795
|
+
if (
|
|
1796
|
+
profileBindingKey !==
|
|
1797
|
+
workspaceAdmissionRuntime.resolve()?.getProfileKey()
|
|
1798
|
+
)
|
|
1799
|
+
return undefined;
|
|
1800
|
+
return resolveWorkspaceRestoreStore()
|
|
1801
|
+
?.listLiveRebindings()
|
|
1802
|
+
.find(function (intent) {
|
|
1803
|
+
return intent.request.operationId === operationId;
|
|
1804
|
+
});
|
|
1473
1805
|
},
|
|
1474
1806
|
topicTargetStore: threadStore,
|
|
1475
1807
|
registrationState: telegramBusFollowerRegistrationState,
|
|
1476
1808
|
getWorkspaceAdmission: workspaceAdmissionRuntime.resolve,
|
|
1477
1809
|
recordRuntimeEvent,
|
|
1478
|
-
}
|
|
1810
|
+
};
|
|
1811
|
+
const followerWorkspaceRestore =
|
|
1812
|
+
BusFollower.createTelegramBusFollowerWorkspaceRestoreHandler({
|
|
1813
|
+
...followerWorkspaceTargetOwnerPorts,
|
|
1814
|
+
getContextAuthority:
|
|
1815
|
+
BusFollower.createTelegramBusFollowerRestoreContextGetter(
|
|
1816
|
+
followerWorkspaceTargetContextPorts,
|
|
1817
|
+
),
|
|
1818
|
+
});
|
|
1819
|
+
const followerLiveRebindTarget =
|
|
1820
|
+
BusFollower.createTelegramBusFollowerWorkspaceRestoreHandler({
|
|
1821
|
+
...followerWorkspaceTargetOwnerPorts,
|
|
1822
|
+
getContextAuthority:
|
|
1823
|
+
BusFollower.createTelegramBusFollowerRestoreContextGetter({
|
|
1824
|
+
...followerWorkspaceTargetContextPorts,
|
|
1825
|
+
capability: Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY,
|
|
1826
|
+
}),
|
|
1827
|
+
});
|
|
1828
|
+
const selectedMenuCaller =
|
|
1829
|
+
BusFollower.createTelegramBusFollowerSelectedMenuCaller({
|
|
1830
|
+
client: {
|
|
1831
|
+
socketPath: getTelegramBusSocketPath,
|
|
1832
|
+
instanceId: telegramInstanceId,
|
|
1833
|
+
createRequestId: telegramBusFollowerClients.createRequestId,
|
|
1834
|
+
getAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
|
|
1835
|
+
getRegistrationGeneration:
|
|
1836
|
+
telegramBusFollowerRegistrationState.getGeneration,
|
|
1837
|
+
},
|
|
1838
|
+
protocolIdentity: telegramBusProtocolIdentity,
|
|
1839
|
+
recipient: {
|
|
1840
|
+
getContextAuthority:
|
|
1841
|
+
BusFollower.createTelegramBusFollowerRestoreContextGetter({
|
|
1842
|
+
...followerWorkspaceTargetContextPorts,
|
|
1843
|
+
// Wire recipient identity is the registered profile key, not the admission namespace.
|
|
1844
|
+
getProfileBindingKey: getTelegramManualFollowerProfileKey,
|
|
1845
|
+
capability: Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
|
|
1846
|
+
}),
|
|
1847
|
+
getJournalBindingKey:
|
|
1848
|
+
followerAdmissionLifecycleRuntime.getJournalBindingKey,
|
|
1849
|
+
getProcessIdentity() {
|
|
1850
|
+
return {
|
|
1851
|
+
processId: telegramProcessId,
|
|
1852
|
+
processBirthId: telegramQueueProcessBirthId,
|
|
1853
|
+
};
|
|
1854
|
+
},
|
|
1855
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
1856
|
+
},
|
|
1857
|
+
});
|
|
1858
|
+
const followerLiveRebindRuntime =
|
|
1859
|
+
BusFollower.createTelegramBusFollowerLiveRebindRuntime({
|
|
1860
|
+
getAdmission(ctx: Pi.ExtensionContext) {
|
|
1861
|
+
return telegramSessionContextStore.isCurrent(ctx)
|
|
1862
|
+
? followerAdmissionLifecycleRuntime
|
|
1863
|
+
: undefined;
|
|
1864
|
+
},
|
|
1865
|
+
applyTarget: followerLiveRebindTarget,
|
|
1866
|
+
getCommandOwner(input, ctx) {
|
|
1867
|
+
if (
|
|
1868
|
+
!Bus.hasTelegramBusCapability(
|
|
1869
|
+
telegramBusProtocolIdentity,
|
|
1870
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
|
|
1871
|
+
) ||
|
|
1872
|
+
!Bus.hasTelegramBusCapability(
|
|
1873
|
+
telegramBusProtocolIdentity,
|
|
1874
|
+
Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
|
|
1875
|
+
) ||
|
|
1876
|
+
!input.selectedCommand ||
|
|
1877
|
+
!telegramSessionContextStore.isCurrent(ctx) ||
|
|
1878
|
+
followerAdmissionLifecycleRuntime.getJournalBindingKey() !==
|
|
1879
|
+
input.recipientBindingKey
|
|
1880
|
+
)
|
|
1881
|
+
return undefined;
|
|
1882
|
+
const recipient = followerLiveRebindTarget.prepareLiveCommandRecipient(
|
|
1883
|
+
{
|
|
1884
|
+
operationId: input.operationId,
|
|
1885
|
+
registrationGeneration: input.recipientRegistrationGeneration,
|
|
1886
|
+
sessionId: input.recipientSessionId,
|
|
1887
|
+
sourceUpdateIds: input.updates.map(function sourceUpdateId(update) {
|
|
1888
|
+
return update.update_id;
|
|
1889
|
+
}),
|
|
1890
|
+
target: input.selectedCommand.target,
|
|
1891
|
+
},
|
|
1892
|
+
ctx,
|
|
1893
|
+
);
|
|
1894
|
+
return (
|
|
1895
|
+
recipient &&
|
|
1896
|
+
Bindings.createTelegramFollowerSelectedCommandBinding({
|
|
1897
|
+
ctx,
|
|
1898
|
+
operationId: input.operationId,
|
|
1899
|
+
registrationGeneration: input.recipientRegistrationGeneration,
|
|
1900
|
+
name: input.selectedCommand.name,
|
|
1901
|
+
target: input.selectedCommand.target,
|
|
1902
|
+
recipient,
|
|
1903
|
+
command: inboundRouteRuntime,
|
|
1904
|
+
menu: menuActionPorts,
|
|
1905
|
+
recordOwnership: messageOwnershipRuntime.recordLocal,
|
|
1906
|
+
deliver(effect, assertAuthority) {
|
|
1907
|
+
return selectedMenuCaller({
|
|
1908
|
+
ctx,
|
|
1909
|
+
operationId: input.operationId,
|
|
1910
|
+
registrationGeneration: input.recipientRegistrationGeneration,
|
|
1911
|
+
effect,
|
|
1912
|
+
assertAuthority,
|
|
1913
|
+
});
|
|
1914
|
+
},
|
|
1915
|
+
})
|
|
1916
|
+
);
|
|
1917
|
+
},
|
|
1918
|
+
observeWork: observeLiveTargetWork,
|
|
1919
|
+
});
|
|
1479
1920
|
const telegramBusFollowerAssembly: BusFollower.TelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext> =
|
|
1480
1921
|
BusFollower.createTelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext>({
|
|
1481
1922
|
instanceId: telegramInstanceId,
|
|
@@ -1486,12 +1927,63 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1486
1927
|
getContext: telegramSessionContextStore.get,
|
|
1487
1928
|
getAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
|
|
1488
1929
|
getRecipientBindingKey: getTelegramManualFollowerProfileKey,
|
|
1930
|
+
getLiveRebindJournalBindingKey:
|
|
1931
|
+
followerAdmissionLifecycleRuntime.getJournalBindingKey,
|
|
1932
|
+
getSessionId() {
|
|
1933
|
+
return telegramBusFollowerAssembly.getReadySessionId();
|
|
1934
|
+
},
|
|
1935
|
+
getLeaderProtocol:
|
|
1936
|
+
telegramBusFollowerRegistrationState.getLeaderProtocol,
|
|
1937
|
+
getLocalProtocol() {
|
|
1938
|
+
return telegramBusProtocolIdentity;
|
|
1939
|
+
},
|
|
1940
|
+
isLiveRebindSaveEnabled() {
|
|
1941
|
+
return Bus.hasTelegramBusCapability(
|
|
1942
|
+
telegramBusProtocolIdentity,
|
|
1943
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE,
|
|
1944
|
+
);
|
|
1945
|
+
},
|
|
1946
|
+
isLiveRebindApplyEnabled() {
|
|
1947
|
+
return Bus.hasTelegramBusCapability(
|
|
1948
|
+
telegramBusProtocolIdentity,
|
|
1949
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY,
|
|
1950
|
+
);
|
|
1951
|
+
},
|
|
1952
|
+
isLiveRebindSettleEnabled() {
|
|
1953
|
+
return Bus.hasTelegramBusCapability(
|
|
1954
|
+
telegramBusProtocolIdentity,
|
|
1955
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE,
|
|
1956
|
+
);
|
|
1957
|
+
},
|
|
1958
|
+
isLiveRebindCommandSetEnabled() {
|
|
1959
|
+
return (
|
|
1960
|
+
Bus.hasTelegramBusCapability(
|
|
1961
|
+
telegramBusProtocolIdentity,
|
|
1962
|
+
Bus.TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
|
|
1963
|
+
) &&
|
|
1964
|
+
Bus.hasTelegramBusCapability(
|
|
1965
|
+
telegramBusProtocolIdentity,
|
|
1966
|
+
Bus.TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
|
|
1967
|
+
)
|
|
1968
|
+
);
|
|
1969
|
+
},
|
|
1970
|
+
handleLiveRebindSave: followerLiveRebindRuntime.save,
|
|
1971
|
+
handleLiveRebindApply: followerLiveRebindRuntime.apply,
|
|
1972
|
+
handleLiveRebindSettle: followerLiveRebindRuntime.settle,
|
|
1489
1973
|
durableAdmission: followerDurableAdmissionRuntime,
|
|
1490
1974
|
handleQueueHandoff: acceptStagedQueueHandoff,
|
|
1491
1975
|
handleWorkspaceRestore: followerWorkspaceRestore,
|
|
1492
1976
|
isWorkspaceRestoreEnabled() {
|
|
1493
|
-
return
|
|
1494
|
-
Bus.hasTelegramBusCapability(
|
|
1977
|
+
return (
|
|
1978
|
+
Bus.hasTelegramBusCapability(
|
|
1979
|
+
telegramBusProtocolIdentity,
|
|
1980
|
+
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE,
|
|
1981
|
+
) &&
|
|
1982
|
+
Bus.hasTelegramBusCapability(
|
|
1983
|
+
telegramBusFollowerRegistrationState.getLeaderProtocol(),
|
|
1984
|
+
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE,
|
|
1985
|
+
)
|
|
1986
|
+
);
|
|
1495
1987
|
},
|
|
1496
1988
|
},
|
|
1497
1989
|
recovery: {
|
|
@@ -1534,7 +2026,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1534
2026
|
>({
|
|
1535
2027
|
state: pollingControllerState,
|
|
1536
2028
|
canStart(ctx) {
|
|
1537
|
-
return
|
|
2029
|
+
return (
|
|
2030
|
+
telegramSessionContextStore.isCurrent(ctx) && lockRuntime.owns(ctx)
|
|
2031
|
+
);
|
|
1538
2032
|
},
|
|
1539
2033
|
onPersistentConflict(ctx, count): Promise<void> {
|
|
1540
2034
|
return lockedPollingRuntime.onPersistentConflict(ctx, count);
|
|
@@ -1557,7 +2051,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1557
2051
|
registry: telegramJournalReferenceRegistry,
|
|
1558
2052
|
resolveBinding: resolveTelegramUpdateJournalBinding,
|
|
1559
2053
|
referenceClass: "polling-cursor",
|
|
1560
|
-
operation(binding) {
|
|
2054
|
+
operation(binding) {
|
|
2055
|
+
return binding.journal.read().acceptedThroughUpdateId;
|
|
2056
|
+
},
|
|
1561
2057
|
});
|
|
1562
2058
|
},
|
|
1563
2059
|
async prepareCursorCutover() {
|
|
@@ -1565,30 +2061,37 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1565
2061
|
registry: telegramJournalReferenceRegistry,
|
|
1566
2062
|
resolveBinding: resolveTelegramUpdateJournalBinding,
|
|
1567
2063
|
referenceClass: "polling-cursor",
|
|
1568
|
-
operation(binding) {
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
2064
|
+
operation(binding) {
|
|
2065
|
+
return Polling.cutOverTelegramPollingCursor({
|
|
2066
|
+
getLegacyCursor: configStore.getLegacyPollingCursor,
|
|
2067
|
+
readJournal: binding.journal.read,
|
|
2068
|
+
publishJournalCursor(acceptedThroughUpdateId) {
|
|
2069
|
+
binding.journal.appendBatch([], acceptedThroughUpdateId);
|
|
2070
|
+
},
|
|
2071
|
+
async removeLegacyCursor() {
|
|
2072
|
+
configStore.removeLegacyPollingCursor();
|
|
2073
|
+
await persistTelegramConfigWithSync();
|
|
2074
|
+
},
|
|
2075
|
+
});
|
|
2076
|
+
},
|
|
1579
2077
|
});
|
|
1580
|
-
if (!cutover)
|
|
2078
|
+
if (!cutover)
|
|
2079
|
+
throw new Error("Telegram update journal binding is unavailable.");
|
|
1581
2080
|
await cutover;
|
|
1582
2081
|
},
|
|
1583
2082
|
getEntryCount: updateAdmissionLifecycleRuntime.getJournalEntryCount,
|
|
1584
2083
|
signalWorker: updateAdmissionLifecycleRuntime.signal,
|
|
1585
2084
|
getBootstrapEntryCount() {
|
|
1586
|
-
return
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
2085
|
+
return (
|
|
2086
|
+
Journal.withTelegramResolvedUpdateJournalReference({
|
|
2087
|
+
registry: telegramJournalReferenceRegistry,
|
|
2088
|
+
resolveBinding: resolveTelegramUpdateJournalBinding,
|
|
2089
|
+
referenceClass: "polling-bootstrap",
|
|
2090
|
+
operation(binding) {
|
|
2091
|
+
return binding.journal.read().entries.length;
|
|
2092
|
+
},
|
|
2093
|
+
}) ?? 0
|
|
2094
|
+
);
|
|
1592
2095
|
},
|
|
1593
2096
|
onSessionStart: updateAdmissionLifecycleRuntime.onSessionStart,
|
|
1594
2097
|
},
|
|
@@ -1600,17 +2103,67 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1600
2103
|
const authorizeFollowerApiCall = Bus.createTelegramFollowerApiCallAuthorizer({
|
|
1601
2104
|
isMessageOwned: messageOwnershipRuntime.isOwnedByFollower,
|
|
1602
2105
|
});
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
2106
|
+
const selectedMenuDelivery =
|
|
2107
|
+
BusLeader.createTelegramBusSelectedMenuDeliveryHandler({
|
|
2108
|
+
followerRegistry: telegramBusFollowerRegistry,
|
|
2109
|
+
protocolIdentity: telegramBusProtocolIdentity,
|
|
2110
|
+
workspace: {
|
|
2111
|
+
getScopeKey() {
|
|
2112
|
+
return workspaceAdmissionRuntime.resolve()?.getProfileKey();
|
|
2113
|
+
},
|
|
2114
|
+
captureAuthority() {
|
|
2115
|
+
const isCurrent = captureTelegramStateAuthority(),
|
|
2116
|
+
epoch = lockRuntime.getOwnedLeaderEpoch(),
|
|
2117
|
+
operatorUserId = configStore.getAllowedUserId();
|
|
2118
|
+
return isCurrent &&
|
|
2119
|
+
epoch !== undefined &&
|
|
2120
|
+
operatorUserId !== undefined
|
|
2121
|
+
? {
|
|
2122
|
+
executor: {
|
|
2123
|
+
instanceId: telegramInstanceId,
|
|
2124
|
+
leaderEpoch: String(epoch),
|
|
2125
|
+
},
|
|
2126
|
+
operatorUserId,
|
|
2127
|
+
isCurrent,
|
|
2128
|
+
}
|
|
2129
|
+
: undefined;
|
|
2130
|
+
},
|
|
2131
|
+
getStore: resolveWorkspaceRestoreStore,
|
|
2132
|
+
threadStore,
|
|
2133
|
+
getJournalBindingKey: liveFollowerPorts.getJournalBindingKey,
|
|
2134
|
+
run: telegramWorkspaceOperationRuntime.run,
|
|
2135
|
+
},
|
|
2136
|
+
api: {
|
|
2137
|
+
runtime: directTelegramApiRuntime,
|
|
2138
|
+
authorize: authorizeFollowerApiCall,
|
|
2139
|
+
record(record, assertCurrent) {
|
|
2140
|
+
assertCurrent();
|
|
2141
|
+
messageOwnershipRuntime.recordFollower(record);
|
|
2142
|
+
assertCurrent();
|
|
2143
|
+
return messageOwnershipRuntime.isOwnedByFollower(record);
|
|
2144
|
+
},
|
|
2145
|
+
},
|
|
2146
|
+
});
|
|
2147
|
+
const telegramSessionFolderSweeper =
|
|
2148
|
+
Recovery.createTelegramSessionFolderSweeper({
|
|
2149
|
+
getSessionsDir: Paths.resolveTelegramSessionsDir,
|
|
2150
|
+
getProfileName: configStore.getActiveProfileName,
|
|
2151
|
+
getKeptSessionIds() {
|
|
2152
|
+
return [
|
|
2153
|
+
...threadStore
|
|
2154
|
+
.listWorkspaceBindings()
|
|
2155
|
+
.map(function bindingSession(binding) {
|
|
2156
|
+
return binding.sessionId;
|
|
2157
|
+
}),
|
|
2158
|
+
...telegramBusFollowerRegistry
|
|
2159
|
+
.list()
|
|
2160
|
+
.map(function followerSession(follower) {
|
|
2161
|
+
return follower.sessionId;
|
|
2162
|
+
}),
|
|
2163
|
+
Pi.getExtensionContextSessionId(telegramSessionContextStore.get()),
|
|
2164
|
+
];
|
|
2165
|
+
},
|
|
2166
|
+
});
|
|
1614
2167
|
const telegramBusLeaderRuntime =
|
|
1615
2168
|
BusLeader.createTelegramBusLeaderRuntimeAssembly<Pi.ExtensionContext>({
|
|
1616
2169
|
runtime: {
|
|
@@ -1621,6 +2174,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1621
2174
|
followerRegistry: telegramBusFollowerRegistry,
|
|
1622
2175
|
authSecret: telegramBusAuthSecret,
|
|
1623
2176
|
protocolIdentity: telegramBusProtocolIdentity,
|
|
2177
|
+
selectedMenuDelivery,
|
|
1624
2178
|
startPolling: pollingAdmissionRuntime.start,
|
|
1625
2179
|
stopPolling: pollingAdmissionRuntime.stop,
|
|
1626
2180
|
authorizeFollowerApiCall,
|
|
@@ -1634,15 +2188,21 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1634
2188
|
message,
|
|
1635
2189
|
});
|
|
1636
2190
|
},
|
|
1637
|
-
isFollowerProcessAlive:
|
|
1638
|
-
afterFollowerPrune() {
|
|
2191
|
+
isFollowerProcessAlive: ProcessIdentity.isProcessAlive,
|
|
2192
|
+
afterFollowerPrune() {
|
|
2193
|
+
telegramSessionFolderSweeper.sweep();
|
|
2194
|
+
},
|
|
1639
2195
|
shouldCleanupConfirmedDeadFollower:
|
|
1640
2196
|
configControls.resolveAutomaticThreadCleanupEnabled,
|
|
1641
2197
|
recordFollowerMessageOwnership(record) {
|
|
1642
2198
|
messageOwnershipRuntime.recordFollower(record);
|
|
1643
2199
|
},
|
|
1644
2200
|
onWorkspaceRestoreRecipientObserved(follower, isCurrent) {
|
|
1645
|
-
return inboundRouteRuntime.onWorkspaceRestoreRecipientObserved(
|
|
2201
|
+
return inboundRouteRuntime.onWorkspaceRestoreRecipientObserved(
|
|
2202
|
+
follower,
|
|
2203
|
+
isCurrent,
|
|
2204
|
+
telegramSessionContextStore.get(),
|
|
2205
|
+
);
|
|
1646
2206
|
},
|
|
1647
2207
|
},
|
|
1648
2208
|
getAllowedUserId: configStore.getAllowedUserId,
|
|
@@ -1654,7 +2214,11 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1654
2214
|
return Config.resolveTelegramThreadDisplayMode(configStore.get());
|
|
1655
2215
|
},
|
|
1656
2216
|
persistThreadDisplayMode(mode, isCurrent) {
|
|
1657
|
-
return Config.setTelegramThreadDisplayMode(
|
|
2217
|
+
return Config.setTelegramThreadDisplayMode(
|
|
2218
|
+
configStore,
|
|
2219
|
+
mode,
|
|
2220
|
+
isCurrent,
|
|
2221
|
+
);
|
|
1658
2222
|
},
|
|
1659
2223
|
onThreadDisplayChanged() {
|
|
1660
2224
|
const ctx = telegramSessionContextStore.get();
|
|
@@ -1674,11 +2238,19 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1674
2238
|
deleteThread: directTelegramApiRuntime.deleteWorkspaceThread,
|
|
1675
2239
|
pruneJournalEvidence: pruneWorkspaceJournalEvidence,
|
|
1676
2240
|
reclaimDeadOwnerQueuedWork(binding, isCurrent) {
|
|
1677
|
-
return telegramWorkspaceOperationRuntime.run(
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
2241
|
+
return telegramWorkspaceOperationRuntime.run(
|
|
2242
|
+
{
|
|
2243
|
+
operationId:
|
|
2244
|
+
WorkspaceAdmission.createTelegramWorkspaceAdmissionOperationId(),
|
|
2245
|
+
operationKind: "workspace.reclaim-dead-owner-queue",
|
|
2246
|
+
scopes: [{ kind: "target", target: binding.target }],
|
|
2247
|
+
},
|
|
2248
|
+
reclaimTelegramWorkspaceDeadOwnerQueue.bind(
|
|
2249
|
+
undefined,
|
|
2250
|
+
binding,
|
|
2251
|
+
isCurrent,
|
|
2252
|
+
),
|
|
2253
|
+
);
|
|
1682
2254
|
},
|
|
1683
2255
|
},
|
|
1684
2256
|
callApi(method, body, options) {
|
|
@@ -1776,10 +2348,19 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1776
2348
|
const lockedPollingRuntime = Locks.createTelegramLockedPollingRuntime({
|
|
1777
2349
|
lock: lockRuntime,
|
|
1778
2350
|
resetDamagedState() {
|
|
1779
|
-
return Locks.resetDamagedTelegramRuntimeState(
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2351
|
+
return Locks.resetDamagedTelegramRuntimeState(
|
|
2352
|
+
Paths.resolveTelegramStatePath(),
|
|
2353
|
+
function (profile, sections) {
|
|
2354
|
+
Threads.parseTelegramWorkspaceStateSection(
|
|
2355
|
+
sections.workspace,
|
|
2356
|
+
profile,
|
|
2357
|
+
);
|
|
2358
|
+
WorkspaceAdmission.assertTelegramConsolidatedAdmissionSection(
|
|
2359
|
+
sections.admission,
|
|
2360
|
+
profile,
|
|
2361
|
+
);
|
|
2362
|
+
},
|
|
2363
|
+
);
|
|
1783
2364
|
},
|
|
1784
2365
|
transportMonitor: telegramThreadCapabilityMonitor,
|
|
1785
2366
|
hasBotToken: configStore.hasBotToken,
|
|
@@ -1791,8 +2372,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1791
2372
|
stopPolling: threadAwarePollingPorts.stopPolling,
|
|
1792
2373
|
registerFollowerWithOwner:
|
|
1793
2374
|
threadAwarePollingPorts.registerFollowerWithOwner,
|
|
1794
|
-
restoreFollowerWithOwner:
|
|
1795
|
-
threadAwarePollingPorts.restoreFollowerWithOwner,
|
|
2375
|
+
restoreFollowerWithOwner: threadAwarePollingPorts.restoreFollowerWithOwner,
|
|
1796
2376
|
stopFollowerRegistration: threadAwarePollingPorts.stopFollowerRegistration,
|
|
1797
2377
|
onTransportAvailabilityChanged() {
|
|
1798
2378
|
modelContextAvailabilityRuntime.reconcile();
|
|
@@ -1823,15 +2403,17 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1823
2403
|
recordRuntimeEvent,
|
|
1824
2404
|
runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
|
|
1825
2405
|
});
|
|
1826
|
-
const prepareThreadPreservationOnQuit =
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
2406
|
+
const prepareThreadPreservationOnQuit =
|
|
2407
|
+
Sync.createTelegramPreservedLeaderQuitHandler({
|
|
2408
|
+
instanceId: telegramInstanceId,
|
|
2409
|
+
topicTargetStore: threadStore,
|
|
2410
|
+
getCurrentLeaderEpoch,
|
|
2411
|
+
getProfileName: configStore.getActiveProfileName,
|
|
2412
|
+
isPollingSuspended: lockedPollingRuntime.isSuspended,
|
|
2413
|
+
resolveAutomaticThreadCleanupEnabled:
|
|
2414
|
+
configControls.resolveAutomaticThreadCleanupEnabled,
|
|
2415
|
+
runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
|
|
2416
|
+
});
|
|
1835
2417
|
const connectionIntent = Lifecycle.createTelegramConnectionIntentRuntime();
|
|
1836
2418
|
const connectionLifecycle = Lifecycle.createTelegramConnectionLifecycle({
|
|
1837
2419
|
intent: connectionIntent,
|
|
@@ -1839,12 +2421,17 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1839
2421
|
isCurrent: telegramSessionContextStore.isCurrent,
|
|
1840
2422
|
getProfileName: configStore.getActiveProfileName,
|
|
1841
2423
|
isConnected() {
|
|
1842
|
-
return
|
|
2424
|
+
return (
|
|
2425
|
+
lockRuntime.owns() ||
|
|
2426
|
+
telegramBusFollowerRegistrationState.isRegistered()
|
|
2427
|
+
);
|
|
1843
2428
|
},
|
|
1844
2429
|
async activateProfile(profileName, isCurrent) {
|
|
1845
2430
|
await configStore.load();
|
|
1846
2431
|
if (!isCurrent()) return false;
|
|
1847
|
-
return
|
|
2432
|
+
return (
|
|
2433
|
+
configStore.activateProfile(profileName) && configStore.hasBotToken()
|
|
2434
|
+
);
|
|
1848
2435
|
},
|
|
1849
2436
|
start(ctx) {
|
|
1850
2437
|
return lockedPollingRuntime.start(ctx);
|
|
@@ -1866,7 +2453,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1866
2453
|
syncFlags: lifecycle.syncFlags,
|
|
1867
2454
|
bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
|
|
1868
2455
|
prepareTempDir() {
|
|
1869
|
-
Recovery.removeTelegramLegacyRecoveryStorage(
|
|
2456
|
+
Recovery.removeTelegramLegacyRecoveryStorage(
|
|
2457
|
+
Paths.resolveTelegramTempDir(),
|
|
2458
|
+
);
|
|
1870
2459
|
return prepareTempDir();
|
|
1871
2460
|
},
|
|
1872
2461
|
updateStatus,
|
|
@@ -1921,101 +2510,203 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1921
2510
|
|
|
1922
2511
|
// --- Extension API Bindings ---
|
|
1923
2512
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
threadName: string,
|
|
1928
|
-
) {
|
|
1929
|
-
try {
|
|
1930
|
-
if (telegramBusFollowerRegistrationState.isRegistered()) {
|
|
1931
|
-
if (typeof expectedTarget.threadId !== "number") {
|
|
1932
|
-
return { ok: false, message: "Telegram Workspace Thread target is unavailable." };
|
|
1933
|
-
}
|
|
1934
|
-
const renamedThreadName =
|
|
1935
|
-
await telegramBusFollowerRegistration.renameThread?.(
|
|
1936
|
-
{ chatId: expectedTarget.chatId, threadId: expectedTarget.threadId },
|
|
1937
|
-
threadName,
|
|
1938
|
-
);
|
|
1939
|
-
if (!renamedThreadName) {
|
|
1940
|
-
return {
|
|
1941
|
-
ok: false,
|
|
1942
|
-
message: "Telegram follower Workspace Thread rename is unavailable.",
|
|
1943
|
-
};
|
|
1944
|
-
}
|
|
1945
|
-
return {
|
|
1946
|
-
ok: true,
|
|
1947
|
-
threadName: renamedThreadName,
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
if (!ownsTelegramDirectDelivery()) {
|
|
1951
|
-
return {
|
|
1952
|
-
ok: false,
|
|
1953
|
-
message: "Telegram Workspace Thread rename requires an active leader or follower connection.",
|
|
1954
|
-
};
|
|
1955
|
-
}
|
|
1956
|
-
if (typeof expectedTarget.threadId !== "number") {
|
|
1957
|
-
return { ok: false, message: "Telegram Workspace Thread target is unavailable." };
|
|
1958
|
-
}
|
|
1959
|
-
const renamed = await telegramBusLeaderRuntime.renameLeaderThreadAdmitted(
|
|
1960
|
-
threadName,
|
|
1961
|
-
{ chatId: expectedTarget.chatId, threadId: expectedTarget.threadId },
|
|
1962
|
-
);
|
|
1963
|
-
telegramBusLeaderState.set({
|
|
1964
|
-
target: renamed.target,
|
|
1965
|
-
slot: renamed.slot,
|
|
1966
|
-
threadName: renamed.manualThreadName ?? renamed.threadName,
|
|
1967
|
-
});
|
|
2513
|
+
const threadNameRecipientAuthority = {
|
|
2514
|
+
getAuthority(): Threads.TelegramWorkspaceThreadRenameAuthority {
|
|
2515
|
+
const ctx = telegramSessionContextStore.get();
|
|
1968
2516
|
return {
|
|
1969
|
-
|
|
1970
|
-
|
|
2517
|
+
context: ctx,
|
|
2518
|
+
sessionId: Pi.getExtensionContextSessionId(ctx),
|
|
2519
|
+
sessionGeneration: telegramSessionContextStore.getGeneration(),
|
|
2520
|
+
cwd: ctx && Pi.getExtensionContextCwd(ctx),
|
|
2521
|
+
profileName: configStore.getActiveProfileName(),
|
|
2522
|
+
botToken: configStore.getBotToken(),
|
|
2523
|
+
operatorUserId: configStore.getAllowedUserId(),
|
|
2524
|
+
leaderEpoch: getCurrentLeaderEpoch(),
|
|
2525
|
+
ownsDirectDelivery: ownsTelegramDirectDelivery(),
|
|
2526
|
+
followerRegistered: telegramBusFollowerRegistrationState.isRegistered(),
|
|
2527
|
+
localTarget: telegramBusLeaderState.getTarget(),
|
|
2528
|
+
localSlot: telegramBusLeaderState.getIdentity()?.slot,
|
|
1971
2529
|
};
|
|
1972
|
-
} catch (error) {
|
|
1973
|
-
return {
|
|
1974
|
-
ok: false,
|
|
1975
|
-
message:
|
|
1976
|
-
error instanceof Error
|
|
1977
|
-
? error.message
|
|
1978
|
-
: "Telegram Workspace Thread rename failed.",
|
|
1979
|
-
};
|
|
1980
|
-
}
|
|
1981
2530
|
},
|
|
1982
|
-
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2531
|
+
};
|
|
2532
|
+
telegramThreadDisplayNameRenameBinding.bind(
|
|
2533
|
+
{
|
|
2534
|
+
async rename(
|
|
2535
|
+
expectedTarget: Parameters<Commands.TelegramThreadDisplayNameRenamePort>[0],
|
|
2536
|
+
threadName: string,
|
|
2537
|
+
options?: Parameters<Commands.TelegramThreadDisplayNameRenamePort>[2],
|
|
2538
|
+
) {
|
|
2539
|
+
try {
|
|
2540
|
+
const assertCaller = options?.assertAuthority;
|
|
2541
|
+
assertCaller?.();
|
|
2542
|
+
if (telegramBusFollowerRegistrationState.isRegistered()) {
|
|
2543
|
+
if (assertCaller)
|
|
2544
|
+
return {
|
|
2545
|
+
ok: false,
|
|
2546
|
+
message:
|
|
2547
|
+
"Guarded follower Workspace Thread rename is unavailable.",
|
|
2548
|
+
};
|
|
2549
|
+
if (typeof expectedTarget.threadId !== "number") {
|
|
2550
|
+
return {
|
|
2551
|
+
ok: false,
|
|
2552
|
+
message: "Telegram Workspace Thread target is unavailable.",
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
const renamedThreadName =
|
|
2556
|
+
await telegramBusFollowerRegistration.renameThread?.(
|
|
2557
|
+
{
|
|
2558
|
+
chatId: expectedTarget.chatId,
|
|
2559
|
+
threadId: expectedTarget.threadId,
|
|
2560
|
+
},
|
|
2561
|
+
threadName,
|
|
2562
|
+
);
|
|
2563
|
+
if (!renamedThreadName) {
|
|
2564
|
+
return {
|
|
2565
|
+
ok: false,
|
|
2566
|
+
message:
|
|
2567
|
+
"Telegram follower Workspace Thread rename is unavailable.",
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2570
|
+
return {
|
|
2571
|
+
ok: true,
|
|
2572
|
+
threadName: renamedThreadName,
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
if (!ownsTelegramDirectDelivery()) {
|
|
2576
|
+
return {
|
|
2577
|
+
ok: false,
|
|
2578
|
+
message:
|
|
2579
|
+
"Telegram Workspace Thread rename requires an active leader or follower connection.",
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
if (typeof expectedTarget.threadId !== "number") {
|
|
2583
|
+
return {
|
|
2584
|
+
ok: false,
|
|
2585
|
+
message: "Telegram Workspace Thread target is unavailable.",
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2588
|
+
const target = {
|
|
2589
|
+
chatId: expectedTarget.chatId,
|
|
2590
|
+
threadId: expectedTarget.threadId,
|
|
2591
|
+
};
|
|
2592
|
+
const recipient = assertCaller
|
|
2593
|
+
? Threads.createTelegramWorkspaceThreadRenameRecipient({
|
|
2594
|
+
store: threadStore,
|
|
2595
|
+
instanceId: telegramInstanceId,
|
|
2596
|
+
target,
|
|
2597
|
+
assertAuthority: assertCaller,
|
|
2598
|
+
getAuthority: threadNameRecipientAuthority.getAuthority,
|
|
2599
|
+
})
|
|
2600
|
+
: undefined;
|
|
2601
|
+
const renamed =
|
|
2602
|
+
await telegramBusLeaderRuntime.renameLeaderThreadAdmitted(
|
|
2603
|
+
threadName,
|
|
2604
|
+
target,
|
|
2605
|
+
recipient?.assertAuthority,
|
|
2606
|
+
);
|
|
2607
|
+
recipient?.assertResult(renamed);
|
|
2608
|
+
telegramBusLeaderState.set({
|
|
2609
|
+
target: renamed.target,
|
|
2610
|
+
slot: renamed.slot,
|
|
2611
|
+
threadName: renamed.manualThreadName ?? renamed.threadName,
|
|
2612
|
+
});
|
|
2613
|
+
recipient?.assertResult(renamed);
|
|
2614
|
+
return {
|
|
2615
|
+
ok: true,
|
|
2616
|
+
threadName: renamed.manualThreadName,
|
|
2617
|
+
};
|
|
2618
|
+
} catch (error) {
|
|
2619
|
+
return {
|
|
2620
|
+
ok: false,
|
|
2621
|
+
message:
|
|
2622
|
+
error instanceof Error
|
|
2623
|
+
? error.message
|
|
2624
|
+
: "Telegram Workspace Thread rename failed.",
|
|
2625
|
+
};
|
|
2001
2626
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2627
|
+
},
|
|
2628
|
+
}.rename,
|
|
2629
|
+
);
|
|
2630
|
+
telegramThreadDisplayNameResetBinding.bind(
|
|
2631
|
+
{
|
|
2632
|
+
async reset(
|
|
2633
|
+
expectedTarget: Parameters<Commands.TelegramThreadDisplayNameResetPort>[0],
|
|
2634
|
+
options?: Parameters<Commands.TelegramThreadDisplayNameResetPort>[1],
|
|
2635
|
+
) {
|
|
2636
|
+
try {
|
|
2637
|
+
const assertCaller = options?.assertAuthority;
|
|
2638
|
+
assertCaller?.();
|
|
2639
|
+
const wasFollower =
|
|
2640
|
+
telegramBusFollowerRegistrationState.isRegistered();
|
|
2641
|
+
if (wasFollower && assertCaller)
|
|
2642
|
+
return {
|
|
2643
|
+
ok: false,
|
|
2644
|
+
message:
|
|
2645
|
+
"Guarded follower Workspace Thread reset is unavailable.",
|
|
2646
|
+
};
|
|
2647
|
+
if (typeof expectedTarget.threadId !== "number") {
|
|
2648
|
+
return {
|
|
2649
|
+
ok: false,
|
|
2650
|
+
message: "Telegram Workspace Thread target is unavailable.",
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
const target = {
|
|
2654
|
+
chatId: expectedTarget.chatId,
|
|
2655
|
+
threadId: expectedTarget.threadId,
|
|
2656
|
+
};
|
|
2657
|
+
const recipient = assertCaller
|
|
2658
|
+
? Threads.createTelegramWorkspaceThreadResetRecipient({
|
|
2659
|
+
store: threadStore,
|
|
2660
|
+
instanceId: telegramInstanceId,
|
|
2661
|
+
target,
|
|
2662
|
+
assertAuthority: assertCaller,
|
|
2663
|
+
getAuthority: threadNameRecipientAuthority.getAuthority,
|
|
2664
|
+
})
|
|
2665
|
+
: undefined;
|
|
2666
|
+
const result = wasFollower
|
|
2667
|
+
? await telegramBusFollowerRegistration.resetThreadName?.(target)
|
|
2668
|
+
: (
|
|
2669
|
+
await (recipient
|
|
2670
|
+
? telegramBusLeaderRuntime.resetLeaderThreadName(
|
|
2671
|
+
target,
|
|
2672
|
+
recipient.assertAuthority,
|
|
2673
|
+
)
|
|
2674
|
+
: telegramBusLeaderRuntime.resetLeaderThreadName(target))
|
|
2675
|
+
).threadName;
|
|
2676
|
+
if (!result) {
|
|
2677
|
+
return {
|
|
2678
|
+
ok: false,
|
|
2679
|
+
message: "Thread display name reset is unavailable.",
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
recipient?.assertResult(result);
|
|
2683
|
+
const leaderTarget = telegramBusLeaderState.getTarget();
|
|
2684
|
+
if (!wasFollower && leaderTarget) {
|
|
2685
|
+
telegramBusLeaderState.set({
|
|
2686
|
+
target: leaderTarget,
|
|
2687
|
+
threadName: result,
|
|
2688
|
+
...(recipient
|
|
2689
|
+
? { slot: telegramBusLeaderState.getIdentity()?.slot }
|
|
2690
|
+
: {}),
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
recipient?.assertResult(result);
|
|
2694
|
+
return {
|
|
2695
|
+
ok: true,
|
|
2696
|
+
threadName: result,
|
|
2697
|
+
};
|
|
2698
|
+
} catch (error) {
|
|
2699
|
+
return {
|
|
2700
|
+
ok: false,
|
|
2701
|
+
message:
|
|
2702
|
+
error instanceof Error
|
|
2703
|
+
? error.message
|
|
2704
|
+
: "Thread display name reset failed.",
|
|
2705
|
+
};
|
|
2005
2706
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
};
|
|
2010
|
-
} catch (error) {
|
|
2011
|
-
return {
|
|
2012
|
-
ok: false,
|
|
2013
|
-
message: error instanceof Error
|
|
2014
|
-
? error.message : "Thread display name reset failed.",
|
|
2015
|
-
};
|
|
2016
|
-
}
|
|
2017
|
-
},
|
|
2018
|
-
}.reset);
|
|
2707
|
+
},
|
|
2708
|
+
}.reset,
|
|
2709
|
+
);
|
|
2019
2710
|
sessionActionsRuntime.register();
|
|
2020
2711
|
Bindings.registerTelegramCommandsAndTools({
|
|
2021
2712
|
pi,
|
|
@@ -2045,27 +2736,42 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
2045
2736
|
sendMarkdownReply,
|
|
2046
2737
|
async sendChannelMarkdownMessage(channel, markdown, options) {
|
|
2047
2738
|
if (!lockRuntime.owns()) {
|
|
2048
|
-
throw new Error(
|
|
2739
|
+
throw new Error(
|
|
2740
|
+
"Telegram channel delivery requires direct leader transport ownership.",
|
|
2741
|
+
);
|
|
2049
2742
|
}
|
|
2050
2743
|
const profileName = configStore.getActiveProfileName() ?? "default";
|
|
2051
2744
|
const botToken = configStore.getBotToken();
|
|
2052
|
-
if (!botToken)
|
|
2053
|
-
|
|
2054
|
-
|
|
2745
|
+
if (!botToken)
|
|
2746
|
+
throw new Error(
|
|
2747
|
+
"Telegram channel delivery requires an active bot token.",
|
|
2748
|
+
);
|
|
2749
|
+
const store = ChannelPosts.openTelegramChannelPostJournalStore({
|
|
2055
2750
|
profileName,
|
|
2056
|
-
|
|
2751
|
+
botToken,
|
|
2057
2752
|
});
|
|
2058
2753
|
const record = await ChannelPosts.publishTelegramChannelPost({
|
|
2059
|
-
store,
|
|
2060
|
-
|
|
2754
|
+
store,
|
|
2755
|
+
operationId: options.operationId,
|
|
2756
|
+
channel: channel as ChannelPosts.TelegramChannelPostAddress,
|
|
2757
|
+
markdown,
|
|
2061
2758
|
async observeChannel(channelAddress) {
|
|
2062
|
-
return telegramApiRuntime.call("getChat", {
|
|
2759
|
+
return telegramApiRuntime.call("getChat", {
|
|
2760
|
+
chat_id: channelAddress,
|
|
2761
|
+
});
|
|
2063
2762
|
},
|
|
2064
2763
|
async send(channelAddress, body) {
|
|
2065
|
-
const sent = await telegramApiRuntime.call<
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2764
|
+
const sent = await telegramApiRuntime.call<
|
|
2765
|
+
TelegramApi.TelegramSentMessage & {
|
|
2766
|
+
chat: { id: number; type: string };
|
|
2767
|
+
}
|
|
2768
|
+
>("sendRichMessage", {
|
|
2769
|
+
chat_id: channelAddress,
|
|
2770
|
+
rich_message: { markdown: body },
|
|
2771
|
+
...(options.replyMarkup
|
|
2772
|
+
? { reply_markup: options.replyMarkup }
|
|
2773
|
+
: {}),
|
|
2774
|
+
});
|
|
2069
2775
|
return { messageId: sent.message_id, chat: sent.chat };
|
|
2070
2776
|
},
|
|
2071
2777
|
});
|
|
@@ -2073,34 +2779,54 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
2073
2779
|
},
|
|
2074
2780
|
async sendChannelMediaMessage(channel, mediaPath, markdown, options) {
|
|
2075
2781
|
if (!lockRuntime.owns()) {
|
|
2076
|
-
throw new Error(
|
|
2782
|
+
throw new Error(
|
|
2783
|
+
"Telegram channel media delivery requires direct leader transport ownership.",
|
|
2784
|
+
);
|
|
2077
2785
|
}
|
|
2078
2786
|
const profileName = configStore.getActiveProfileName() ?? "default";
|
|
2079
2787
|
const botToken = configStore.getBotToken();
|
|
2080
|
-
if (!botToken)
|
|
2081
|
-
|
|
2788
|
+
if (!botToken)
|
|
2789
|
+
throw new Error(
|
|
2790
|
+
"Telegram channel media delivery requires an active bot token.",
|
|
2791
|
+
);
|
|
2792
|
+
const media =
|
|
2793
|
+
await ChannelPosts.inspectTelegramChannelPostMedia(mediaPath);
|
|
2082
2794
|
const caption = Replies.renderTelegramMarkdownToHtmlDraft(markdown);
|
|
2083
2795
|
ChannelPosts.assertTelegramChannelPostCaptionWithinLimit(caption);
|
|
2084
|
-
const store = ChannelPosts.
|
|
2085
|
-
...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
|
|
2796
|
+
const store = ChannelPosts.openTelegramChannelPostJournalStore({
|
|
2086
2797
|
profileName,
|
|
2087
|
-
|
|
2798
|
+
botToken,
|
|
2088
2799
|
});
|
|
2089
2800
|
const record = await ChannelPosts.publishTelegramChannelPost({
|
|
2090
|
-
store,
|
|
2091
|
-
|
|
2801
|
+
store,
|
|
2802
|
+
operationId: options.operationId,
|
|
2803
|
+
channel: channel as ChannelPosts.TelegramChannelPostAddress,
|
|
2804
|
+
markdown,
|
|
2805
|
+
media,
|
|
2092
2806
|
async observeChannel(channelAddress) {
|
|
2093
|
-
return telegramApiRuntime.call("getChat", {
|
|
2807
|
+
return telegramApiRuntime.call("getChat", {
|
|
2808
|
+
chat_id: channelAddress,
|
|
2809
|
+
});
|
|
2094
2810
|
},
|
|
2095
2811
|
async send(channelAddress) {
|
|
2096
|
-
const sent = await telegramApiRuntime.callMultipart<
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2812
|
+
const sent = await telegramApiRuntime.callMultipart<
|
|
2813
|
+
TelegramApi.TelegramSentMessage & {
|
|
2814
|
+
chat: { id: number; type: string };
|
|
2815
|
+
}
|
|
2816
|
+
>(
|
|
2817
|
+
media.kind === "photo" ? "sendPhoto" : "sendVideo",
|
|
2818
|
+
{
|
|
2819
|
+
chat_id: String(channelAddress),
|
|
2820
|
+
caption,
|
|
2821
|
+
parse_mode: "HTML",
|
|
2822
|
+
...(options.replyMarkup
|
|
2823
|
+
? { reply_markup: JSON.stringify(options.replyMarkup) }
|
|
2824
|
+
: {}),
|
|
2825
|
+
},
|
|
2826
|
+
media.kind === "photo" ? "photo" : "video",
|
|
2827
|
+
mediaPath,
|
|
2828
|
+
media.fileName,
|
|
2829
|
+
);
|
|
2104
2830
|
return { messageId: sent.message_id, chat: sent.chat };
|
|
2105
2831
|
},
|
|
2106
2832
|
});
|
|
@@ -2109,61 +2835,108 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
2109
2835
|
listChannelPosts(input) {
|
|
2110
2836
|
const profileName = configStore.getActiveProfileName() ?? "default";
|
|
2111
2837
|
const botToken = configStore.getBotToken();
|
|
2112
|
-
if (!botToken)
|
|
2113
|
-
|
|
2114
|
-
|
|
2838
|
+
if (!botToken)
|
|
2839
|
+
throw new Error("Telegram channel posts require an active bot token.");
|
|
2840
|
+
return ChannelPosts.openTelegramChannelPostJournalStore({
|
|
2115
2841
|
profileName,
|
|
2116
|
-
|
|
2842
|
+
botToken,
|
|
2117
2843
|
}).list(input);
|
|
2118
2844
|
},
|
|
2119
2845
|
async mutateChannelPost(input) {
|
|
2120
|
-
if (!lockRuntime.owns())
|
|
2846
|
+
if (!lockRuntime.owns())
|
|
2847
|
+
throw new Error(
|
|
2848
|
+
"Telegram channel post mutation requires direct leader ownership.",
|
|
2849
|
+
);
|
|
2121
2850
|
const profileName = configStore.getActiveProfileName() ?? "default";
|
|
2122
2851
|
const botToken = configStore.getBotToken();
|
|
2123
|
-
if (!botToken)
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2852
|
+
if (!botToken)
|
|
2853
|
+
throw new Error(
|
|
2854
|
+
"Telegram channel post mutation requires an active bot token.",
|
|
2855
|
+
);
|
|
2856
|
+
const store = ChannelPosts.openTelegramChannelPostJournalStore({
|
|
2857
|
+
profileName,
|
|
2858
|
+
botToken,
|
|
2127
2859
|
});
|
|
2128
2860
|
if (input.action === "edit") {
|
|
2129
|
-
if (!input.markdown)
|
|
2861
|
+
if (!input.markdown)
|
|
2862
|
+
throw new Error("Telegram channel post edit requires markdown.");
|
|
2130
2863
|
const current = store.get(input.operationId);
|
|
2131
2864
|
const caption = current?.media
|
|
2132
|
-
? Replies.renderTelegramMarkdownToHtmlDraft(input.markdown)
|
|
2865
|
+
? Replies.renderTelegramMarkdownToHtmlDraft(input.markdown)
|
|
2866
|
+
: undefined;
|
|
2133
2867
|
if (caption !== undefined) {
|
|
2134
2868
|
ChannelPosts.assertTelegramChannelPostCaptionWithinLimit(caption);
|
|
2135
2869
|
}
|
|
2136
|
-
const begun = store.beginEdit({
|
|
2137
|
-
|
|
2870
|
+
const begun = store.beginEdit({
|
|
2871
|
+
operationId: input.operationId,
|
|
2872
|
+
mutationId: input.mutationId,
|
|
2873
|
+
markdown: input.markdown,
|
|
2874
|
+
});
|
|
2138
2875
|
if (!begun.began) {
|
|
2139
|
-
if (
|
|
2876
|
+
if (
|
|
2877
|
+
begun.record.state === "published" &&
|
|
2878
|
+
begun.record.lastMutationId === input.mutationId
|
|
2879
|
+
)
|
|
2140
2880
|
return begun.record;
|
|
2141
|
-
throw new Error(
|
|
2881
|
+
throw new Error(
|
|
2882
|
+
"Telegram channel post edit outcome is unknown; refusing automatic replay.",
|
|
2883
|
+
);
|
|
2142
2884
|
}
|
|
2143
|
-
if (begun.record.state !== "edit-outcome-unknown")
|
|
2885
|
+
if (begun.record.state !== "edit-outcome-unknown")
|
|
2886
|
+
throw new Error("Telegram channel post edit authority is invalid.");
|
|
2144
2887
|
if (begun.record.media) {
|
|
2145
2888
|
if (caption === undefined) {
|
|
2146
|
-
throw new Error(
|
|
2889
|
+
throw new Error(
|
|
2890
|
+
"Telegram channel post media caption edit requires retained media identity.",
|
|
2891
|
+
);
|
|
2147
2892
|
}
|
|
2148
|
-
await telegramApiRuntime.call("editMessageCaption", {
|
|
2149
|
-
|
|
2893
|
+
await telegramApiRuntime.call("editMessageCaption", {
|
|
2894
|
+
chat_id: begun.record.channelId,
|
|
2895
|
+
message_id: begun.record.messageId,
|
|
2896
|
+
caption,
|
|
2897
|
+
parse_mode: "HTML",
|
|
2898
|
+
});
|
|
2150
2899
|
} else {
|
|
2151
|
-
await telegramApiRuntime.call("editMessageText", {
|
|
2900
|
+
await telegramApiRuntime.call("editMessageText", {
|
|
2901
|
+
chat_id: begun.record.channelId,
|
|
2152
2902
|
message_id: begun.record.messageId,
|
|
2153
|
-
text: Replies.renderTelegramMarkdownToHtmlDraft(input.markdown),
|
|
2903
|
+
text: Replies.renderTelegramMarkdownToHtmlDraft(input.markdown),
|
|
2904
|
+
parse_mode: "HTML",
|
|
2905
|
+
});
|
|
2154
2906
|
}
|
|
2155
|
-
return store.confirmEdited({
|
|
2907
|
+
return store.confirmEdited({
|
|
2908
|
+
operationId: input.operationId,
|
|
2909
|
+
mutationId: input.mutationId,
|
|
2910
|
+
}).record;
|
|
2156
2911
|
}
|
|
2157
|
-
if (input.markdown !== undefined)
|
|
2158
|
-
|
|
2912
|
+
if (input.markdown !== undefined)
|
|
2913
|
+
throw new Error(
|
|
2914
|
+
"Telegram channel post deletion does not accept markdown.",
|
|
2915
|
+
);
|
|
2916
|
+
const begun = store.beginDelete({
|
|
2917
|
+
operationId: input.operationId,
|
|
2918
|
+
mutationId: input.mutationId,
|
|
2919
|
+
});
|
|
2159
2920
|
if (!begun.began) {
|
|
2160
|
-
if (
|
|
2161
|
-
|
|
2921
|
+
if (
|
|
2922
|
+
begun.record.state === "deleted" &&
|
|
2923
|
+
begun.record.mutationId === input.mutationId
|
|
2924
|
+
)
|
|
2925
|
+
return begun.record;
|
|
2926
|
+
throw new Error(
|
|
2927
|
+
"Telegram channel post deletion outcome is unknown; refusing automatic replay.",
|
|
2928
|
+
);
|
|
2162
2929
|
}
|
|
2163
|
-
if (begun.record.state !== "delete-outcome-unknown")
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2930
|
+
if (begun.record.state !== "delete-outcome-unknown")
|
|
2931
|
+
throw new Error("Telegram channel post deletion authority is invalid.");
|
|
2932
|
+
await telegramApiRuntime.call("deleteMessage", {
|
|
2933
|
+
chat_id: begun.record.channelId,
|
|
2934
|
+
message_id: begun.record.messageId,
|
|
2935
|
+
});
|
|
2936
|
+
return store.confirmDeleted({
|
|
2937
|
+
operationId: input.operationId,
|
|
2938
|
+
mutationId: input.mutationId,
|
|
2939
|
+
}).record;
|
|
2167
2940
|
},
|
|
2168
2941
|
callMultipart,
|
|
2169
2942
|
getDefaultChatId: proactivePushChatIdGetter,
|