@llblab/pi-telegram 0.54.3 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -2
- package/BACKLOG.md +1 -1
- package/CHANGELOG.md +21 -0
- package/README.md +5 -3
- package/dist/lib/activity.d.ts +5 -1
- package/dist/lib/activity.js +31 -2
- package/dist/lib/bindings.d.ts +5 -1
- package/dist/lib/bindings.js +9 -2
- package/dist/lib/bus-api.d.ts +2 -2
- package/dist/lib/bus-api.js +7 -2
- package/dist/lib/bus-follower.d.ts +49 -3
- package/dist/lib/bus-follower.js +313 -146
- package/dist/lib/bus-leader.d.ts +3 -0
- package/dist/lib/bus-leader.js +99 -18
- package/dist/lib/bus.d.ts +5 -3
- package/dist/lib/bus.js +13 -10
- package/dist/lib/delivery.d.ts +4 -0
- package/dist/lib/delivery.js +48 -15
- package/dist/lib/extension.js +56 -6
- package/dist/lib/lifecycle.d.ts +68 -0
- package/dist/lib/lifecycle.js +178 -58
- package/dist/lib/machine.d.ts +23 -0
- package/dist/lib/machine.js +37 -0
- package/dist/lib/media.d.ts +114 -28
- package/dist/lib/media.js +292 -119
- package/dist/lib/model.d.ts +54 -15
- package/dist/lib/model.js +98 -53
- package/dist/lib/polling.js +2 -0
- package/dist/lib/preview.d.ts +128 -113
- package/dist/lib/preview.js +448 -366
- package/dist/lib/queue.d.ts +2 -2
- package/dist/lib/queue.js +3 -3
- package/dist/lib/routing.d.ts +14 -2
- package/dist/lib/routing.js +546 -207
- package/dist/lib/runtime.d.ts +77 -11
- package/dist/lib/runtime.js +174 -92
- package/dist/lib/sections.js +0 -1
- package/dist/lib/telegram-api.d.ts +11 -9
- package/dist/lib/telegram-api.js +53 -4
- package/dist/lib/text-groups.d.ts +0 -26
- package/dist/lib/text-groups.js +51 -98
- package/dist/lib/thread-display.js +14 -6
- package/dist/lib/threads.d.ts +20 -5
- package/dist/lib/threads.js +111 -32
- package/dist/lib/turns.d.ts +2 -0
- package/dist/lib/turns.js +6 -1
- package/dist/lib/updates.d.ts +11 -4
- package/dist/lib/updates.js +54 -19
- package/dist/package.json +1 -1
- package/docs/activity.md +1 -1
- package/docs/architecture.md +16 -15
- package/docs/delivery.md +7 -5
- package/docs/multi-instance-bus.md +18 -8
- package/docs/public-api.md +2 -2
- package/docs/ui-style.md +8 -6
- package/lib/activity.ts +29 -5
- package/lib/bindings.ts +13 -1
- package/lib/bus-api.ts +11 -3
- package/lib/bus-follower.ts +417 -203
- package/lib/bus-leader.ts +88 -14
- package/lib/bus.ts +17 -15
- package/lib/delivery.ts +47 -19
- package/lib/extension.ts +59 -9
- package/lib/lifecycle.ts +229 -55
- package/lib/machine.ts +60 -0
- package/lib/media.ts +438 -172
- package/lib/model.ts +169 -74
- package/lib/polling.ts +2 -0
- package/lib/preview.ts +715 -566
- package/lib/queue.ts +5 -5
- package/lib/routing.ts +644 -283
- package/lib/runtime.ts +260 -104
- package/lib/sections.ts +0 -2
- package/lib/telegram-api.ts +61 -8
- package/lib/text-groups.ts +75 -136
- package/lib/thread-display.ts +15 -4
- package/lib/threads.ts +130 -38
- package/lib/turns.ts +8 -1
- package/lib/updates.ts +58 -21
- package/package.json +1 -1
package/dist/lib/bus-leader.d.ts
CHANGED
|
@@ -63,6 +63,8 @@ export interface TelegramBusLeaderTargetProvisionerDeps<TContext> {
|
|
|
63
63
|
}
|
|
64
64
|
export interface TelegramBusFollowerTargetProvisionerDeps {
|
|
65
65
|
getAllowedUserId: () => number | undefined;
|
|
66
|
+
getLiveFollowers?: () => readonly TelegramBusFollowerView[];
|
|
67
|
+
isFollowerProcessAlive?: (pid: number) => boolean;
|
|
66
68
|
topicTargetStore: Threads.TelegramTopicTargetStore;
|
|
67
69
|
callApi: <TResponse>(method: string, body: Record<string, unknown>) => Promise<TResponse>;
|
|
68
70
|
getCurrentLeaderEpoch?: () => number | string | undefined;
|
|
@@ -276,6 +278,7 @@ export interface TelegramBusLeaderEnvelopeHandlerDeps {
|
|
|
276
278
|
recordRuntimeEvent?: (category: string, error: unknown, details?: Record<string, unknown>) => void;
|
|
277
279
|
applyThreadDisplayMode?: (mode: TelegramThreadDisplayMode, isCurrent: () => boolean) => Promise<void>;
|
|
278
280
|
getThreadDisplayMode?: () => TelegramThreadDisplayMode;
|
|
281
|
+
requiresPreDispatchRebindReader?: () => boolean;
|
|
279
282
|
getCurrentLeaderEpoch?: () => number | string | undefined;
|
|
280
283
|
getTelegramProfile?: () => string | undefined;
|
|
281
284
|
getAllowedUserId?: () => number | undefined;
|
package/dist/lib/bus-leader.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { isDeepStrictEqual } from "node:util";
|
|
8
8
|
import { getTelegramBusTransportRetryPolicy } from "./bus-transport.js";
|
|
9
|
-
import { createTelegramBusLocalServer, createUnauthorizedBusAck, rejectTelegramBusRequest, getTelegramBusEnvelopeTrafficClass, getTelegramBusFollowerSocketPath, getTelegramBusProtocolCompatibility, hasTelegramBusCapability, isTelegramBusEnvelopeAuthorized, parseTelegramBusEnvelope, sendTelegramBusLocalEnvelope, stripTelegramBusApiMetadata, TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT, TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION, TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF, TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY, TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT, TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE, TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE, TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME, } from "./bus.js";
|
|
9
|
+
import { createTelegramBusLocalServer, createUnauthorizedBusAck, rejectTelegramBusRequest, getTelegramBusEnvelopeTrafficClass, getTelegramBusFollowerSocketPath, getTelegramBusProtocolCompatibility, hasTelegramBusCapability, isTelegramBusEnvelopeAuthorized, parseTelegramBusEnvelope, sendTelegramBusLocalEnvelope, stripTelegramBusApiMetadata, TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT, TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION, TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF, TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY, TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT, TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE, TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND, TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE, TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME, } from "./bus.js";
|
|
10
10
|
import { escapeHtml } from "./rendering.js";
|
|
11
11
|
import * as Sync from "./sync.js";
|
|
12
12
|
import { parseTelegramIntegerId as asInteger, } from "./target.js";
|
|
@@ -223,12 +223,17 @@ export function createTelegramBusLeaderRuntimeAssembly(deps) {
|
|
|
223
223
|
});
|
|
224
224
|
});
|
|
225
225
|
};
|
|
226
|
+
/** Returns the stabilization this startup owns; a stop or a newer startup meanwhile makes it inert. */
|
|
226
227
|
const beginStartupDisplayStabilization = () => {
|
|
227
228
|
cancelStartupDisplayStabilization();
|
|
228
229
|
startupDisplaySettled = false;
|
|
230
|
+
return startupDisplayGeneration;
|
|
229
231
|
};
|
|
230
|
-
const armStartupDisplayReconciliation = () => {
|
|
231
|
-
|
|
232
|
+
const armStartupDisplayReconciliation = (generation) => {
|
|
233
|
+
if (startupDisplayGeneration !== generation)
|
|
234
|
+
return;
|
|
235
|
+
if (startupDisplayTimer)
|
|
236
|
+
clearTimeout(startupDisplayTimer);
|
|
232
237
|
const delayMs = deps.runtime.followerStaleAfterMs ?? TELEGRAM_BUS_FOLLOWER_STALE_AFTER_MS;
|
|
233
238
|
startupDisplayTimer = setTimeout(() => {
|
|
234
239
|
if (startupDisplayGeneration !== generation)
|
|
@@ -299,6 +304,8 @@ export function createTelegramBusLeaderRuntimeAssembly(deps) {
|
|
|
299
304
|
});
|
|
300
305
|
const provisionFollowerTarget = createTelegramBusFollowerTargetProvisioner({
|
|
301
306
|
...provisionerPorts,
|
|
307
|
+
getLiveFollowers: deps.runtime.followerRegistry.list,
|
|
308
|
+
isFollowerProcessAlive: deps.runtime.isFollowerProcessAlive,
|
|
302
309
|
});
|
|
303
310
|
const renameLeaderThreadAdmitted = async (threadName, expectedTarget, assertRecipientAuthority) => {
|
|
304
311
|
const store = deps.topicTargetStore, callApi = deps.callApi;
|
|
@@ -802,17 +809,19 @@ export function createTelegramBusLeaderRuntimeAssembly(deps) {
|
|
|
802
809
|
resetThreadNameAdmitted,
|
|
803
810
|
captureWorkspaceExternalProtection: deps.captureWorkspaceExternalProtection,
|
|
804
811
|
async startPolling(ctx) {
|
|
805
|
-
|
|
806
|
-
beginStartupDisplayStabilization()
|
|
812
|
+
const stabilization = display
|
|
813
|
+
? beginStartupDisplayStabilization()
|
|
814
|
+
: undefined;
|
|
807
815
|
try {
|
|
808
816
|
await runtime.startPolling(ctx);
|
|
809
817
|
}
|
|
810
818
|
catch (error) {
|
|
811
|
-
|
|
819
|
+
if (stabilization === startupDisplayGeneration)
|
|
820
|
+
cancelStartupDisplayStabilization();
|
|
812
821
|
throw error;
|
|
813
822
|
}
|
|
814
|
-
if (
|
|
815
|
-
armStartupDisplayReconciliation();
|
|
823
|
+
if (stabilization !== undefined)
|
|
824
|
+
armStartupDisplayReconciliation(stabilization);
|
|
816
825
|
},
|
|
817
826
|
async stopPolling() {
|
|
818
827
|
cancelStartupDisplayStabilization();
|
|
@@ -872,9 +881,40 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
|
|
|
872
881
|
if (typeof chatId !== "number")
|
|
873
882
|
return registration.target;
|
|
874
883
|
await deps.topicTargetStore.load();
|
|
884
|
+
let restoredPredecessor;
|
|
885
|
+
if (options?.existingWorkspaceBindingOnly && registration.cwd && registration.sessionId &&
|
|
886
|
+
!registration.previousInstanceId && deps.getLiveFollowers && deps.isFollowerProcessAlive) {
|
|
887
|
+
const identity = Threads.createTelegramWorkspaceBindingIdentity(registration.cwd, 0, registration.sessionId);
|
|
888
|
+
const records = deps.topicTargetStore.list(), liveFollowers = deps.getLiveFollowers();
|
|
889
|
+
const candidates = deps.topicTargetStore.listWorkspaceBindings()
|
|
890
|
+
.filter(binding => identity && binding.cwd === identity.cwd && binding.sessionId === identity.sessionId &&
|
|
891
|
+
(!registration.target || (binding.target.chatId === registration.target.chatId && binding.target.threadId === registration.target.threadId)))
|
|
892
|
+
.flatMap(binding => {
|
|
893
|
+
const matches = records.filter(record => record.status === "active" && record.owner?.kind === "manual-follower" &&
|
|
894
|
+
record.target.chatId === binding.target.chatId && record.target.threadId === binding.target.threadId);
|
|
895
|
+
return matches.length === 1 ? matches : [];
|
|
896
|
+
})
|
|
897
|
+
.filter(record => {
|
|
898
|
+
if (liveFollowers.some(peer => peer.instanceId === record.instanceId ||
|
|
899
|
+
(peer.target?.chatId === record.target.chatId && peer.target.threadId === record.target.threadId)))
|
|
900
|
+
return false;
|
|
901
|
+
// A retained status is not current execution proof. Only an absent actual runtime PID grants this claim handoff.
|
|
902
|
+
const match = /^([1-9][0-9]*):[0-9]+$/.exec(record.instanceId ?? ""), pid = match ? Number(match[1]) : undefined;
|
|
903
|
+
if (pid === undefined || !Number.isSafeInteger(pid))
|
|
904
|
+
return false;
|
|
905
|
+
try {
|
|
906
|
+
return deps.isFollowerProcessAlive(pid) === false;
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
if (candidates.length === 1)
|
|
913
|
+
restoredPredecessor = candidates[0];
|
|
914
|
+
}
|
|
875
915
|
let capacityUnavailable = false;
|
|
876
916
|
const workspaceIdentity = registration.cwd
|
|
877
|
-
? deps.topicTargetStore.claimWorkspaceIdentity(registration.cwd, registration.instanceId, registration.previousInstanceId, {
|
|
917
|
+
? deps.topicTargetStore.claimWorkspaceIdentity(registration.cwd, registration.instanceId, registration.previousInstanceId ?? restoredPredecessor?.instanceId, {
|
|
878
918
|
existingBindingOnly: options?.existingWorkspaceBindingOnly === true,
|
|
879
919
|
sessionId: registration.sessionId,
|
|
880
920
|
onCapacityUnavailable() {
|
|
@@ -925,6 +965,7 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
|
|
|
925
965
|
return (record.owner?.kind === "manual-follower" &&
|
|
926
966
|
((matchesCurrentIdentity && matchesRequestedTarget) ||
|
|
927
967
|
matchesSessionHandoff ||
|
|
968
|
+
(record.instanceId === restoredPredecessor?.instanceId && matchesRequestedTarget) ||
|
|
928
969
|
matchesWorkspaceRecovery));
|
|
929
970
|
});
|
|
930
971
|
const followerOwner = Threads.getTelegramThreadOwnerFromProfileKey(followerProfileKey);
|
|
@@ -1096,14 +1137,12 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
|
|
|
1096
1137
|
const requiresVisibilityProbe = crossSessionReuse ||
|
|
1097
1138
|
probeRequiredRecord ||
|
|
1098
1139
|
recoverableTarget !== undefined;
|
|
1140
|
+
const retainedDisplayBinding = deps.topicTargetStore.getWorkspaceBindingByTarget(result.target, registration.sessionId);
|
|
1099
1141
|
let connectedAnnouncement = !result.reused || requiresVisibilityProbe
|
|
1100
1142
|
? createTelegramBusInstanceLifecycleAnnouncement({
|
|
1101
1143
|
target: result.target,
|
|
1102
1144
|
threadName: result.displayTitle ??
|
|
1103
|
-
(
|
|
1104
|
-
workspaceBinding.target.threadId === result.target.threadId
|
|
1105
|
-
? workspaceBinding.displayTitle
|
|
1106
|
-
: undefined) ??
|
|
1145
|
+
(retainedDisplayBinding?.cwd === workspaceIdentity?.cwd ? retainedDisplayBinding?.displayTitle : undefined) ??
|
|
1107
1146
|
result.record.threadName,
|
|
1108
1147
|
slot: result.record.slot,
|
|
1109
1148
|
state: "connected",
|
|
@@ -1242,6 +1281,16 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
|
|
|
1242
1281
|
updatedAtMs: getNowMs(),
|
|
1243
1282
|
},
|
|
1244
1283
|
});
|
|
1284
|
+
// Same-target reuse can retain a different slot than the initial claim.
|
|
1285
|
+
if (result.record.slot !== workspaceCommit.slot) {
|
|
1286
|
+
result = {
|
|
1287
|
+
...result,
|
|
1288
|
+
record: deps.topicTargetStore.upsert({
|
|
1289
|
+
...result.record,
|
|
1290
|
+
slot: workspaceCommit.slot,
|
|
1291
|
+
}),
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1245
1294
|
if (connectedAnnouncement && workspaceCommit.displayTitle) {
|
|
1246
1295
|
connectedAnnouncement =
|
|
1247
1296
|
createTelegramBusInstanceLifecycleAnnouncement({
|
|
@@ -1712,8 +1761,7 @@ export function createTelegramBusSelectedMenuDeliveryHandler(deps) {
|
|
|
1712
1761
|
.listLiveRebindings()
|
|
1713
1762
|
.find((value) => value.request.operationId === expected.operationId);
|
|
1714
1763
|
if (!retained ||
|
|
1715
|
-
retained
|
|
1716
|
-
retained.cleanup ||
|
|
1764
|
+
!Threads.isTelegramLiveRebindInputAuthorized(retained) ||
|
|
1717
1765
|
retained.request.source.updateIds.length !== 1 ||
|
|
1718
1766
|
retained.recipient.kind !== "follower" ||
|
|
1719
1767
|
!isDeepStrictEqual(retained.executor, executor) ||
|
|
@@ -1792,8 +1840,13 @@ export function createTelegramBusSelectedMenuDeliveryHandler(deps) {
|
|
|
1792
1840
|
live.target.threadId !== recipient.target.threadId ||
|
|
1793
1841
|
resolveJournal.call(workspace, live) !== recipient.journalBindingKey)
|
|
1794
1842
|
throw new Error("Selected-menu authority changed.");
|
|
1843
|
+
const latest = store.listLiveRebindings().find(value => value.request.operationId === intent.request.operationId);
|
|
1844
|
+
if (!latest || !Threads.isTelegramLiveRebindInputAuthorized(latest) ||
|
|
1845
|
+
!isDeepStrictEqual({ ...latest, ...(intent.inputRelease ? { inputRelease: intent.inputRelease } : {}), revision: intent.revision, updatedAtMs: intent.updatedAtMs }, intent) ||
|
|
1846
|
+
(latest.inputRelease !== intent.inputRelease && !(intent.inputRelease === "issued" && latest.inputRelease === "accepted")))
|
|
1847
|
+
throw new Error("Selected-menu input grant changed.");
|
|
1795
1848
|
let confirmed = false;
|
|
1796
|
-
snapshot.call(threads,
|
|
1849
|
+
snapshot.call(threads, latest, (view) => {
|
|
1797
1850
|
const bindings = (view.workspaceBindings ?? []).filter((value) => value.bindingKey === intent.request.binding.bindingKey);
|
|
1798
1851
|
const owners = view.threads.filter((value) => value.status === "active" &&
|
|
1799
1852
|
(value.slot === intent.request.binding.slot ||
|
|
@@ -2198,6 +2251,8 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
|
|
|
2198
2251
|
local: deps.protocolIdentity,
|
|
2199
2252
|
remote: envelope.registration.protocol,
|
|
2200
2253
|
});
|
|
2254
|
+
const readerCompatible = () => deps.requiresPreDispatchRebindReader?.() !== true ||
|
|
2255
|
+
hasTelegramBusCapability(envelope.registration.protocol, TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND);
|
|
2201
2256
|
const displayCompatible = () => (deps.getThreadDisplayMode?.() ?? "names") === "names" ||
|
|
2202
2257
|
(hasTelegramBusCapability(deps.protocolIdentity, TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE) &&
|
|
2203
2258
|
hasTelegramBusCapability(envelope.registration.protocol, TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE));
|
|
@@ -2206,6 +2261,7 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
|
|
|
2206
2261
|
hasTelegramBusCapability(envelope.registration.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT);
|
|
2207
2262
|
if (!compatibility.compatible ||
|
|
2208
2263
|
!displayCompatible() ||
|
|
2264
|
+
!readerCompatible() ||
|
|
2209
2265
|
(envelope.registration.cwd !== undefined &&
|
|
2210
2266
|
envelope.registration.sessionId === undefined) ||
|
|
2211
2267
|
(restoringWorkspace && !supportsWorkspaceAutoConnect)) {
|
|
@@ -2236,8 +2292,9 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
|
|
|
2236
2292
|
const leaderEpoch = deps.getCurrentLeaderEpoch?.();
|
|
2237
2293
|
const telegramProfile = deps.getTelegramProfile?.();
|
|
2238
2294
|
const operatorUserId = deps.getAllowedUserId?.();
|
|
2239
|
-
const registrationCurrent = () => (
|
|
2240
|
-
deps.getCurrentLeaderEpoch
|
|
2295
|
+
const registrationCurrent = () => readerCompatible() &&
|
|
2296
|
+
(!deps.getCurrentLeaderEpoch ||
|
|
2297
|
+
deps.getCurrentLeaderEpoch() === leaderEpoch) &&
|
|
2241
2298
|
(!deps.getTelegramProfile ||
|
|
2242
2299
|
deps.getTelegramProfile() === telegramProfile) &&
|
|
2243
2300
|
(!deps.getAllowedUserId ||
|
|
@@ -2955,6 +3012,7 @@ export function createTelegramBusLeaderRuntime(deps) {
|
|
|
2955
3012
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
2956
3013
|
applyThreadDisplayMode: deps.applyThreadDisplayMode,
|
|
2957
3014
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
3015
|
+
requiresPreDispatchRebindReader: deps.requiresPreDispatchRebindReader,
|
|
2958
3016
|
getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
|
|
2959
3017
|
getTelegramProfile: deps.getTelegramProfile,
|
|
2960
3018
|
getAllowedUserId: deps.getAllowedUserId,
|
|
@@ -3027,24 +3085,47 @@ export function createTelegramBusLeaderRuntime(deps) {
|
|
|
3027
3085
|
},
|
|
3028
3086
|
handleEnvelope,
|
|
3029
3087
|
});
|
|
3088
|
+
// The start that owns the endpoint and pruning. A stop clears it; a start that loses it mid-await undoes only what
|
|
3089
|
+
// nobody newer owns, so a stop during startup never leaves a stopped leader's endpoint listening.
|
|
3090
|
+
let activeStart;
|
|
3091
|
+
const releaseSupersededStart = async () => {
|
|
3092
|
+
if (activeStart !== undefined)
|
|
3093
|
+
return;
|
|
3094
|
+
stopPruning();
|
|
3095
|
+
await localServer
|
|
3096
|
+
.stop()
|
|
3097
|
+
.catch((error) => deps.recordRuntimeEvent?.("bus", error, { phase: "stop" }));
|
|
3098
|
+
};
|
|
3030
3099
|
return {
|
|
3031
3100
|
routeQueueHandoff: (envelope) => routeQueueHandoffEnvelope(envelope),
|
|
3032
3101
|
startPolling: async (ctx) => {
|
|
3102
|
+
const start = {};
|
|
3103
|
+
activeStart = start;
|
|
3033
3104
|
// Replay durable cleanup before publishing the follower endpoint so a
|
|
3034
3105
|
// replacement registration cannot reclaim a target while it is deleted.
|
|
3035
3106
|
await deps.provisionLeaderTarget?.(ctx);
|
|
3107
|
+
if (activeStart !== start)
|
|
3108
|
+
return;
|
|
3036
3109
|
await localServer.start();
|
|
3110
|
+
if (activeStart !== start)
|
|
3111
|
+
return releaseSupersededStart();
|
|
3037
3112
|
startPruning();
|
|
3038
3113
|
try {
|
|
3039
3114
|
await deps.startPolling(ctx);
|
|
3040
3115
|
}
|
|
3041
3116
|
catch (error) {
|
|
3117
|
+
if (activeStart !== start)
|
|
3118
|
+
return releaseSupersededStart();
|
|
3119
|
+
activeStart = undefined;
|
|
3042
3120
|
stopPruning();
|
|
3043
3121
|
await localServer.stop();
|
|
3044
3122
|
throw error;
|
|
3045
3123
|
}
|
|
3124
|
+
if (activeStart !== start)
|
|
3125
|
+
return releaseSupersededStart();
|
|
3046
3126
|
},
|
|
3047
3127
|
stopPolling: async () => {
|
|
3128
|
+
activeStart = undefined;
|
|
3048
3129
|
stopPruning();
|
|
3049
3130
|
try {
|
|
3050
3131
|
await deps.stopPolling();
|
package/dist/lib/bus.d.ts
CHANGED
|
@@ -43,6 +43,9 @@ export declare const TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT: "director
|
|
|
43
43
|
export declare const TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT: "workspace-follower-auto-connect-v1";
|
|
44
44
|
export declare const TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT: "session-replacement-intent-v1";
|
|
45
45
|
export declare const TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE: "workspace-restore-v1";
|
|
46
|
+
/** Shared Workspace readers understand independent pre-dispatch input/cleanup state. */
|
|
47
|
+
export declare const TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND: "live-rebind-pre-dispatch-reader-v2";
|
|
48
|
+
export declare const TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND_EXECUTION: "live-rebind-pre-dispatch-v1";
|
|
46
49
|
export declare const TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE: "live-thread-rebind-save-v1";
|
|
47
50
|
export declare const TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY: "live-thread-rebind-apply-v1";
|
|
48
51
|
export declare const TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE: "live-thread-rebind-settle-v1";
|
|
@@ -239,7 +242,6 @@ export declare function getTelegramFollowerTargetOwnership(input: {
|
|
|
239
242
|
recipientBindingKey: string;
|
|
240
243
|
protocolIdentity: TelegramBusProtocolIdentity;
|
|
241
244
|
} | undefined;
|
|
242
|
-
export declare function markTelegramBusAggregateDelivery<T extends Record<string, unknown>>(body: T): T;
|
|
243
245
|
export declare function markTelegramBusCrossTargetDelivery<T extends Record<string, unknown>>(body: T): T;
|
|
244
246
|
export declare function stripTelegramBusApiMetadata<T extends Record<string, unknown>>(body: T): T;
|
|
245
247
|
export declare function isTelegramFollowerApiCallAllowed(input: {
|
|
@@ -460,7 +462,7 @@ export type TelegramBusEnvelope = ({
|
|
|
460
462
|
recipientBindingKey: string;
|
|
461
463
|
operationId: string;
|
|
462
464
|
sourceUpdateIds: number[];
|
|
463
|
-
mode: "release" | "discard" | "observe" | "observe-command";
|
|
465
|
+
mode: "release" | "discard" | "observe" | "observe-held" | "observe-command";
|
|
464
466
|
oldTarget?: TelegramTarget & {
|
|
465
467
|
threadId: number;
|
|
466
468
|
};
|
|
@@ -720,7 +722,7 @@ export declare function createTelegramBusLiveRebindController(deps: {
|
|
|
720
722
|
update_id: number;
|
|
721
723
|
} & Record<string, unknown>)[];
|
|
722
724
|
} | {
|
|
723
|
-
mode: "apply" | "inspect" | "release" | "discard" | "observe" | "observe-command";
|
|
725
|
+
mode: "apply" | "inspect" | "release" | "discard" | "observe" | "observe-held" | "observe-command";
|
|
724
726
|
sourceUpdateIds: number[];
|
|
725
727
|
slot: string;
|
|
726
728
|
target: TelegramTarget & {
|
package/dist/lib/bus.js
CHANGED
|
@@ -57,6 +57,9 @@ export const TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT = "directory-displ
|
|
|
57
57
|
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT = "workspace-follower-auto-connect-v1";
|
|
58
58
|
export const TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT = "session-replacement-intent-v1";
|
|
59
59
|
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE = "workspace-restore-v1";
|
|
60
|
+
/** Shared Workspace readers understand independent pre-dispatch input/cleanup state. */
|
|
61
|
+
export const TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND = "live-rebind-pre-dispatch-reader-v2";
|
|
62
|
+
export const TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND_EXECUTION = "live-rebind-pre-dispatch-v1";
|
|
60
63
|
export const TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SAVE = "live-thread-rebind-save-v1";
|
|
61
64
|
export const TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY = "live-thread-rebind-apply-v1";
|
|
62
65
|
export const TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE = "live-thread-rebind-settle-v1";
|
|
@@ -270,9 +273,6 @@ function hasTelegramBusDeliveryMarker(body, marker) {
|
|
|
270
273
|
!Array.isArray(body) &&
|
|
271
274
|
body[marker] === true);
|
|
272
275
|
}
|
|
273
|
-
export function markTelegramBusAggregateDelivery(body) {
|
|
274
|
-
return { ...body, [TELEGRAM_BUS_AGGREGATE_DELIVERY_FIELD]: true };
|
|
275
|
-
}
|
|
276
276
|
export function markTelegramBusCrossTargetDelivery(body) {
|
|
277
277
|
return { ...body, [TELEGRAM_BUS_CROSS_TARGET_DELIVERY_FIELD]: true };
|
|
278
278
|
}
|
|
@@ -375,11 +375,10 @@ export function isTelegramFollowerApiCallAllowed(input) {
|
|
|
375
375
|
if (apiMethod === "setMyCommands")
|
|
376
376
|
return isBotCommandRegistration(input.args[1]);
|
|
377
377
|
if (apiMethod === "sendChatAction")
|
|
378
|
-
return
|
|
378
|
+
return isTargetScoped(input.args[1]);
|
|
379
379
|
if (apiMethod === "sendMessage" &&
|
|
380
380
|
hasTelegramBusDeliveryMarker(input.args[1], TELEGRAM_BUS_AGGREGATE_DELIVERY_FIELD)) {
|
|
381
|
-
|
|
382
|
-
return body.message_thread_id === undefined && isTargetChatScoped(body);
|
|
381
|
+
return false;
|
|
383
382
|
}
|
|
384
383
|
if ((apiMethod === "sendMessage" || apiMethod === "sendRichMessage") &&
|
|
385
384
|
hasTelegramBusDeliveryMarker(input.args[1], TELEGRAM_BUS_CROSS_TARGET_DELIVERY_FIELD)) {
|
|
@@ -643,7 +642,8 @@ export function parseTelegramBusEnvelope(line) {
|
|
|
643
642
|
]) ||
|
|
644
643
|
(Object.hasOwn(value, "preparedSource") && !selectedCommand) ||
|
|
645
644
|
(value.mode === "observe-command" &&
|
|
646
|
-
(!selectedCommand || Object.hasOwn(value, "oldTarget")))
|
|
645
|
+
(!selectedCommand || Object.hasOwn(value, "oldTarget"))) ||
|
|
646
|
+
(value.mode === "observe-held" && (parseThreadTarget(value.oldTarget)?.threadId ?? 0) <= 0))
|
|
647
647
|
break;
|
|
648
648
|
if (Object.hasOwn(value, "selectedCommand") &&
|
|
649
649
|
(!selectedCommand ||
|
|
@@ -660,7 +660,7 @@ export function parseTelegramBusEnvelope(line) {
|
|
|
660
660
|
: value.mode === "release" ||
|
|
661
661
|
value.mode === "discard" ||
|
|
662
662
|
value.mode === "observe-command" ||
|
|
663
|
-
(value.mode === "observe" &&
|
|
663
|
+
((value.mode === "observe" || value.mode === "observe-held") &&
|
|
664
664
|
!!parseThreadTarget(value.oldTarget))) &&
|
|
665
665
|
isUniqueSourceUpdateIds(value.sourceUpdateIds) &&
|
|
666
666
|
typeof value.sentAtMs === "number" &&
|
|
@@ -672,7 +672,7 @@ export function parseTelegramBusEnvelope(line) {
|
|
|
672
672
|
sourceUpdateIds: value.sourceUpdateIds,
|
|
673
673
|
mode: value.mode,
|
|
674
674
|
sentAtMs: value.sentAtMs,
|
|
675
|
-
...(value.mode === "observe"
|
|
675
|
+
...((value.mode === "observe" || value.mode === "observe-held")
|
|
676
676
|
? { oldTarget: parseThreadTarget(value.oldTarget) }
|
|
677
677
|
: {}),
|
|
678
678
|
...(selectedCommand ? { selectedCommand, preparedSource } : {}),
|
|
@@ -1249,6 +1249,7 @@ export function createTelegramBusLiveRebindController(deps) {
|
|
|
1249
1249
|
"release",
|
|
1250
1250
|
"discard",
|
|
1251
1251
|
"observe",
|
|
1252
|
+
"observe-held",
|
|
1252
1253
|
"observe-command",
|
|
1253
1254
|
].includes(input.mode) ||
|
|
1254
1255
|
(input.mode === "observe-command" && !selectedCommand))))
|
|
@@ -1289,7 +1290,8 @@ export function createTelegramBusLiveRebindController(deps) {
|
|
|
1289
1290
|
}
|
|
1290
1291
|
: { updates: input.updates }),
|
|
1291
1292
|
});
|
|
1292
|
-
const observing = "mode" in expected && expected.mode === "observe";
|
|
1293
|
+
const observing = "mode" in expected && (expected.mode === "observe" || expected.mode === "observe-held");
|
|
1294
|
+
const observingHeld = "mode" in expected && expected.mode === "observe-held";
|
|
1293
1295
|
const commandObserving = "mode" in expected && expected.mode === "observe-command";
|
|
1294
1296
|
const settling = "mode" in expected &&
|
|
1295
1297
|
(expected.mode === "release" ||
|
|
@@ -1302,6 +1304,7 @@ export function createTelegramBusLiveRebindController(deps) {
|
|
|
1302
1304
|
? [TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY]
|
|
1303
1305
|
: []),
|
|
1304
1306
|
...(settling || held ? [TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE] : []),
|
|
1307
|
+
...(observingHeld ? [TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND_EXECUTION] : []),
|
|
1305
1308
|
...(held
|
|
1306
1309
|
? [
|
|
1307
1310
|
TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
|
package/dist/lib/delivery.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ export interface TelegramDeliveryTransportOptions {
|
|
|
76
76
|
/** @internal */
|
|
77
77
|
export interface TelegramDeliveryRuntimeDeps extends TelegramDeliveryTargetResolverDeps {
|
|
78
78
|
generation: string;
|
|
79
|
+
captureDrain?: () => (() => void) | undefined;
|
|
79
80
|
renderView: (view: TelegramDeliveryView) => readonly TelegramDeliveryRenderedChunk[];
|
|
80
81
|
sendChunk: (target: TelegramDeliveryTarget, chunk: TelegramDeliveryRenderedChunk, options: TelegramDeliveryTransportOptions) => Promise<number>;
|
|
81
82
|
editChunk: (target: TelegramDeliveryTarget, messageId: number, chunk: TelegramDeliveryRenderedChunk, options: TelegramDeliveryTransportOptions) => Promise<void>;
|
|
@@ -86,6 +87,7 @@ export interface TelegramDeliveryRuntimeDeps extends TelegramDeliveryTargetResol
|
|
|
86
87
|
/** @internal */
|
|
87
88
|
export interface TelegramBridgeDeliveryRuntimeDeps {
|
|
88
89
|
generation: string;
|
|
90
|
+
captureDrain?: () => (() => void) | undefined;
|
|
89
91
|
getTargetPolicyView: () => TelegramDeliveryTargetPolicyView;
|
|
90
92
|
getActiveTurnTarget: () => TelegramDeliveryTarget | undefined;
|
|
91
93
|
isTransportActive?: () => boolean;
|
|
@@ -114,6 +116,7 @@ export declare function createTelegramBridgeDeliveryLifecycleHooks<TTransportSta
|
|
|
114
116
|
export interface TelegramDeliveryTargetPolicyView {
|
|
115
117
|
canDeliver: boolean;
|
|
116
118
|
ownsDirect: boolean;
|
|
119
|
+
threaded?: boolean;
|
|
117
120
|
allowedChatId?: number;
|
|
118
121
|
followerTarget?: TelegramDeliveryTarget;
|
|
119
122
|
leaderTarget?: TelegramDeliveryTarget;
|
|
@@ -127,6 +130,7 @@ export interface TelegramDeliveryTargetPolicyRuntime {
|
|
|
127
130
|
/** @internal */
|
|
128
131
|
export declare function createTelegramDeliveryTargetPolicyRuntime(deps: {
|
|
129
132
|
ownsDirect(): boolean;
|
|
133
|
+
isThreadedMode?(): boolean;
|
|
130
134
|
isFollowerRegistered(): boolean;
|
|
131
135
|
getAllowedChatId(): number | undefined;
|
|
132
136
|
getFollowerTarget(): TelegramDeliveryTarget | undefined;
|
package/dist/lib/delivery.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Zones: telegram delivery, extension API, runtime binding
|
|
4
4
|
* Owns the public extension delivery contract, authorized scope resolution, operational rendering adapter, per-target serialization, chunk reconciliation, generation-fenced logical handles, and process-local runtime membrane; composes the established reply renderer with bus-aware Telegram API ports and excludes bot clients, Pi contexts, and consumer-extension policy
|
|
5
5
|
*/
|
|
6
|
-
import { markTelegramBusAggregateDelivery } from "./bus.js";
|
|
7
6
|
import { assertTelegramInlineKeyboardCallbackData, } from "./keyboard.js";
|
|
8
7
|
import { renderTelegramMessage, withTelegramReplyParameters, } from "./replies.js";
|
|
9
8
|
import { areTelegramTargetsEqual as areDeliveryTargetsEqual, getTelegramTargetThreadParams, } from "./target.js";
|
|
@@ -90,6 +89,7 @@ export function createTelegramDeliveryTargetPolicyRuntime(deps) {
|
|
|
90
89
|
return {
|
|
91
90
|
canDeliver: ownsDirect || deps.isFollowerRegistered(),
|
|
92
91
|
ownsDirect,
|
|
92
|
+
threaded: deps.isThreadedMode?.() === true,
|
|
93
93
|
allowedChatId: deps.getAllowedChatId(),
|
|
94
94
|
followerTarget: deps.getFollowerTarget(),
|
|
95
95
|
leaderTarget: deps.getLeaderTarget(),
|
|
@@ -103,19 +103,20 @@ export function createTelegramDeliveryTargetPolicyRuntime(deps) {
|
|
|
103
103
|
},
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
|
+
function isThreadedDelivery(view) {
|
|
107
|
+
return view.threaded === true || view.followerTarget?.threadId !== undefined || view.leaderTarget?.threadId !== undefined;
|
|
108
|
+
}
|
|
106
109
|
/** @internal */
|
|
107
110
|
export function resolveTelegramDeliveryInstanceTarget(view) {
|
|
108
111
|
if (!view.canDeliver)
|
|
109
112
|
return undefined;
|
|
110
|
-
|
|
111
|
-
view.
|
|
112
|
-
|
|
113
|
-
? undefined
|
|
114
|
-
: { chatId: view.allowedChatId }));
|
|
113
|
+
const target = view.followerTarget ?? view.leaderTarget ??
|
|
114
|
+
(view.allowedChatId === undefined ? undefined : { chatId: view.allowedChatId });
|
|
115
|
+
return target && (!isThreadedDelivery(view) || target.threadId !== undefined) ? target : undefined;
|
|
115
116
|
}
|
|
116
117
|
/** @internal */
|
|
117
118
|
export function resolveTelegramDeliveryAggregateTarget(view) {
|
|
118
|
-
return !view.canDeliver || view.allowedChatId === undefined
|
|
119
|
+
return !view.canDeliver || view.allowedChatId === undefined || isThreadedDelivery(view)
|
|
119
120
|
? undefined
|
|
120
121
|
: { chatId: view.allowedChatId };
|
|
121
122
|
}
|
|
@@ -127,7 +128,7 @@ export function isTelegramDeliveryExplicitTargetAuthorized(candidate, view) {
|
|
|
127
128
|
return false;
|
|
128
129
|
}
|
|
129
130
|
if (candidate.threadId === undefined)
|
|
130
|
-
return
|
|
131
|
+
return !isThreadedDelivery(view);
|
|
131
132
|
if (view.followerTarget) {
|
|
132
133
|
return areDeliveryTargetsEqual(candidate, view.followerTarget);
|
|
133
134
|
}
|
|
@@ -166,10 +167,28 @@ function resolveTelegramDeliveryTarget(scope, deps) {
|
|
|
166
167
|
}
|
|
167
168
|
return { ok: true, value: cloneTarget(target) };
|
|
168
169
|
}
|
|
169
|
-
|
|
170
|
+
/** Observation cannot turn failed delivery into success or borrow a newer queue lifetime. */
|
|
171
|
+
function captureDeliveryDrainObserver(capture) {
|
|
172
|
+
try {
|
|
173
|
+
const notify = capture?.();
|
|
174
|
+
return () => {
|
|
175
|
+
try {
|
|
176
|
+
const emitted = notify?.();
|
|
177
|
+
if (emitted)
|
|
178
|
+
void Promise.resolve(emitted).catch(() => undefined);
|
|
179
|
+
}
|
|
180
|
+
catch { /* Observer failure is inert. */ }
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return () => { };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function createTelegramDeliveryTargetQueue(captureDrain) {
|
|
170
188
|
const queues = new Map();
|
|
171
189
|
async function run(target, operation) {
|
|
172
190
|
const key = targetKey(target);
|
|
191
|
+
const notifyDrained = captureDeliveryDrainObserver(captureDrain);
|
|
173
192
|
const previous = queues.get(key) ?? Promise.resolve();
|
|
174
193
|
const current = previous.then(operation, operation);
|
|
175
194
|
const settled = current.then(() => { }, () => { });
|
|
@@ -178,8 +197,10 @@ function createTelegramDeliveryTargetQueue() {
|
|
|
178
197
|
return await current;
|
|
179
198
|
}
|
|
180
199
|
finally {
|
|
181
|
-
if (queues.get(key) === settled)
|
|
200
|
+
if (queues.get(key) === settled) {
|
|
182
201
|
queues.delete(key);
|
|
202
|
+
notifyDrained();
|
|
203
|
+
}
|
|
183
204
|
}
|
|
184
205
|
}
|
|
185
206
|
return {
|
|
@@ -203,7 +224,10 @@ function getChunkTransportOptions(view, index, chunkCount, replyToMessageId, edi
|
|
|
203
224
|
export function createTelegramDeliveryRuntime(deps) {
|
|
204
225
|
let active = true;
|
|
205
226
|
const handleBindings = new WeakMap();
|
|
206
|
-
const targetQueue = createTelegramDeliveryTargetQueue()
|
|
227
|
+
const targetQueue = createTelegramDeliveryTargetQueue(() => {
|
|
228
|
+
const notify = deps.captureDrain?.();
|
|
229
|
+
return () => active ? notify?.() : undefined;
|
|
230
|
+
});
|
|
207
231
|
const runForTarget = targetQueue.run;
|
|
208
232
|
const render = (view) => {
|
|
209
233
|
const chunks = deps.renderView(view);
|
|
@@ -398,9 +422,17 @@ export function createTelegramBridgeDeliveryRuntime(deps) {
|
|
|
398
422
|
throw new TelegramDeliveryTransportGenerationError();
|
|
399
423
|
}
|
|
400
424
|
};
|
|
425
|
+
const assertOutputTarget = (target) => {
|
|
426
|
+
if (isThreadedDelivery(getPolicyView()) && target.threadId === undefined)
|
|
427
|
+
throw new Error("Telegram All is input-only in Threaded Mode.");
|
|
428
|
+
};
|
|
401
429
|
return createTelegramDeliveryRuntime({
|
|
402
430
|
generation: deps.generation,
|
|
403
|
-
|
|
431
|
+
captureDrain: deps.captureDrain,
|
|
432
|
+
getActiveTurnTarget() {
|
|
433
|
+
const target = deps.getActiveTurnTarget();
|
|
434
|
+
return target && (!isThreadedDelivery(getPolicyView()) || target.threadId !== undefined) ? target : undefined;
|
|
435
|
+
},
|
|
404
436
|
getInstanceTarget() {
|
|
405
437
|
return resolveTelegramDeliveryInstanceTarget(getPolicyView());
|
|
406
438
|
},
|
|
@@ -423,6 +455,7 @@ export function createTelegramBridgeDeliveryRuntime(deps) {
|
|
|
423
455
|
},
|
|
424
456
|
async sendChunk(target, chunk, options) {
|
|
425
457
|
assertTransportActive();
|
|
458
|
+
assertOutputTarget(target);
|
|
426
459
|
const sent = await withTelegramReplyParameters(target.chatId, options.replyToMessageId, target, (replyParameters) => {
|
|
427
460
|
const body = {
|
|
428
461
|
chat_id: target.chatId,
|
|
@@ -436,9 +469,7 @@ export function createTelegramBridgeDeliveryRuntime(deps) {
|
|
|
436
469
|
? { reply_markup: options.replyMarkup }
|
|
437
470
|
: {}),
|
|
438
471
|
};
|
|
439
|
-
return deps.api.sendMessage(
|
|
440
|
-
? markTelegramBusAggregateDelivery(body)
|
|
441
|
-
: body);
|
|
472
|
+
return deps.api.sendMessage(body);
|
|
442
473
|
});
|
|
443
474
|
assertTransportActive();
|
|
444
475
|
deps.recordOwnership({
|
|
@@ -450,6 +481,7 @@ export function createTelegramBridgeDeliveryRuntime(deps) {
|
|
|
450
481
|
},
|
|
451
482
|
async editChunk(target, messageId, chunk, options) {
|
|
452
483
|
assertTransportActive();
|
|
484
|
+
assertOutputTarget(target);
|
|
453
485
|
await deps.api.editMessageText({
|
|
454
486
|
chat_id: target.chatId,
|
|
455
487
|
message_id: messageId,
|
|
@@ -466,6 +498,7 @@ export function createTelegramBridgeDeliveryRuntime(deps) {
|
|
|
466
498
|
},
|
|
467
499
|
async sendChatAction(target, action) {
|
|
468
500
|
assertTransportActive();
|
|
501
|
+
assertOutputTarget(target);
|
|
469
502
|
await deps.api.sendChatAction(target.chatId, action, {
|
|
470
503
|
message_thread_id: target.threadId,
|
|
471
504
|
});
|