@llblab/pi-telegram 0.26.12 → 0.26.14
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 +0 -13
- package/CHANGELOG.md +10 -0
- package/README.md +8 -0
- package/index.ts +37 -37
- package/lib/activity-verbosity.ts +11 -3
- package/lib/bus-follower.ts +88 -1
- package/lib/delivery.ts +4 -0
- package/lib/lifecycle.ts +62 -0
- package/lib/polling.ts +2 -0
- package/lib/queue.ts +7 -1
- package/lib/sync.ts +3 -1
- package/package.json +1 -1
package/BACKLOG.md
CHANGED
|
@@ -2,19 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
_This backlog tracks only open release-relevant work: hotfixes, bounded maintenance, live runtime verification, evidence-gated Telegram client follow-ups, and upstream Pi API blockers. Completed outcomes and validation evidence belong in `CHANGELOG.md`, not in this queue._
|
|
4
4
|
|
|
5
|
-
## P0 — Monospaced Tool Evidence Keys
|
|
6
|
-
|
|
7
|
-
Context: nested `arguments`, `update`, `result`, and `error` summaries already behave like quote-free outer JSON keys. Rendering the lowercase label itself as inline code completes that semantic relationship without reintroducing icons, markers, or extra height.
|
|
8
|
-
|
|
9
|
-
Open work:
|
|
10
|
-
|
|
11
|
-
- [x] Render every nested tool evidence summary as one lowercase monospaced label, retaining omission suffixes inside the same code span.
|
|
12
|
-
- [x] Preserve root styling, Arguments open-by-default behavior, collapsed secondary evidence, JSON blocks, and all delivery guarantees.
|
|
13
|
-
- [x] Update focused regressions, durable UI documentation, changelog, and package metadata.
|
|
14
|
-
- [ ] Pass live Telegram smoke plus two fresh Linux/macOS/Windows matrices before release.
|
|
15
|
-
|
|
16
|
-
Done when: child keys read as compact code-like metadata, hierarchy and behavior remain unchanged, live clients confirm the style, and the guarded hotfix release completes.
|
|
17
|
-
|
|
18
5
|
## P1 — Windows Graceful Disconnect Test Budget
|
|
19
6
|
|
|
20
7
|
Context: the graceful follower-disconnect integration overrides the production 30-second bus budget with 500 milliseconds even though the path persists cleanup intent, performs close/delete operations, updates binding state, and returns an authenticated response. A loaded Windows runner exceeded that artificial deadline while the same focused path completes promptly under normal scheduling.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.14: Reasoning Activity Throttle
|
|
4
|
+
|
|
5
|
+
- `Reasoning Throttle`: Added a minimum interval between reasoning-block edits (`TELEGRAM_REASONING_MIN_INTERVAL_MS = 1_200`) so the first frame publishes immediately while later frames wait until both the interval and the 160-char delta threshold pass. The final `reasoning-end`/`agent-end` flush stays unconditional, and the 24-frame cap is unchanged. Impact: fast-token models with very large reasoning blocks no longer push an edit per character burst, so parallel threads stop saturating the bus and inbound Telegram messages arrive without multi-second delay.
|
|
6
|
+
|
|
7
|
+
## 0.26.13: Entrypoint Compression And BotFather Prerequisites
|
|
8
|
+
|
|
9
|
+
- `Entrypoint Compression`: Moved low-level policy out of `index.ts` into owning domains: the deferred queue dispatch delay defaults in the queue domain, the delivery generation seed is built by the delivery domain, the `target-bindings` sync slice key and the default profile name resolve from their domains, and bus-runtime capability answers come from the thread capability state. Removed a redundant follower client timeout literal that duplicated the domain default. Impact: the composition root wires ports while constants, generations, and capability decisions stay with their owning domains.
|
|
10
|
+
- `Assembly Ownership`: Extracted the follower assembly (receiver, target replacement, recovery, registration) and session-lifecycle assembly (queue, follower, services) dependency structures into builders owned by `bus-follower` and `lifecycle`, normalizing shared ports and moving auth-secret derivation and grouped-input sequencing into domain runtimes. Impact: assembly shape and policy live in the owning domains, and the entrypoint reads as named construction steps instead of nested constructor calls.
|
|
11
|
+
- `Setup Documentation`: Documented the BotFather prerequisites for guest mode, private-chat Threaded Mode, and queue-reaction admin rights in the Quick Start. Impact: operators can enable the capabilities a feature needs instead of discovering that a disabled capability silently never triggers.
|
|
12
|
+
|
|
3
13
|
## 0.26.12: Monospaced Tool Evidence Keys
|
|
4
14
|
|
|
5
15
|
- `Tool Evidence Typography`: Rendered nested `arguments`, `update`, `result`, and `error` summaries as lowercase inline code, including any dropped-update suffix in the same span. Impact: child labels read as compact quote-free outer JSON keys while root styling, disclosure state, evidence blocks, message height, and delivery guarantees remain unchanged.
|
package/README.md
CHANGED
|
@@ -67,6 +67,14 @@ Open the bot DM and send:
|
|
|
67
67
|
|
|
68
68
|
The first Telegram user to message the bot becomes the allowed owner. Other users are ignored.
|
|
69
69
|
|
|
70
|
+
### 5. Enable optional bot capabilities in BotFather
|
|
71
|
+
|
|
72
|
+
Enable the optional capabilities the bridge needs in [@BotFather](https://t.me/BotFather). The bridge does not fail loudly when a capability is off; the feature simply never triggers.
|
|
73
|
+
|
|
74
|
+
1. Enable guest mode so the bot can answer mentions and replies in chats where it is not a member.
|
|
75
|
+
2. Enable private-chat Threaded Mode; when it is available, one live instance becomes the profile's leader and later visible Pi instances register as followers. Without it, the bridge stays in classic single-owner DM mode.
|
|
76
|
+
3. Make the bot an administrator in any chat where the queue reaction shortcuts (👍 promote, 👎 remove) should work. Reaction updates require admin rights, so the shortcuts silently do nothing in non-admin chats; private chats deliver reactions without admin rights.
|
|
77
|
+
|
|
70
78
|
## What It Feels Like
|
|
71
79
|
|
|
72
80
|
- Start a task in the terminal, walk away, and keep supervising it from your phone.
|
package/index.ts
CHANGED
|
@@ -107,7 +107,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
107
107
|
Ownership.createTelegramBusMessageOwnershipRuntime({
|
|
108
108
|
instanceId: telegramInstanceId,
|
|
109
109
|
getProfileKey() {
|
|
110
|
-
return getActiveTelegramThreadProfile() ??
|
|
110
|
+
return getActiveTelegramThreadProfile() ?? Config.TELEGRAM_DEFAULT_PROFILE_NAME;
|
|
111
111
|
},
|
|
112
112
|
listFollowers: telegramBusFollowerRegistry.list,
|
|
113
113
|
});
|
|
@@ -117,9 +117,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
117
117
|
const runtimeEvents = runtimeDiagnostics.events;
|
|
118
118
|
const recordRuntimeEvent = runtimeDiagnostics.recordRuntimeEvent;
|
|
119
119
|
const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
|
|
120
|
-
const isTelegramBusRuntimeEnabled =
|
|
121
|
-
return !telegramThreadCapabilityState.isTopicModeUnavailable();
|
|
122
|
-
};
|
|
120
|
+
const isTelegramBusRuntimeEnabled = telegramThreadCapabilityState.isBusRuntimeEnabled;
|
|
123
121
|
Config.bindGlobalTelegramConfigRuntime(configStore);
|
|
124
122
|
const configControls = Config.createTelegramConfigControls(configStore);
|
|
125
123
|
const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
|
|
@@ -225,7 +223,6 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
225
223
|
);
|
|
226
224
|
const deferredQueueDispatchRuntime =
|
|
227
225
|
Queue.createTelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>({
|
|
228
|
-
delayMs: 50,
|
|
229
226
|
recordRuntimeEvent,
|
|
230
227
|
});
|
|
231
228
|
const pollingControllerState = Polling.createTelegramPollingControllerState();
|
|
@@ -364,7 +361,6 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
364
361
|
getForwardCommentBatchPosition:
|
|
365
362
|
textGroupRuntime.getPreparedForwardingPosition,
|
|
366
363
|
recordRuntimeEvent,
|
|
367
|
-
timeoutMs: 30_000,
|
|
368
364
|
});
|
|
369
365
|
const telegramApiRuntime = BusApi.createTelegramBusAwareApiRuntime({
|
|
370
366
|
directRuntime: directTelegramApiRuntime,
|
|
@@ -446,7 +442,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
446
442
|
getActiveTurnTarget: activeTurnRuntime.getTarget,
|
|
447
443
|
getActiveGuestQueryId: activeTurnRuntime.getGuestQueryId,
|
|
448
444
|
});
|
|
449
|
-
const deliveryGenerationSeed =
|
|
445
|
+
const deliveryGenerationSeed =
|
|
446
|
+
Delivery.createTelegramDeliveryGenerationSeed(telegramInstanceId);
|
|
450
447
|
const deliveryLifecycleRuntime =
|
|
451
448
|
Delivery.createTelegramBridgeDeliveryLifecycleHooks({
|
|
452
449
|
generationSeed: deliveryGenerationSeed,
|
|
@@ -687,10 +684,13 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
687
684
|
followerRegistry: telegramBusFollowerRegistry,
|
|
688
685
|
followerTargetController,
|
|
689
686
|
onRestored() {
|
|
690
|
-
telegramSyncStateRuntime.markSliceFresh(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
687
|
+
telegramSyncStateRuntime.markSliceFresh(
|
|
688
|
+
Sync.TELEGRAM_SYNC_SLICE_TARGET_BINDINGS,
|
|
689
|
+
{
|
|
690
|
+
nowMs: Date.now(),
|
|
691
|
+
action: "follower-thread-restore",
|
|
692
|
+
},
|
|
693
|
+
);
|
|
694
694
|
},
|
|
695
695
|
});
|
|
696
696
|
const observedThreadTargetBinding =
|
|
@@ -811,58 +811,54 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
811
811
|
handleAuthorizedReactionUpdate:
|
|
812
812
|
inboundRouteRuntime.handleAuthorizedReactionUpdate,
|
|
813
813
|
});
|
|
814
|
-
const
|
|
815
|
-
BusFollower.
|
|
814
|
+
const telegramBusFollowerAssemblyDeps =
|
|
815
|
+
BusFollower.createTelegramBusFollowerRuntimeAssemblyDeps<
|
|
816
816
|
Pi.ExtensionContext,
|
|
817
817
|
Updates.TelegramMessageReactionUpdated,
|
|
818
818
|
Routing.TelegramRoutedCallbackQuery,
|
|
819
819
|
Routing.TelegramRoutedMessage
|
|
820
820
|
>({
|
|
821
|
+
instanceId: telegramInstanceId,
|
|
822
|
+
registrationState: telegramBusFollowerRegistrationState,
|
|
823
|
+
recordRuntimeEvent,
|
|
821
824
|
receiver: {
|
|
822
825
|
socketPath: getTelegramBusFollowerSocketPath,
|
|
823
|
-
instanceId: telegramInstanceId,
|
|
824
826
|
getContext: telegramSessionContextStore.get,
|
|
825
827
|
getAuthSecret: telegramBusFollowerControlState.getActiveAuthSecret,
|
|
826
828
|
...forwardedRouteHandlers,
|
|
827
829
|
prepareForwardedMessage: textGroupRuntime.prepareForwardedMessage,
|
|
828
|
-
recordRuntimeEvent,
|
|
829
830
|
},
|
|
830
831
|
targetReplacement: {
|
|
831
832
|
topicTargetStore: threadStore,
|
|
832
|
-
registrationState: telegramBusFollowerRegistrationState,
|
|
833
|
-
instanceId: telegramInstanceId,
|
|
834
833
|
getManualFollowerProfileKey: getTelegramManualFollowerProfileKey,
|
|
835
834
|
manualFollowerOwnerId: telegramManualFollowerOwnerId,
|
|
836
835
|
getSyncState: telegramSyncStateRuntime.getState,
|
|
837
836
|
setSyncState: telegramSyncStateRuntime.setState,
|
|
838
837
|
updateStatus,
|
|
839
|
-
recordRuntimeEvent,
|
|
840
838
|
},
|
|
841
839
|
recovery: {
|
|
842
|
-
registrationState: telegramBusFollowerRegistrationState,
|
|
843
840
|
getLeaderState: lockRuntime.getState,
|
|
844
841
|
setLifecyclePhase: telegramBusFollowerControlState.setLifecyclePhase,
|
|
845
842
|
updateStatus,
|
|
846
843
|
promoteToLeader: promoteTelegramBusFollowerToLeader,
|
|
847
844
|
getActiveContext: telegramSessionContextStore.get,
|
|
848
|
-
recordRuntimeEvent,
|
|
849
845
|
},
|
|
850
846
|
registration: {
|
|
851
|
-
instanceId: telegramInstanceId,
|
|
852
847
|
getFollowerBusSocketPath: getTelegramBusFollowerSocketPath,
|
|
853
848
|
getLeaderSocketPath: getTelegramBusSocketPath,
|
|
854
|
-
registrationState: telegramBusFollowerRegistrationState,
|
|
855
849
|
isContextActive: telegramSessionContextStore.isCurrent,
|
|
856
850
|
createRequestId: telegramBusFollowerClients.createRequestId,
|
|
857
|
-
|
|
858
|
-
return owner.busSecret;
|
|
859
|
-
},
|
|
860
|
-
setActiveAuthSecret:
|
|
861
|
-
telegramBusFollowerControlState.setActiveAuthSecret,
|
|
851
|
+
setActiveAuthSecret: telegramBusFollowerControlState.setActiveAuthSecret,
|
|
862
852
|
getProfileKey: getTelegramManualFollowerProfileKey,
|
|
863
|
-
recordRuntimeEvent,
|
|
864
853
|
},
|
|
865
854
|
});
|
|
855
|
+
const telegramBusFollowerAssembly: BusFollower.TelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext> =
|
|
856
|
+
BusFollower.createTelegramBusFollowerRuntimeAssembly<
|
|
857
|
+
Pi.ExtensionContext,
|
|
858
|
+
Updates.TelegramMessageReactionUpdated,
|
|
859
|
+
Routing.TelegramRoutedCallbackQuery,
|
|
860
|
+
Routing.TelegramRoutedMessage
|
|
861
|
+
>(telegramBusFollowerAssemblyDeps);
|
|
866
862
|
const telegramBusFollowerRegistration =
|
|
867
863
|
telegramBusFollowerAssembly.registration;
|
|
868
864
|
const pollingRuntime = Polling.createTelegramPollingControllerRuntime({
|
|
@@ -1001,8 +997,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1001
997
|
stopPolling: lockedPollingRuntime.stop,
|
|
1002
998
|
recordRuntimeEvent,
|
|
1003
999
|
});
|
|
1004
|
-
const
|
|
1005
|
-
Lifecycle.
|
|
1000
|
+
const telegramBridgeSessionLifecycleDeps =
|
|
1001
|
+
Lifecycle.createTelegramBridgeSessionLifecycleDeps({
|
|
1006
1002
|
contextStore: telegramSessionContextStore,
|
|
1007
1003
|
queue: {
|
|
1008
1004
|
getCurrentModel: getContextModel,
|
|
@@ -1038,20 +1034,24 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1038
1034
|
recordRuntimeEvent,
|
|
1039
1035
|
},
|
|
1040
1036
|
services: {
|
|
1041
|
-
|
|
1042
|
-
mediaGroupRuntime.resume
|
|
1043
|
-
|
|
1037
|
+
mediaGroup: {
|
|
1038
|
+
resume: mediaGroupRuntime.resume,
|
|
1039
|
+
suspend: mediaGroupRuntime.suspend,
|
|
1040
|
+
},
|
|
1041
|
+
textGroup: {
|
|
1042
|
+
resume: textGroupRuntime.resume,
|
|
1043
|
+
suspend: textGroupRuntime.suspend,
|
|
1044
1044
|
},
|
|
1045
|
-
suspendGroupedInput: TextGroups.createTelegramGroupedInputClearer({
|
|
1046
|
-
clearMediaGroups: mediaGroupRuntime.suspend,
|
|
1047
|
-
clearTextGroups: textGroupRuntime.suspend,
|
|
1048
|
-
}),
|
|
1049
1045
|
delivery: deliveryLifecycleRuntime,
|
|
1050
1046
|
polling: lockedPollingRuntime,
|
|
1051
1047
|
capabilityMonitor: telegramThreadCapabilityMonitor,
|
|
1052
1048
|
queueWatchdog: queueDispatchWatchdogRuntime,
|
|
1053
1049
|
},
|
|
1054
1050
|
});
|
|
1051
|
+
const sessionLifecycleRuntime =
|
|
1052
|
+
Lifecycle.createTelegramBridgeSessionLifecycleAssembly(
|
|
1053
|
+
telegramBridgeSessionLifecycleDeps,
|
|
1054
|
+
);
|
|
1055
1055
|
|
|
1056
1056
|
// --- Extension API Bindings ---
|
|
1057
1057
|
|
|
@@ -24,6 +24,7 @@ export const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3_900;
|
|
|
24
24
|
export const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
|
|
25
25
|
export const TELEGRAM_REASONING_MESSAGE_MAX_FRAMES = 24;
|
|
26
26
|
export const TELEGRAM_REASONING_BUFFER_MAX_CHARS = 1_200;
|
|
27
|
+
export const TELEGRAM_REASONING_MIN_INTERVAL_MS = 1_200;
|
|
27
28
|
export const TELEGRAM_TOOL_UPDATE_MAX_ENTRIES = 4;
|
|
28
29
|
|
|
29
30
|
interface ToolActivity {
|
|
@@ -291,6 +292,7 @@ export interface TelegramActivityVerbosityRuntime {
|
|
|
291
292
|
export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
292
293
|
getActivityMode: () => "quiet" | "thinking" | "tools" | "verbose";
|
|
293
294
|
refreshActivityMode?: () => Promise<void>;
|
|
295
|
+
getNowMs?: () => number;
|
|
294
296
|
resolveTarget: (event: TelegramActivityEvent) => TelegramTarget | undefined;
|
|
295
297
|
captureAuthority: () => TAuthority;
|
|
296
298
|
isAuthorityActive: (authority: TAuthority) => boolean;
|
|
@@ -315,6 +317,7 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
|
315
317
|
let active = true;
|
|
316
318
|
let generation = 0;
|
|
317
319
|
let tail = Promise.resolve();
|
|
320
|
+
const getNowMs = deps.getNowMs ?? Date.now;
|
|
318
321
|
let activityId: string | undefined;
|
|
319
322
|
let authority: TAuthority | undefined;
|
|
320
323
|
let target: TelegramTarget | undefined;
|
|
@@ -324,6 +327,7 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
|
324
327
|
let lastReasoningMessageChars = 0;
|
|
325
328
|
let reasoningMessage: ReasoningMessage | undefined;
|
|
326
329
|
let reasoningBlocked = false;
|
|
330
|
+
let lastReasoningPublishMs = 0;
|
|
327
331
|
let toolMessage: ToolMessage | undefined;
|
|
328
332
|
const tools = new Map<string, ToolActivity>();
|
|
329
333
|
const toolOrder: string[] = [];
|
|
@@ -338,6 +342,7 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
|
338
342
|
lastReasoningMessageChars = 0;
|
|
339
343
|
reasoningMessage = undefined;
|
|
340
344
|
reasoningBlocked = false;
|
|
345
|
+
lastReasoningPublishMs = 0;
|
|
341
346
|
toolMessage = undefined;
|
|
342
347
|
tools.clear();
|
|
343
348
|
toolOrder.length = 0;
|
|
@@ -412,6 +417,7 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
|
412
417
|
if (generation !== acceptedGeneration) return;
|
|
413
418
|
reasoningMessageFrames += 1;
|
|
414
419
|
lastReasoningMessageChars = reasoningChars;
|
|
420
|
+
lastReasoningPublishMs = getNowMs();
|
|
415
421
|
} catch (error) {
|
|
416
422
|
deps.recordFailure?.(
|
|
417
423
|
canEdit ? "reasoning-edit" : "reasoning-send",
|
|
@@ -552,10 +558,11 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
|
552
558
|
reasoningBuffer = `${reasoningBuffer}${event.delta}`.slice(
|
|
553
559
|
-TELEGRAM_REASONING_BUFFER_MAX_CHARS,
|
|
554
560
|
);
|
|
555
|
-
if (
|
|
556
|
-
reasoningMessageFrames < TELEGRAM_REASONING_MESSAGE_MAX_FRAMES &&
|
|
561
|
+
if (reasoningMessageFrames < TELEGRAM_REASONING_MESSAGE_MAX_FRAMES &&
|
|
557
562
|
(reasoningMessageFrames === 0 ||
|
|
558
|
-
|
|
563
|
+
(getNowMs() - lastReasoningPublishMs >=
|
|
564
|
+
TELEGRAM_REASONING_MIN_INTERVAL_MS &&
|
|
565
|
+
reasoningChars - lastReasoningMessageChars >= 160))
|
|
559
566
|
) {
|
|
560
567
|
await publishReasoning(event, acceptedGeneration);
|
|
561
568
|
}
|
|
@@ -580,6 +587,7 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
|
580
587
|
reasoningChars = 0;
|
|
581
588
|
reasoningMessageFrames = 0;
|
|
582
589
|
lastReasoningMessageChars = 0;
|
|
590
|
+
lastReasoningPublishMs = 0;
|
|
583
591
|
reasoningMessage = undefined;
|
|
584
592
|
reasoningBlocked = false;
|
|
585
593
|
return;
|
package/lib/bus-follower.ts
CHANGED
|
@@ -514,6 +514,91 @@ export function createTelegramBusForwardedRouteHandlers<
|
|
|
514
514
|
};
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
+
export interface TelegramBusFollowerRuntimeAssemblyPorts<
|
|
518
|
+
TContext extends { cwd?: string },
|
|
519
|
+
TReactionUpdate,
|
|
520
|
+
TCallbackQuery,
|
|
521
|
+
TMessage = unknown,
|
|
522
|
+
> {
|
|
523
|
+
instanceId: string;
|
|
524
|
+
registrationState: TelegramBusFollowerRegistrationState;
|
|
525
|
+
recordRuntimeEvent: (
|
|
526
|
+
category: string,
|
|
527
|
+
error: unknown,
|
|
528
|
+
details?: Record<string, unknown>,
|
|
529
|
+
) => void;
|
|
530
|
+
receiver: Omit<
|
|
531
|
+
TelegramBusForwardedUpdateReceiverRuntimeDeps<
|
|
532
|
+
TContext,
|
|
533
|
+
TReactionUpdate,
|
|
534
|
+
TCallbackQuery,
|
|
535
|
+
TMessage
|
|
536
|
+
>,
|
|
537
|
+
"handleReplaceTarget" | "instanceId" | "recordRuntimeEvent"
|
|
538
|
+
>;
|
|
539
|
+
targetReplacement: Omit<
|
|
540
|
+
TelegramBusFollowerTargetReplacementHandlerDeps<TContext>,
|
|
541
|
+
"registrationState" | "instanceId" | "recordRuntimeEvent"
|
|
542
|
+
>;
|
|
543
|
+
recovery: Omit<
|
|
544
|
+
TelegramBusFollowerHeartbeatRecoveryHandlerDeps<TContext>,
|
|
545
|
+
"getRegistrationRuntime" | "registrationState" | "recordRuntimeEvent"
|
|
546
|
+
>;
|
|
547
|
+
registration: Omit<
|
|
548
|
+
TelegramBusFollowerRegistrationRuntimeDeps<TContext>,
|
|
549
|
+
| "startReceiving"
|
|
550
|
+
| "stopReceiving"
|
|
551
|
+
| "onHeartbeatFailure"
|
|
552
|
+
| "instanceId"
|
|
553
|
+
| "registrationState"
|
|
554
|
+
| "recordRuntimeEvent"
|
|
555
|
+
>;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
export function createTelegramBusFollowerRuntimeAssemblyDeps<
|
|
559
|
+
TContext extends { cwd?: string },
|
|
560
|
+
TReactionUpdate,
|
|
561
|
+
TCallbackQuery,
|
|
562
|
+
TMessage = unknown,
|
|
563
|
+
>(
|
|
564
|
+
ports: TelegramBusFollowerRuntimeAssemblyPorts<
|
|
565
|
+
TContext,
|
|
566
|
+
TReactionUpdate,
|
|
567
|
+
TCallbackQuery,
|
|
568
|
+
TMessage
|
|
569
|
+
>,
|
|
570
|
+
): TelegramBusFollowerRuntimeAssemblyDeps<
|
|
571
|
+
TContext,
|
|
572
|
+
TReactionUpdate,
|
|
573
|
+
TCallbackQuery,
|
|
574
|
+
TMessage
|
|
575
|
+
> {
|
|
576
|
+
return {
|
|
577
|
+
receiver: {
|
|
578
|
+
...ports.receiver,
|
|
579
|
+
instanceId: ports.instanceId,
|
|
580
|
+
recordRuntimeEvent: ports.recordRuntimeEvent,
|
|
581
|
+
},
|
|
582
|
+
targetReplacement: {
|
|
583
|
+
...ports.targetReplacement,
|
|
584
|
+
instanceId: ports.instanceId,
|
|
585
|
+
registrationState: ports.registrationState,
|
|
586
|
+
recordRuntimeEvent: ports.recordRuntimeEvent,
|
|
587
|
+
},
|
|
588
|
+
recovery: {
|
|
589
|
+
...ports.recovery,
|
|
590
|
+
registrationState: ports.registrationState,
|
|
591
|
+
recordRuntimeEvent: ports.recordRuntimeEvent,
|
|
592
|
+
},
|
|
593
|
+
registration: {
|
|
594
|
+
...ports.registration,
|
|
595
|
+
instanceId: ports.instanceId,
|
|
596
|
+
registrationState: ports.registrationState,
|
|
597
|
+
recordRuntimeEvent: ports.recordRuntimeEvent,
|
|
598
|
+
},
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
517
602
|
export function createTelegramBusFollowerRuntimeAssembly<
|
|
518
603
|
TContext extends { cwd?: string },
|
|
519
604
|
TReactionUpdate,
|
|
@@ -1260,7 +1345,9 @@ export function createTelegramBusFollowerRegistrationRuntime<
|
|
|
1260
1345
|
deps.getLeaderSocketPath?.() ??
|
|
1261
1346
|
getTelegramBusSocketPath();
|
|
1262
1347
|
await deps.startReceiving?.();
|
|
1263
|
-
activeAuthSecret = deps.getLeaderAuthSecret
|
|
1348
|
+
activeAuthSecret = deps.getLeaderAuthSecret
|
|
1349
|
+
? deps.getLeaderAuthSecret(leader)
|
|
1350
|
+
: leader?.busSecret;
|
|
1264
1351
|
deps.setActiveAuthSecret?.(activeAuthSecret);
|
|
1265
1352
|
const registrationGeneration = deps.createRequestId();
|
|
1266
1353
|
const registrationEnvelope: Extract<
|
package/lib/delivery.ts
CHANGED
|
@@ -198,6 +198,10 @@ export function createTelegramDeliveryLifecycleHooks(
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
export function createTelegramDeliveryGenerationSeed(instanceId: string): string {
|
|
202
|
+
return `${instanceId}:${Date.now()}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
201
205
|
/** @internal */
|
|
202
206
|
export function createTelegramBridgeDeliveryLifecycleHooks<TTransportStamp>(
|
|
203
207
|
deps: Omit<
|
package/lib/lifecycle.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import * as BusFollower from "./bus-follower.ts";
|
|
8
8
|
import * as Queue from "./queue.ts";
|
|
9
|
+
import * as TextGroups from "./text-groups.ts";
|
|
9
10
|
import type {
|
|
10
11
|
AgentEndEvent,
|
|
11
12
|
AgentSettledEvent,
|
|
@@ -253,6 +254,67 @@ export interface TelegramBridgeSessionLifecycleAssemblyDeps<
|
|
|
253
254
|
services: TelegramBridgeSessionServiceRuntime;
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
export interface TelegramBridgeSessionLifecyclePorts<
|
|
258
|
+
TQueueItem,
|
|
259
|
+
TModel = unknown,
|
|
260
|
+
> {
|
|
261
|
+
contextStore: TelegramSessionContextStore<ExtensionContext>;
|
|
262
|
+
queue: Omit<
|
|
263
|
+
Queue.TelegramSessionLifecycleRuntimeDeps<
|
|
264
|
+
ExtensionContext,
|
|
265
|
+
TQueueItem,
|
|
266
|
+
TModel
|
|
267
|
+
>,
|
|
268
|
+
"isSessionActive" | "stopPolling" | "clearPendingMediaGroups"
|
|
269
|
+
>;
|
|
270
|
+
follower: Omit<
|
|
271
|
+
BusFollower.TelegramBusFollowerSessionRefreshHookDeps<ExtensionContext>,
|
|
272
|
+
"isSessionActive"
|
|
273
|
+
> &
|
|
274
|
+
BusFollower.TelegramBusFollowerSessionReplacementSuspenderDeps;
|
|
275
|
+
services: {
|
|
276
|
+
mediaGroup: {
|
|
277
|
+
resume(ctx: ExtensionContext): void;
|
|
278
|
+
suspend(): void;
|
|
279
|
+
};
|
|
280
|
+
textGroup: {
|
|
281
|
+
resume(ctx: ExtensionContext): void;
|
|
282
|
+
suspend(): void;
|
|
283
|
+
};
|
|
284
|
+
delivery: TelegramBridgeSessionServiceRuntime["delivery"];
|
|
285
|
+
polling: TelegramBridgeSessionServiceRuntime["polling"];
|
|
286
|
+
capabilityMonitor: TelegramBridgeSessionServiceRuntime["capabilityMonitor"];
|
|
287
|
+
queueWatchdog: TelegramBridgeSessionServiceRuntime["queueWatchdog"];
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function createTelegramBridgeSessionLifecycleDeps<
|
|
292
|
+
TQueueItem,
|
|
293
|
+
TModel = unknown,
|
|
294
|
+
>(
|
|
295
|
+
ports: TelegramBridgeSessionLifecyclePorts<TQueueItem, TModel>,
|
|
296
|
+
): TelegramBridgeSessionLifecycleAssemblyDeps<TQueueItem, TModel> {
|
|
297
|
+
return {
|
|
298
|
+
contextStore: ports.contextStore,
|
|
299
|
+
queue: ports.queue,
|
|
300
|
+
follower: ports.follower,
|
|
301
|
+
services: {
|
|
302
|
+
resumeGroupedInput(ctx) {
|
|
303
|
+
ports.services.mediaGroup.resume(ctx);
|
|
304
|
+
ports.services.textGroup.resume(ctx);
|
|
305
|
+
},
|
|
306
|
+
suspendGroupedInput: TextGroups.createTelegramGroupedInputClearer({
|
|
307
|
+
clearMediaGroups: ports.services.mediaGroup.suspend,
|
|
308
|
+
clearTextGroups: ports.services.textGroup.suspend,
|
|
309
|
+
}),
|
|
310
|
+
delivery: ports.services.delivery,
|
|
311
|
+
polling: ports.services.polling,
|
|
312
|
+
capabilityMonitor: ports.services.capabilityMonitor,
|
|
313
|
+
queueWatchdog: ports.services.queueWatchdog,
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
256
318
|
export function createTelegramBridgeSessionLifecycleAssembly<
|
|
257
319
|
TQueueItem,
|
|
258
320
|
TModel = unknown,
|
package/lib/polling.ts
CHANGED
|
@@ -351,6 +351,7 @@ export interface TelegramThreadCapabilityStateRuntime {
|
|
|
351
351
|
setBusPollingStarted(started: boolean): void;
|
|
352
352
|
isTopicModeUnavailable(): boolean;
|
|
353
353
|
setTopicModeUnavailable(unavailable: boolean): void;
|
|
354
|
+
isBusRuntimeEnabled(): boolean;
|
|
354
355
|
shouldForceFreshLeaderThread(): boolean;
|
|
355
356
|
setForceFreshLeaderThread(forceFresh: boolean): void;
|
|
356
357
|
}
|
|
@@ -458,6 +459,7 @@ export function createTelegramThreadCapabilityStateRuntime(): TelegramThreadCapa
|
|
|
458
459
|
setTopicModeUnavailable(unavailable) {
|
|
459
460
|
topicModeUnavailable = unavailable;
|
|
460
461
|
},
|
|
462
|
+
isBusRuntimeEnabled: () => !topicModeUnavailable,
|
|
461
463
|
shouldForceFreshLeaderThread: () => forceFreshLeaderThread,
|
|
462
464
|
setForceFreshLeaderThread(forceFresh) {
|
|
463
465
|
forceFreshLeaderThread = forceFresh;
|
package/lib/queue.ts
CHANGED
|
@@ -2133,13 +2133,19 @@ export interface TelegramDeferredQueueDispatchRuntime<TContext = unknown> {
|
|
|
2133
2133
|
request: (dispatchNextQueuedTelegramTurn: (ctx: TContext) => void) => void;
|
|
2134
2134
|
}
|
|
2135
2135
|
|
|
2136
|
+
/**
|
|
2137
|
+
* Production debounce for deferred queue dispatch; the factory defaults to this
|
|
2138
|
+
* so the entrypoint wires ports instead of policy constants.
|
|
2139
|
+
*/
|
|
2140
|
+
export const TELEGRAM_DEFERRED_DISPATCH_DELAY_MS = 50;
|
|
2141
|
+
|
|
2136
2142
|
export function createTelegramDeferredQueueDispatchRuntime<TContext = unknown>(
|
|
2137
2143
|
deps: TelegramDeferredQueueDispatchRuntimeDeps = {},
|
|
2138
2144
|
): TelegramDeferredQueueDispatchRuntime<TContext> {
|
|
2139
2145
|
let boundContext: TContext | undefined;
|
|
2140
2146
|
let generation = 0;
|
|
2141
2147
|
const timers = new Set<ReturnType<typeof setTimeout>>();
|
|
2142
|
-
const delayMs = deps.delayMs ??
|
|
2148
|
+
const delayMs = deps.delayMs ?? TELEGRAM_DEFERRED_DISPATCH_DELAY_MS;
|
|
2143
2149
|
const setTimer =
|
|
2144
2150
|
deps.setTimer ??
|
|
2145
2151
|
((callback: () => void, ms: number): ReturnType<typeof setTimeout> =>
|
package/lib/sync.ts
CHANGED
|
@@ -486,6 +486,8 @@ export async function ensureTelegramLeaderThreadBinding(
|
|
|
486
486
|
return ownTarget;
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
export const TELEGRAM_SYNC_SLICE_TARGET_BINDINGS = "target-bindings";
|
|
490
|
+
|
|
489
491
|
export const TELEGRAM_SYNC_SLICES = [
|
|
490
492
|
"bot-identity",
|
|
491
493
|
"bot-capabilities",
|
|
@@ -493,7 +495,7 @@ export const TELEGRAM_SYNC_SLICES = [
|
|
|
493
495
|
"allowed-user",
|
|
494
496
|
"topic-capability",
|
|
495
497
|
"topic-state",
|
|
496
|
-
|
|
498
|
+
TELEGRAM_SYNC_SLICE_TARGET_BINDINGS,
|
|
497
499
|
"reservations",
|
|
498
500
|
"transport-health",
|
|
499
501
|
] as const;
|