@llblab/pi-telegram 0.45.11 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +5 -1
- package/CHANGELOG.md +12 -0
- package/README.md +6 -6
- package/dist/api/activity.d.ts +6 -0
- package/dist/api/activity.js +6 -0
- package/dist/api/commands.d.ts +6 -0
- package/dist/api/commands.js +6 -0
- package/dist/api/delivery.d.ts +6 -0
- package/dist/api/delivery.js +6 -0
- package/dist/api/inbound.d.ts +6 -0
- package/dist/api/inbound.js +6 -0
- package/dist/api/keyboard.d.ts +6 -0
- package/dist/api/keyboard.js +6 -0
- package/dist/api/outbound.d.ts +6 -0
- package/dist/api/outbound.js +6 -0
- package/dist/api/sections.d.ts +7 -0
- package/dist/api/sections.js +6 -0
- package/dist/api/status.d.ts +6 -0
- package/dist/api/status.js +6 -0
- package/dist/api/updates.d.ts +6 -0
- package/dist/api/updates.js +6 -0
- package/dist/api/voice.d.ts +6 -0
- package/dist/api/voice.js +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/lib/activity-verbosity.d.ts +52 -0
- package/dist/lib/activity-verbosity.js +596 -0
- package/dist/lib/activity.d.ts +220 -0
- package/dist/lib/activity.js +574 -0
- package/dist/lib/agent-messages.d.ts +28 -0
- package/dist/lib/agent-messages.js +86 -0
- package/dist/lib/bindings.d.ts +250 -0
- package/dist/lib/bindings.js +936 -0
- package/dist/lib/bus-api.d.ts +18 -0
- package/dist/lib/bus-api.js +254 -0
- package/dist/lib/bus-follower.d.ts +464 -0
- package/dist/lib/bus-follower.js +1686 -0
- package/dist/lib/bus-leader.d.ts +292 -0
- package/dist/lib/bus-leader.js +2242 -0
- package/dist/lib/bus-transport.d.ts +64 -0
- package/dist/lib/bus-transport.js +140 -0
- package/dist/lib/bus.d.ts +518 -0
- package/dist/lib/bus.js +2055 -0
- package/dist/lib/channel-posts.d.ts +170 -0
- package/dist/lib/channel-posts.js +611 -0
- package/dist/lib/command-templates.d.ts +70 -0
- package/dist/lib/command-templates.js +744 -0
- package/dist/lib/commands.d.ts +541 -0
- package/dist/lib/commands.js +1155 -0
- package/dist/lib/config.d.ts +252 -0
- package/dist/lib/config.js +889 -0
- package/dist/lib/delivery.d.ts +163 -0
- package/dist/lib/delivery.js +542 -0
- package/dist/lib/extension.d.ts +7 -0
- package/dist/lib/extension.js +1608 -0
- package/dist/lib/generative-app-worker.mjs +104 -0
- package/dist/lib/generative-apps.d.ts +212 -0
- package/dist/lib/generative-apps.js +1006 -0
- package/dist/lib/inbound.d.ts +91 -0
- package/dist/lib/inbound.js +502 -0
- package/dist/lib/journal.d.ts +665 -0
- package/dist/lib/journal.js +3701 -0
- package/dist/lib/keyboard.d.ts +23 -0
- package/dist/lib/keyboard.js +37 -0
- package/dist/lib/lifecycle.d.ts +157 -0
- package/dist/lib/lifecycle.js +395 -0
- package/dist/lib/locks.d.ts +164 -0
- package/dist/lib/locks.js +1208 -0
- package/dist/lib/logging.d.ts +50 -0
- package/dist/lib/logging.js +274 -0
- package/dist/lib/media.d.ts +181 -0
- package/dist/lib/media.js +602 -0
- package/dist/lib/menu-model.d.ts +217 -0
- package/dist/lib/menu-model.js +663 -0
- package/dist/lib/menu-queue.d.ts +37 -0
- package/dist/lib/menu-queue.js +408 -0
- package/dist/lib/menu-settings.d.ts +116 -0
- package/dist/lib/menu-settings.js +595 -0
- package/dist/lib/menu-status.d.ts +32 -0
- package/dist/lib/menu-status.js +112 -0
- package/dist/lib/menu-thinking.d.ts +29 -0
- package/dist/lib/menu-thinking.js +82 -0
- package/dist/lib/menu.d.ts +171 -0
- package/dist/lib/menu.js +323 -0
- package/dist/lib/model.d.ts +127 -0
- package/dist/lib/model.js +409 -0
- package/dist/lib/outbound-attachments.d.ts +241 -0
- package/dist/lib/outbound-attachments.js +639 -0
- package/dist/lib/outbound-buttons.d.ts +69 -0
- package/dist/lib/outbound-buttons.js +248 -0
- package/dist/lib/outbound-markup.d.ts +42 -0
- package/dist/lib/outbound-markup.js +678 -0
- package/dist/lib/outbound-voice.d.ts +55 -0
- package/dist/lib/outbound-voice.js +152 -0
- package/dist/lib/outbound.d.ts +185 -0
- package/dist/lib/outbound.js +516 -0
- package/dist/lib/ownership.d.ts +77 -0
- package/dist/lib/ownership.js +174 -0
- package/dist/lib/paths.d.ts +40 -0
- package/dist/lib/paths.js +94 -0
- package/dist/lib/pi.d.ts +72 -0
- package/dist/lib/pi.js +121 -0
- package/dist/lib/polling.d.ts +351 -0
- package/dist/lib/polling.js +1196 -0
- package/dist/lib/preview.d.ts +192 -0
- package/dist/lib/preview.js +614 -0
- package/dist/lib/prompt-templates.d.ts +24 -0
- package/dist/lib/prompt-templates.js +118 -0
- package/dist/lib/prompts.d.ts +57 -0
- package/dist/lib/prompts.js +167 -0
- package/dist/lib/queue.d.ts +766 -0
- package/dist/lib/queue.js +1965 -0
- package/dist/lib/recovery.d.ts +86 -0
- package/dist/lib/recovery.js +285 -0
- package/dist/lib/rendering.d.ts +20 -0
- package/dist/lib/rendering.js +983 -0
- package/dist/lib/replies.d.ts +214 -0
- package/dist/lib/replies.js +737 -0
- package/dist/lib/routing.d.ts +207 -0
- package/dist/lib/routing.js +2282 -0
- package/dist/lib/runtime.d.ts +172 -0
- package/dist/lib/runtime.js +402 -0
- package/dist/lib/sections.d.ts +165 -0
- package/dist/lib/sections.js +327 -0
- package/dist/lib/setup.d.ts +82 -0
- package/dist/lib/setup.js +150 -0
- package/dist/lib/skills.d.ts +8 -0
- package/dist/lib/skills.js +12 -0
- package/dist/lib/status.d.ts +442 -0
- package/dist/lib/status.js +1006 -0
- package/dist/lib/sync.d.ts +179 -0
- package/dist/lib/sync.js +782 -0
- package/dist/lib/target.d.ts +20 -0
- package/dist/lib/target.js +27 -0
- package/dist/lib/telegram-api.d.ts +541 -0
- package/dist/lib/telegram-api.js +1159 -0
- package/dist/lib/text-groups.d.ts +89 -0
- package/dist/lib/text-groups.js +317 -0
- package/dist/lib/thread-cleanup-manager.d.ts +288 -0
- package/dist/lib/thread-cleanup-manager.js +560 -0
- package/dist/lib/thread-display.d.ts +46 -0
- package/dist/lib/thread-display.js +257 -0
- package/dist/lib/thread-naming.d.ts +46 -0
- package/dist/lib/thread-naming.js +78 -0
- package/dist/lib/thread-reconciler.d.ts +239 -0
- package/dist/lib/thread-reconciler.js +644 -0
- package/dist/lib/threads.d.ts +621 -0
- package/dist/lib/threads.js +3679 -0
- package/dist/lib/time-injection.d.ts +15 -0
- package/dist/lib/time-injection.js +56 -0
- package/dist/lib/turns.d.ts +109 -0
- package/dist/lib/turns.js +500 -0
- package/dist/lib/updates.d.ts +1290 -0
- package/dist/lib/updates.js +3634 -0
- package/dist/lib/voice.d.ts +117 -0
- package/dist/lib/voice.js +174 -0
- package/dist/lib/workspace-admission.d.ts +264 -0
- package/dist/lib/workspace-admission.js +1136 -0
- package/dist/lib/workspace-retirement.d.ts +219 -0
- package/dist/lib/workspace-retirement.js +587 -0
- package/dist/lib/workspace-slots.d.ts +30 -0
- package/dist/lib/workspace-slots.js +54 -0
- package/dist/package.json +126 -0
- package/dist/pi-telegram/index.js +1 -0
- package/dist/skills/generated-control-surface/SKILL.md +107 -0
- package/dist/skills/generated-control-surface/references/capability-adapters.md +27 -0
- package/dist/skills/generated-control-surface/references/layout-and-state.md +37 -0
- package/dist/skills/generative-apps/SKILL.md +115 -0
- package/dist/skills/show-me/SKILL.md +166 -0
- package/dist/skills/show-me/references/telegram-surfaces.md +43 -0
- package/dist/skills/telegram-bridge/SKILL.md +129 -0
- package/dist/skills/telegram-bridge/references/configuration.md +15 -0
- package/dist/skills/telegram-bridge/references/delivery-and-threads.md +27 -0
- package/dist/skills/telegram-bridge/references/diagnosis.md +18 -0
- package/docs/architecture.md +1 -1
- package/docs/compact-matrix-literal.md +10 -3
- package/docs/generative-apps.md +15 -13
- package/docs/multi-instance-bus.md +17 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +2 -2
- package/lib/bindings.ts +89 -6
- package/lib/bus-follower.ts +11 -3
- package/lib/bus-leader.ts +57 -20
- package/lib/bus.ts +12 -2
- package/lib/commands.ts +2 -2
- package/lib/config.ts +14 -7
- package/lib/delivery.ts +38 -6
- package/lib/extension.ts +26 -1
- package/lib/generative-apps.ts +379 -13
- package/lib/menu-settings.ts +21 -5
- package/lib/outbound-markup.ts +16 -7
- package/lib/pi.ts +4 -0
- package/lib/sync.ts +3 -0
- package/lib/telegram-api.ts +12 -1
- package/lib/thread-cleanup-manager.ts +36 -6
- package/lib/thread-display.ts +96 -18
- package/lib/threads.ts +150 -30
- package/package.json +56 -13
- package/scripts/build-dist.mjs +44 -0
- package/scripts/measure-bus.mjs +8 -1
- package/scripts/measure-workspace.mjs +8 -1
- package/skills/generative-apps/SKILL.md +1 -1
- package/skills/show-me/SKILL.md +1 -1
- package/skills/telegram-bridge/SKILL.md +1 -1
package/lib/bindings.ts
CHANGED
|
@@ -10,6 +10,7 @@ import * as ChannelPosts from "./channel-posts.ts";
|
|
|
10
10
|
import * as CommandTemplates from "./command-templates.ts";
|
|
11
11
|
import * as Commands from "./commands.ts";
|
|
12
12
|
import * as Config from "./config.ts";
|
|
13
|
+
import * as Delivery from "./delivery.ts";
|
|
13
14
|
import * as Keyboard from "./keyboard.ts";
|
|
14
15
|
import * as Lifecycle from "./lifecycle.ts";
|
|
15
16
|
import * as Locks from "./locks.ts";
|
|
@@ -176,14 +177,47 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
|
|
|
176
177
|
};
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
export interface TelegramGenerativeAppLiveSurfaceBinding {
|
|
181
|
+
get: () =>
|
|
182
|
+
| GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
|
|
183
|
+
| undefined;
|
|
184
|
+
set: (
|
|
185
|
+
runtime: GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
|
|
186
|
+
| undefined,
|
|
187
|
+
) => void;
|
|
188
|
+
shutdown: () => void;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function createTelegramGenerativeAppLiveSurfaceBinding():
|
|
192
|
+
TelegramGenerativeAppLiveSurfaceBinding {
|
|
193
|
+
let current:
|
|
194
|
+
| GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
|
|
195
|
+
| undefined;
|
|
196
|
+
return {
|
|
197
|
+
get() { return current; },
|
|
198
|
+
set(runtime) {
|
|
199
|
+
current?.shutdown();
|
|
200
|
+
current = runtime;
|
|
201
|
+
},
|
|
202
|
+
shutdown() {
|
|
203
|
+
current?.shutdown();
|
|
204
|
+
current = undefined;
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
179
209
|
export function createTelegramGenerativeAppBoundButtonActionInvoker<
|
|
180
210
|
TQuery extends {
|
|
181
|
-
message?: { chat?: { id?: number }; message_id?: number };
|
|
211
|
+
message?: { chat?: { id?: number }; message_id?: number; message_thread_id?: number };
|
|
182
212
|
},
|
|
183
213
|
>(deps: {
|
|
184
214
|
agentDir: string;
|
|
185
215
|
assertExecutionCurrent: (query: TQuery) => void;
|
|
186
216
|
getExecutionFence: (query: TQuery) => GenerativeApps.GenerativeAppExecutionFence | undefined;
|
|
217
|
+
getActiveProfileName?: () => string | undefined;
|
|
218
|
+
getLiveSurfaceRuntime?: () =>
|
|
219
|
+
| GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
|
|
220
|
+
| undefined;
|
|
187
221
|
planOutput: ReturnType<typeof OutboundHandlers.createTelegramOutboundReplyPlanner>;
|
|
188
222
|
sendMarkdownReply: (
|
|
189
223
|
chatId: number,
|
|
@@ -205,10 +239,29 @@ export function createTelegramGenerativeAppBoundButtonActionInvoker<
|
|
|
205
239
|
) => Promise<false | "new" | "edit"> {
|
|
206
240
|
return async (action, query) => {
|
|
207
241
|
let boundAction: GenerativeApps.GenerativeAppBoundAction | undefined;
|
|
242
|
+
let handedOff:
|
|
243
|
+
| GenerativeApps.GenerativeAppLiveSurface<GenerativeApps.TelegramBindLiveHandle>
|
|
244
|
+
| undefined;
|
|
208
245
|
try {
|
|
209
246
|
boundAction = GenerativeApps.parseGenerativeAppBoundAction(action.prompt);
|
|
210
247
|
if (!boundAction) return false;
|
|
211
248
|
deps.assertExecutionCurrent(query);
|
|
249
|
+
const chatId = query.message?.chat?.id;
|
|
250
|
+
const messageId = query.message?.message_id;
|
|
251
|
+
if (typeof chatId !== "number" || typeof messageId !== "number") {
|
|
252
|
+
throw new Error("Generative App callback target is unavailable.");
|
|
253
|
+
}
|
|
254
|
+
const liveSurfaces = deps.getLiveSurfaceRuntime?.();
|
|
255
|
+
handedOff = liveSurfaces?.take(GenerativeApps.getTelegramBindLiveSurfaceKey(
|
|
256
|
+
boundAction.app,
|
|
257
|
+
deps.getActiveProfileName?.() ?? "default",
|
|
258
|
+
{
|
|
259
|
+
chatId,
|
|
260
|
+
...(query.message?.message_thread_id !== undefined
|
|
261
|
+
? { threadId: query.message.message_thread_id }
|
|
262
|
+
: {}),
|
|
263
|
+
},
|
|
264
|
+
));
|
|
212
265
|
const result = await GenerativeApps.invokeGenerativeApp({
|
|
213
266
|
agentDir: deps.agentDir,
|
|
214
267
|
...(deps.getExecutionFence(query)
|
|
@@ -227,11 +280,6 @@ export function createTelegramGenerativeAppBoundButtonActionInvoker<
|
|
|
227
280
|
app: boundAction.app,
|
|
228
281
|
});
|
|
229
282
|
deps.assertExecutionCurrent(query);
|
|
230
|
-
const chatId = query.message?.chat?.id;
|
|
231
|
-
const messageId = query.message?.message_id;
|
|
232
|
-
if (typeof chatId !== "number" || typeof messageId !== "number") {
|
|
233
|
-
throw new Error("Generative App callback target is unavailable.");
|
|
234
|
-
}
|
|
235
283
|
const reply = deps.planOutput(result.output, {
|
|
236
284
|
binding: {
|
|
237
285
|
generation: result.generation,
|
|
@@ -239,6 +287,19 @@ export function createTelegramGenerativeAppBoundButtonActionInvoker<
|
|
|
239
287
|
revision: result.revision,
|
|
240
288
|
},
|
|
241
289
|
});
|
|
290
|
+
if (handedOff && liveSurfaces) {
|
|
291
|
+
try {
|
|
292
|
+
await liveSurfaces.resume(handedOff, result);
|
|
293
|
+
deps.assertExecutionCurrent(query);
|
|
294
|
+
return "edit";
|
|
295
|
+
} catch (error) {
|
|
296
|
+
deps.recordRuntimeEvent("generative-app", error, {
|
|
297
|
+
phase: "bound-action-live-edit-fallback",
|
|
298
|
+
app: boundAction.app,
|
|
299
|
+
method: boundAction.method,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
242
303
|
if (result.viewMode === "edit" && deps.editInteractiveMessage) {
|
|
243
304
|
let editFailed = false;
|
|
244
305
|
try {
|
|
@@ -267,6 +328,7 @@ export function createTelegramGenerativeAppBoundButtonActionInvoker<
|
|
|
267
328
|
deps.assertExecutionCurrent(query);
|
|
268
329
|
return "new";
|
|
269
330
|
} catch (error) {
|
|
331
|
+
if (handedOff) deps.getLiveSurfaceRuntime?.()?.open(handedOff);
|
|
270
332
|
deps.recordRuntimeEvent("generative-app", error, {
|
|
271
333
|
phase: "bound-action",
|
|
272
334
|
...(boundAction
|
|
@@ -534,6 +596,10 @@ interface TelegramCommandsAndToolsBindingDeps {
|
|
|
534
596
|
resolveAgentTarget?: OutboundAttachments.TelegramOutboundMessageToolRegistrationDeps["resolveAgentTarget"];
|
|
535
597
|
routeAgentMessage?: OutboundAttachments.TelegramOutboundMessageToolRegistrationDeps["routeAgentMessage"];
|
|
536
598
|
canSendDirect: () => boolean;
|
|
599
|
+
setGenerativeAppLiveSurfaceRuntime?: (
|
|
600
|
+
runtime: GenerativeApps.GenerativeAppLiveSurfaceRuntime<GenerativeApps.TelegramBindLiveHandle>
|
|
601
|
+
| undefined,
|
|
602
|
+
) => void;
|
|
537
603
|
updateStatus: TelegramBridgeStatusUpdater;
|
|
538
604
|
recordRuntimeEvent: TelegramRuntimeEventRecorder;
|
|
539
605
|
}
|
|
@@ -565,17 +631,31 @@ export function registerTelegramCommandsAndTools({
|
|
|
565
631
|
resolveAgentTarget,
|
|
566
632
|
routeAgentMessage,
|
|
567
633
|
canSendDirect,
|
|
634
|
+
setGenerativeAppLiveSurfaceRuntime,
|
|
568
635
|
recordRuntimeEvent,
|
|
569
636
|
updateStatus,
|
|
570
637
|
}: TelegramCommandsAndToolsBindingDeps): void {
|
|
571
638
|
GenerativeApps.registerTelegramBindTool(pi, {
|
|
572
639
|
agentDir,
|
|
640
|
+
getActiveProfileName: configStore.getActiveProfileName,
|
|
573
641
|
getActiveTurn: activeTurnRuntime.get,
|
|
642
|
+
...(setGenerativeAppLiveSurfaceRuntime
|
|
643
|
+
? { setLiveSurfaceRuntime: setGenerativeAppLiveSurfaceRuntime }
|
|
644
|
+
: {}),
|
|
645
|
+
isDeliveryHandleCurrent: Delivery.isTelegramDeliveryHandleCurrent,
|
|
646
|
+
editView: (handle, view) => Delivery.editTelegramView(
|
|
647
|
+
handle as Delivery.TelegramDeliveryHandle,
|
|
648
|
+
view as Delivery.TelegramDeliveryView,
|
|
649
|
+
),
|
|
574
650
|
planOutput: OutboundHandlers.createTelegramOutboundReplyPlanner(
|
|
575
651
|
buttonActionStore,
|
|
576
652
|
Config.createTelegramConfigControls(configStore).getAssistantRenderingMode,
|
|
577
653
|
),
|
|
578
654
|
sendMarkdownReply,
|
|
655
|
+
sendView: (view, options) => Delivery.sendTelegramView(
|
|
656
|
+
view as Delivery.TelegramDeliveryView,
|
|
657
|
+
options,
|
|
658
|
+
),
|
|
579
659
|
recordRuntimeEvent,
|
|
580
660
|
});
|
|
581
661
|
ChannelPosts.registerTelegramChannelPostMutationTool(pi, { mutate: mutateChannelPost });
|
|
@@ -782,6 +862,7 @@ interface TelegramLifecycleBindingDeps {
|
|
|
782
862
|
deferredQueueDispatchRuntime: Queue.TelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>;
|
|
783
863
|
modelContextAvailabilityRuntime: Prompts.TelegramModelContextAvailabilityRuntime;
|
|
784
864
|
disconnectOnQuit?: () => Promise<unknown>;
|
|
865
|
+
shutdownGenerativeAppLiveSurfaces?: () => void;
|
|
785
866
|
resolveAutomaticThreadCleanupEnabled?: () => boolean | Promise<boolean>;
|
|
786
867
|
buttonActionStore: OutboundHandlers.TelegramButtonActionStore;
|
|
787
868
|
callMultipart: OutboundHandlers.TelegramVoiceReplySenderDeps["sendMultipart"];
|
|
@@ -871,6 +952,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
871
952
|
deferredQueueDispatchRuntime,
|
|
872
953
|
modelContextAvailabilityRuntime,
|
|
873
954
|
disconnectOnQuit,
|
|
955
|
+
shutdownGenerativeAppLiveSurfaces,
|
|
874
956
|
resolveAutomaticThreadCleanupEnabled,
|
|
875
957
|
buttonActionStore,
|
|
876
958
|
callMultipart,
|
|
@@ -1194,6 +1276,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1194
1276
|
},
|
|
1195
1277
|
async onSessionShutdown(event, ctx) {
|
|
1196
1278
|
if (!isSessionContextActive(ctx)) return;
|
|
1279
|
+
shutdownGenerativeAppLiveSurfaces?.();
|
|
1197
1280
|
agentLifecycleHooks.clearRetainedAgentEnd();
|
|
1198
1281
|
activityRuntime.onSessionShutdown();
|
|
1199
1282
|
activityVerbosityRuntime?.reset();
|
package/lib/bus-follower.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
type TelegramBusSocketPathSource,
|
|
44
44
|
TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME,
|
|
45
45
|
TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE,
|
|
46
|
+
TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT,
|
|
46
47
|
} from "./bus.ts";
|
|
47
48
|
import type { TelegramConfigStore, TelegramThreadDisplayMode } from "./config.ts";
|
|
48
49
|
import {
|
|
@@ -293,6 +294,7 @@ export interface TelegramBusFollowerRegistrationRuntimeDeps<
|
|
|
293
294
|
getNowMs?: () => number;
|
|
294
295
|
getPid?: () => number;
|
|
295
296
|
getProcessBirthId?: () => string;
|
|
297
|
+
getSessionId?: (ctx: TContext) => string | undefined;
|
|
296
298
|
getSessionGeneration?: () => number;
|
|
297
299
|
timeoutMs?: number;
|
|
298
300
|
registrationTimeoutMs?: number;
|
|
@@ -374,6 +376,7 @@ export function createTelegramBusFollowerPromotionHandler<
|
|
|
374
376
|
topicTargetStore: Threads.TelegramTopicTargetStore;
|
|
375
377
|
instanceId: string;
|
|
376
378
|
getActiveProfileName: () => string | undefined;
|
|
379
|
+
getSessionId?: (ctx: TContext) => string | undefined;
|
|
377
380
|
startLeader: (
|
|
378
381
|
ctx: TContext,
|
|
379
382
|
election: TelegramBusFollowerElection,
|
|
@@ -400,6 +403,7 @@ export function createTelegramBusFollowerPromotionHandler<
|
|
|
400
403
|
store: input.topicTargetStore,
|
|
401
404
|
instanceId: input.instanceId,
|
|
402
405
|
cwd: ctx.cwd,
|
|
406
|
+
sessionId: input.getSessionId?.(ctx),
|
|
403
407
|
telegramProfile: input.getActiveProfileName(),
|
|
404
408
|
target: binding.target,
|
|
405
409
|
slot: binding.slot,
|
|
@@ -1771,6 +1775,7 @@ export function createTelegramBusFollowerRegistrationRuntime<
|
|
|
1771
1775
|
? { slot: deps.registrationState?.getSlot() ?? lastKnownSlot }
|
|
1772
1776
|
: {}),
|
|
1773
1777
|
cwd: ctx.cwd,
|
|
1778
|
+
sessionId: deps.getSessionId?.(ctx),
|
|
1774
1779
|
pid: getPid(),
|
|
1775
1780
|
processBirthId: deps.getProcessBirthId?.(),
|
|
1776
1781
|
sessionGeneration: deps.getSessionGeneration?.(),
|
|
@@ -1910,9 +1915,12 @@ export function createTelegramBusFollowerRegistrationRuntime<
|
|
|
1910
1915
|
if (!socketPath || !generation || !deps.registrationState?.isRegistered()) {
|
|
1911
1916
|
throw new Error("Telegram follower is not registered with the leader.");
|
|
1912
1917
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1918
|
+
const requiredCapability = mode === "directory-snake" || mode === "directory-title"
|
|
1919
|
+
? TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT
|
|
1920
|
+
: TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE;
|
|
1921
|
+
if (!hasTelegramBusCapability(deps.protocolIdentity, requiredCapability) ||
|
|
1922
|
+
!hasTelegramBusCapability(deps.registrationState.getLeaderProtocol(), requiredCapability)) {
|
|
1923
|
+
throw new Error("The Telegram peers do not support this Thread display setting. Update or restart both instances.");
|
|
1916
1924
|
}
|
|
1917
1925
|
const requestId = deps.createRequestId();
|
|
1918
1926
|
const response = await sendTelegramBusLocalEnvelope({
|
package/lib/bus-leader.ts
CHANGED
|
@@ -9,6 +9,7 @@ import * as Sync from "./sync.ts";
|
|
|
9
9
|
import {
|
|
10
10
|
createTelegramThreadDisplayReconciler,
|
|
11
11
|
resolveTelegramInitialWorkspaceDisplayName,
|
|
12
|
+
resolveTelegramLiveWorkspaceBindingKeys,
|
|
12
13
|
} from "./thread-display.ts";
|
|
13
14
|
import type { TelegramThreadDisplayMode } from "./config.ts";
|
|
14
15
|
import * as ThreadReconciler from "./thread-reconciler.ts";
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT,
|
|
41
42
|
TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME,
|
|
42
43
|
TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE,
|
|
44
|
+
TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT,
|
|
43
45
|
} from "./bus.ts";
|
|
44
46
|
import { getTelegramBusTransportRetryPolicy } from "./bus-transport.ts";
|
|
45
47
|
import type { TelegramQueueHandoffPayload } from "./queue.ts";
|
|
@@ -113,6 +115,7 @@ export interface TelegramBusLeaderTargetProvisionerDeps<TContext> {
|
|
|
113
115
|
getAllowedUserId: () => number | undefined;
|
|
114
116
|
instanceId: string;
|
|
115
117
|
getCwd?: (ctx: TContext) => string | undefined;
|
|
118
|
+
getSessionId?: (ctx: TContext) => string | undefined;
|
|
116
119
|
getTelegramProfile?: () => string | undefined;
|
|
117
120
|
shouldForceFreshUnnamed?: () => boolean;
|
|
118
121
|
getRequestedThreadName?: () => string | undefined;
|
|
@@ -234,6 +237,7 @@ export interface TelegramBusLeaderRuntimeAssemblyDeps<TContext> {
|
|
|
234
237
|
getAllowedUserId: () => number | undefined;
|
|
235
238
|
instanceId: string;
|
|
236
239
|
getCwd?: (ctx: TContext) => string | undefined;
|
|
240
|
+
getSessionId?: (ctx: TContext) => string | undefined;
|
|
237
241
|
getTelegramProfile?: () => string | undefined;
|
|
238
242
|
shouldForceFreshUnnamed?: () => boolean;
|
|
239
243
|
getRequestedThreadName?: () => string | undefined;
|
|
@@ -307,6 +311,13 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
307
311
|
});
|
|
308
312
|
},
|
|
309
313
|
}).run;
|
|
314
|
+
const captureLiveBindingKeys = (
|
|
315
|
+
bindings: readonly Threads.TelegramWorkspaceThreadBinding[],
|
|
316
|
+
): ReadonlySet<string> => resolveTelegramLiveWorkspaceBindingKeys(
|
|
317
|
+
bindings,
|
|
318
|
+
deps.topicTargetStore.getActiveByInstanceId(deps.instanceId)?.target,
|
|
319
|
+
deps.runtime.followerRegistry.list(),
|
|
320
|
+
);
|
|
310
321
|
const provisionerPorts = {
|
|
311
322
|
getAllowedUserId: deps.getAllowedUserId,
|
|
312
323
|
topicTargetStore: deps.topicTargetStore,
|
|
@@ -325,6 +336,9 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
325
336
|
bindings: deps.topicTargetStore.listWorkspaceBindings(),
|
|
326
337
|
binding,
|
|
327
338
|
mode: deps.getThreadDisplayMode!(),
|
|
339
|
+
liveBindingKeys: captureLiveBindingKeys(
|
|
340
|
+
deps.topicTargetStore.listWorkspaceBindings(),
|
|
341
|
+
),
|
|
328
342
|
});
|
|
329
343
|
},
|
|
330
344
|
}
|
|
@@ -337,6 +351,7 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
337
351
|
getMode: deps.getThreadDisplayMode,
|
|
338
352
|
getProfileKey: () => deps.getTelegramProfile?.() ?? "default",
|
|
339
353
|
getLeaderEpoch: () => deps.getCurrentLeaderEpoch?.(),
|
|
354
|
+
captureLiveBindingKeys,
|
|
340
355
|
captureBindingAuthority(binding) {
|
|
341
356
|
const matches = (target: TelegramTarget | undefined) =>
|
|
342
357
|
target?.chatId === binding.target.chatId && target?.threadId === binding.target.threadId;
|
|
@@ -390,8 +405,11 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
390
405
|
throw new Error("Telegram Thread display setting requires current leader authority.");
|
|
391
406
|
}
|
|
392
407
|
const assertDisplayPeers = () => {
|
|
408
|
+
const requiredCapability = mode === "directory-snake" || mode === "directory-title"
|
|
409
|
+
? TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT
|
|
410
|
+
: TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE;
|
|
393
411
|
if (mode !== "names" && deps.runtime.followerRegistry.list().some((follower) =>
|
|
394
|
-
!hasTelegramBusCapability(follower.protocol,
|
|
412
|
+
!hasTelegramBusCapability(follower.protocol, requiredCapability),
|
|
395
413
|
)) throw new Error("Update or restart all connected followers before changing Thread display mode.");
|
|
396
414
|
};
|
|
397
415
|
assertDisplayPeers();
|
|
@@ -413,6 +431,7 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
413
431
|
...provisionerPorts,
|
|
414
432
|
instanceId: deps.instanceId,
|
|
415
433
|
getCwd: deps.getCwd,
|
|
434
|
+
getSessionId: deps.getSessionId,
|
|
416
435
|
getTelegramProfile: deps.getTelegramProfile,
|
|
417
436
|
shouldForceFreshUnnamed: deps.shouldForceFreshUnnamed,
|
|
418
437
|
getRequestedThreadName: deps.getRequestedThreadName,
|
|
@@ -594,14 +613,17 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
594
613
|
);
|
|
595
614
|
return binding?.displayTitle ?? binding?.threadName;
|
|
596
615
|
},
|
|
597
|
-
onFollowerDisconnected: (follower) =>
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
616
|
+
onFollowerDisconnected: async (follower) => {
|
|
617
|
+
await runWorkspaceOperation(
|
|
618
|
+
{
|
|
619
|
+
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
620
|
+
operationKind: "workspace.disconnect-follower",
|
|
621
|
+
scopes: [{ kind: "profile" }],
|
|
622
|
+
},
|
|
623
|
+
() => disconnectFollower(follower),
|
|
624
|
+
);
|
|
625
|
+
scheduleDisplay();
|
|
626
|
+
},
|
|
605
627
|
async renameFollowerThread(follower, threadName) {
|
|
606
628
|
return runWorkspaceOperation({
|
|
607
629
|
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
@@ -689,14 +711,17 @@ export function createTelegramBusLeaderRuntimeAssembly<TContext>(
|
|
|
689
711
|
});
|
|
690
712
|
},
|
|
691
713
|
),
|
|
692
|
-
onFollowerConfirmedDead: (follower) =>
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
714
|
+
onFollowerConfirmedDead: async (follower) => {
|
|
715
|
+
await runWorkspaceOperation(
|
|
716
|
+
{
|
|
717
|
+
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
718
|
+
operationKind: "workspace.cleanup-dead-follower",
|
|
719
|
+
scopes: [{ kind: "profile" }],
|
|
720
|
+
},
|
|
721
|
+
() => cleanupConfirmedDeadFollower(follower),
|
|
722
|
+
);
|
|
723
|
+
scheduleDisplay();
|
|
724
|
+
},
|
|
700
725
|
provisionFollowerTarget: (registration, options) => runWorkspaceOperation(
|
|
701
726
|
{
|
|
702
727
|
operationId: `follower-provision:${registration.instanceId}:${registration.registrationGeneration}`,
|
|
@@ -908,6 +933,7 @@ export function createTelegramBusFollowerTargetProvisioner(
|
|
|
908
933
|
{
|
|
909
934
|
existingBindingOnly:
|
|
910
935
|
options?.existingWorkspaceBindingOnly === true,
|
|
936
|
+
sessionId: registration.sessionId,
|
|
911
937
|
onCapacityUnavailable() {
|
|
912
938
|
capacityUnavailable = true;
|
|
913
939
|
},
|
|
@@ -925,6 +951,7 @@ export function createTelegramBusFollowerTargetProvisioner(
|
|
|
925
951
|
? deps.topicTargetStore.getWorkspaceBinding(
|
|
926
952
|
workspaceIdentity.cwd,
|
|
927
953
|
workspaceIdentity.instanceSlot,
|
|
954
|
+
workspaceIdentity.sessionId,
|
|
928
955
|
)
|
|
929
956
|
: undefined;
|
|
930
957
|
const provision = Threads.createTelegramTopicTargetProvisioner({
|
|
@@ -1567,6 +1594,7 @@ export function createTelegramBusLeaderTargetProvisioner<TContext>(
|
|
|
1567
1594
|
getAllowedUserId: deps.getAllowedUserId,
|
|
1568
1595
|
instanceId: deps.instanceId,
|
|
1569
1596
|
cwd: normalizedCwd,
|
|
1597
|
+
sessionId: deps.getSessionId?.(ctx),
|
|
1570
1598
|
telegramProfile: deps.getTelegramProfile?.(),
|
|
1571
1599
|
forceFreshUnnamed: deps.shouldForceFreshUnnamed?.(),
|
|
1572
1600
|
requestedThreadName: deps.getRequestedThreadName?.(),
|
|
@@ -2101,6 +2129,8 @@ export function createTelegramBusLeaderEnvelopeHandler(deps: {
|
|
|
2101
2129
|
);
|
|
2102
2130
|
if (
|
|
2103
2131
|
!compatibility.compatible || !displayCompatible() ||
|
|
2132
|
+
(envelope.registration.cwd !== undefined &&
|
|
2133
|
+
envelope.registration.sessionId === undefined) ||
|
|
2104
2134
|
(restoringWorkspace && !supportsWorkspaceAutoConnect)
|
|
2105
2135
|
) {
|
|
2106
2136
|
return {
|
|
@@ -2110,7 +2140,11 @@ export function createTelegramBusLeaderEnvelopeHandler(deps: {
|
|
|
2110
2140
|
protocol: deps.protocolIdentity,
|
|
2111
2141
|
error: { code: "incompatible-protocol" as const },
|
|
2112
2142
|
message: `Incompatible Telegram bus protocol: ${
|
|
2113
|
-
compatibility.reason ??
|
|
2143
|
+
compatibility.reason ??
|
|
2144
|
+
(envelope.registration.cwd !== undefined &&
|
|
2145
|
+
envelope.registration.sessionId === undefined
|
|
2146
|
+
? "missing-session-identity"
|
|
2147
|
+
: "missing-capability")
|
|
2114
2148
|
}.`,
|
|
2115
2149
|
};
|
|
2116
2150
|
}
|
|
@@ -2231,9 +2265,12 @@ export function createTelegramBusLeaderEnvelopeHandler(deps: {
|
|
|
2231
2265
|
const current = () => epoch !== undefined && deps.getCurrentLeaderEpoch?.() === epoch &&
|
|
2232
2266
|
deps.followerRegistry.get(envelope.instanceId)?.registrationGeneration === envelope.registrationGeneration;
|
|
2233
2267
|
const follower = deps.followerRegistry.get(envelope.instanceId);
|
|
2268
|
+
const requiredCapability = envelope.mode === "directory-snake" || envelope.mode === "directory-title"
|
|
2269
|
+
? TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT
|
|
2270
|
+
: TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE;
|
|
2234
2271
|
if (!current() || !follower?.registrationGeneration || !deps.applyThreadDisplayMode ||
|
|
2235
|
-
!hasTelegramBusCapability(deps.protocolIdentity,
|
|
2236
|
-
!hasTelegramBusCapability(follower.protocol,
|
|
2272
|
+
!hasTelegramBusCapability(deps.protocolIdentity, requiredCapability) ||
|
|
2273
|
+
!hasTelegramBusCapability(follower.protocol, requiredCapability)) {
|
|
2237
2274
|
return { kind: "bus.ack", requestId: envelope.requestId, ok: false,
|
|
2238
2275
|
message: "Thread display settings require current registration and compatible leader authority." };
|
|
2239
2276
|
}
|
package/lib/bus.ts
CHANGED
|
@@ -220,7 +220,7 @@ export function createTelegramBusAuthSecret(): string {
|
|
|
220
220
|
return randomBytes(32).toString("base64url");
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
export const TELEGRAM_BUS_PROTOCOL_VERSION =
|
|
223
|
+
export const TELEGRAM_BUS_PROTOCOL_VERSION = 2 as const;
|
|
224
224
|
export const TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION =
|
|
225
225
|
"durable-follower-admission-v1" as const;
|
|
226
226
|
export const TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF =
|
|
@@ -230,6 +230,8 @@ export const TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE =
|
|
|
230
230
|
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME =
|
|
231
231
|
"workspace-thread-rename-v1" as const;
|
|
232
232
|
export const TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE = "thread-display-mode-v1" as const;
|
|
233
|
+
export const TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT =
|
|
234
|
+
"directory-display-format-v1" as const;
|
|
233
235
|
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT =
|
|
234
236
|
"workspace-follower-auto-connect-v1" as const;
|
|
235
237
|
|
|
@@ -369,6 +371,7 @@ export interface TelegramBusInstanceRegistration {
|
|
|
369
371
|
threadName?: string;
|
|
370
372
|
slot?: string;
|
|
371
373
|
cwd?: string;
|
|
374
|
+
sessionId?: string;
|
|
372
375
|
pid?: number;
|
|
373
376
|
target?: TelegramTarget;
|
|
374
377
|
busSocketPath?: string;
|
|
@@ -1026,7 +1029,8 @@ export function parseTelegramBusEnvelope(
|
|
|
1026
1029
|
case "follower.setThreadDisplayMode":
|
|
1027
1030
|
if (typeof value.instanceId === "string" &&
|
|
1028
1031
|
typeof value.registrationGeneration === "string" &&
|
|
1029
|
-
(value.mode === "letters" || value.mode === "names" || value.mode === "directories"
|
|
1032
|
+
(value.mode === "letters" || value.mode === "names" || value.mode === "directories" ||
|
|
1033
|
+
value.mode === "directory-snake" || value.mode === "directory-title")) {
|
|
1030
1034
|
envelope = { kind, requestId, instanceId: value.instanceId,
|
|
1031
1035
|
registrationGeneration: value.registrationGeneration, mode: value.mode };
|
|
1032
1036
|
}
|
|
@@ -2991,6 +2995,12 @@ function parseRegistration(
|
|
|
2991
2995
|
registration.slot = value.slot;
|
|
2992
2996
|
}
|
|
2993
2997
|
if (typeof value.cwd === "string") registration.cwd = value.cwd;
|
|
2998
|
+
if (value.sessionId !== undefined) {
|
|
2999
|
+
if (typeof value.sessionId !== "string" || !value.sessionId ||
|
|
3000
|
+
value.sessionId !== value.sessionId.trim() ||
|
|
3001
|
+
Buffer.byteLength(value.sessionId, "utf8") > 256) return undefined;
|
|
3002
|
+
registration.sessionId = value.sessionId;
|
|
3003
|
+
}
|
|
2994
3004
|
if (typeof value.pid === "number") registration.pid = value.pid;
|
|
2995
3005
|
if (typeof value.busSocketPath === "string") {
|
|
2996
3006
|
registration.busSocketPath = value.busSocketPath;
|
package/lib/commands.ts
CHANGED
|
@@ -1205,7 +1205,7 @@ export const TELEGRAM_APP_MENU_INTRO_HTML = [
|
|
|
1205
1205
|
"<b>Pi Telegram</b>",
|
|
1206
1206
|
"",
|
|
1207
1207
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
1208
|
-
`${formatTelegramCommandEmojiPrefix("name")}/name
|
|
1208
|
+
`${formatTelegramCommandEmojiPrefix("name")}/name — Rename this thread`,
|
|
1209
1209
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
1210
1210
|
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
|
1211
1211
|
`${formatTelegramCommandEmojiPrefix("continue")}/continue — Queue continue prompt`,
|
|
@@ -1246,7 +1246,7 @@ function buildTelegramAppMenuIntroHtml(): string {
|
|
|
1246
1246
|
"<b>Pi Telegram</b>",
|
|
1247
1247
|
"",
|
|
1248
1248
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
1249
|
-
`${formatTelegramCommandEmojiPrefix("name")}/name
|
|
1249
|
+
`${formatTelegramCommandEmojiPrefix("name")}/name — Rename this thread`,
|
|
1250
1250
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
1251
1251
|
...extensionLines,
|
|
1252
1252
|
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
package/lib/config.ts
CHANGED
|
@@ -136,16 +136,23 @@ export interface ResolvedTelegramTimeConfig {
|
|
|
136
136
|
timezone: string;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export type TelegramThreadDisplayMode =
|
|
139
|
+
export type TelegramThreadDisplayMode =
|
|
140
|
+
| "letters"
|
|
141
|
+
| "names"
|
|
142
|
+
| "directories"
|
|
143
|
+
| "directory-snake"
|
|
144
|
+
| "directory-title";
|
|
145
|
+
|
|
146
|
+
const TELEGRAM_THREAD_DISPLAY_MODES: readonly TelegramThreadDisplayMode[] = [
|
|
147
|
+
"letters", "names", "directories", "directory-snake", "directory-title",
|
|
148
|
+
];
|
|
140
149
|
|
|
141
150
|
export function resolveTelegramThreadDisplayMode(
|
|
142
151
|
config: Pick<TelegramConfig, "threadDisplayMode">,
|
|
143
152
|
): TelegramThreadDisplayMode {
|
|
144
|
-
return config.threadDisplayMode
|
|
145
|
-
?
|
|
146
|
-
:
|
|
147
|
-
? "directories"
|
|
148
|
-
: "letters";
|
|
153
|
+
return TELEGRAM_THREAD_DISPLAY_MODES.includes(config.threadDisplayMode as TelegramThreadDisplayMode)
|
|
154
|
+
? config.threadDisplayMode as TelegramThreadDisplayMode
|
|
155
|
+
: "letters";
|
|
149
156
|
}
|
|
150
157
|
|
|
151
158
|
export async function setTelegramThreadDisplayMode(
|
|
@@ -153,7 +160,7 @@ export async function setTelegramThreadDisplayMode(
|
|
|
153
160
|
mode: TelegramThreadDisplayMode,
|
|
154
161
|
isCurrent: () => boolean,
|
|
155
162
|
): Promise<void> {
|
|
156
|
-
if (!
|
|
163
|
+
if (!TELEGRAM_THREAD_DISPLAY_MODES.includes(mode)) {
|
|
157
164
|
throw new Error("Invalid Telegram Thread display mode.");
|
|
158
165
|
}
|
|
159
166
|
const profile = store.getActiveProfileName();
|
package/lib/delivery.ts
CHANGED
|
@@ -18,7 +18,10 @@ import {
|
|
|
18
18
|
type TelegramTarget,
|
|
19
19
|
} from "./target.ts";
|
|
20
20
|
import {
|
|
21
|
+
getTelegramApiRetryAfterMs,
|
|
22
|
+
isRetryableTelegramApiError,
|
|
21
23
|
isTelegramApiCommitUnknownError,
|
|
24
|
+
isTelegramMessageUnavailableError,
|
|
22
25
|
type TelegramBridgeApiRuntime,
|
|
23
26
|
} from "./telegram-api.ts";
|
|
24
27
|
|
|
@@ -60,6 +63,9 @@ export type TelegramDeliveryFailureReason =
|
|
|
60
63
|
| "stale-handle"
|
|
61
64
|
| "invalid-view"
|
|
62
65
|
| "commit-unknown"
|
|
66
|
+
| "message-unavailable"
|
|
67
|
+
| "rate-limited"
|
|
68
|
+
| "transport-retryable"
|
|
63
69
|
| "transport-failed";
|
|
64
70
|
|
|
65
71
|
export type TelegramDeliveryResult<T> =
|
|
@@ -68,6 +74,8 @@ export type TelegramDeliveryResult<T> =
|
|
|
68
74
|
ok: false;
|
|
69
75
|
reason: TelegramDeliveryFailureReason;
|
|
70
76
|
message: string;
|
|
77
|
+
/** Exact Telegram flood-control delay when supplied by the API. */
|
|
78
|
+
retryAfterMs?: number;
|
|
71
79
|
/** Successfully materialized state that callers may edit or delete to recover. */
|
|
72
80
|
partial?: T;
|
|
73
81
|
};
|
|
@@ -246,10 +254,26 @@ function failure<T>(
|
|
|
246
254
|
reason: TelegramDeliveryFailureReason,
|
|
247
255
|
message: string,
|
|
248
256
|
partial?: T,
|
|
257
|
+
retryAfterMs?: number,
|
|
249
258
|
): TelegramDeliveryResult<T> {
|
|
259
|
+
const retry = retryAfterMs === undefined ? {} : { retryAfterMs };
|
|
250
260
|
return partial === undefined
|
|
251
|
-
? { ok: false, reason, message }
|
|
252
|
-
: { ok: false, reason, message, partial };
|
|
261
|
+
? { ok: false, reason, message, ...retry }
|
|
262
|
+
: { ok: false, reason, message, partial, ...retry };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function classifyTelegramDeliveryTransportError(error: unknown): {
|
|
266
|
+
reason: Extract<TelegramDeliveryFailureReason,
|
|
267
|
+
"commit-unknown" | "message-unavailable" | "rate-limited" |
|
|
268
|
+
"transport-retryable" | "transport-failed">;
|
|
269
|
+
retryAfterMs?: number;
|
|
270
|
+
} {
|
|
271
|
+
if (isTelegramApiCommitUnknownError(error)) return { reason: "commit-unknown" };
|
|
272
|
+
if (isTelegramMessageUnavailableError(error)) return { reason: "message-unavailable" };
|
|
273
|
+
const retryAfterMs = getTelegramApiRetryAfterMs(error);
|
|
274
|
+
if (retryAfterMs !== undefined) return { reason: "rate-limited", retryAfterMs };
|
|
275
|
+
if (isRetryableTelegramApiError(error)) return { reason: "transport-retryable" };
|
|
276
|
+
return { reason: "transport-failed" };
|
|
253
277
|
}
|
|
254
278
|
|
|
255
279
|
/** @internal */
|
|
@@ -479,14 +503,14 @@ export function createTelegramDeliveryRuntime(
|
|
|
479
503
|
if (error instanceof TelegramDeliveryTransportGenerationError) {
|
|
480
504
|
return inactive();
|
|
481
505
|
}
|
|
506
|
+
const classified = classifyTelegramDeliveryTransportError(error);
|
|
482
507
|
return failure(
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
: "transport-failed",
|
|
486
|
-
isTelegramApiCommitUnknownError(error)
|
|
508
|
+
classified.reason,
|
|
509
|
+
classified.reason === "commit-unknown"
|
|
487
510
|
? `Telegram delivery ${operation} may have committed before transport failed.`
|
|
488
511
|
: `Telegram delivery ${operation} failed.`,
|
|
489
512
|
partial,
|
|
513
|
+
classified.retryAfterMs,
|
|
490
514
|
);
|
|
491
515
|
};
|
|
492
516
|
const createHandle = (
|
|
@@ -837,6 +861,14 @@ export function clearTelegramDeliveryRuntime(): void {
|
|
|
837
861
|
registry.runtime = undefined;
|
|
838
862
|
}
|
|
839
863
|
|
|
864
|
+
/** @internal */
|
|
865
|
+
export function isTelegramDeliveryHandleCurrent(
|
|
866
|
+
handle: TelegramDeliveryHandle,
|
|
867
|
+
): boolean {
|
|
868
|
+
const runtime = getTelegramDeliveryRuntimeRegistry().runtime;
|
|
869
|
+
return runtime !== undefined && runtime.generation === handle.generation;
|
|
870
|
+
}
|
|
871
|
+
|
|
840
872
|
export async function sendTelegramView(
|
|
841
873
|
view: TelegramDeliveryView,
|
|
842
874
|
options: SendTelegramViewOptions,
|