@llblab/pi-telegram 0.27.11 → 0.28.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 +150 -258
- package/BACKLOG.md +1 -169
- package/CHANGELOG.md +396 -441
- package/README.md +9 -6
- package/api/updates.ts +5 -0
- package/docs/architecture.md +71 -26
- package/docs/multi-instance-bus.md +23 -5
- package/docs/public-api.md +7 -6
- package/docs/ui-style.md +6 -6
- package/docs/updates.md +29 -11
- package/index.ts +356 -246
- package/lib/activity-verbosity.ts +26 -0
- package/lib/bindings.ts +240 -2
- package/lib/bus-follower.ts +436 -238
- package/lib/bus-leader.ts +395 -42
- package/lib/bus.ts +994 -153
- package/lib/commands.ts +184 -30
- package/lib/config.ts +23 -2
- package/lib/journal.ts +3140 -0
- package/lib/lifecycle.ts +4 -0
- package/lib/locks.ts +21 -21
- package/lib/media.ts +71 -32
- package/lib/menu-queue.ts +31 -17
- package/lib/menu.ts +5 -3
- package/lib/model.ts +51 -24
- package/lib/ownership.ts +42 -7
- package/lib/paths.ts +35 -0
- package/lib/polling.ts +591 -106
- package/lib/prompts.ts +17 -0
- package/lib/queue.ts +732 -143
- package/lib/routing.ts +291 -64
- package/lib/runtime.ts +26 -10
- package/lib/status.ts +257 -18
- package/lib/sync.ts +131 -5
- package/lib/telegram-api.ts +41 -11
- package/lib/text-groups.ts +75 -35
- package/lib/threads.ts +112 -4
- package/lib/turns.ts +79 -14
- package/lib/updates.ts +3771 -223
- package/package.json +3 -3
- package/scripts/check-downgrade.mjs +435 -0
package/lib/routing.ts
CHANGED
|
@@ -515,6 +515,8 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
515
515
|
body: Record<string, unknown>,
|
|
516
516
|
) => Promise<TResponse>;
|
|
517
517
|
getCurrentInstanceId?: () => string | undefined;
|
|
518
|
+
getAdmissionScope?: () => string | undefined;
|
|
519
|
+
getAdmissionJournalBinding?: () => string | undefined;
|
|
518
520
|
getMessageOwnership?: Updates.TelegramMessageOwnershipLookup;
|
|
519
521
|
getTargetOwnership?: Updates.TelegramTargetOwnershipLookup;
|
|
520
522
|
recordMessageOwnership?: Updates.TelegramMessageOwnershipRecorder;
|
|
@@ -591,6 +593,7 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
591
593
|
inboundHandlerRuntime: TelegramInboundHandlerRuntime<TContext>;
|
|
592
594
|
threadStore?: Threads.TelegramTopicTargetStore;
|
|
593
595
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
596
|
+
isContextActive?: (ctx: TContext) => boolean;
|
|
594
597
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
595
598
|
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
596
599
|
dispatch: (ctx: TContext) => void,
|
|
@@ -743,6 +746,32 @@ export function createTelegramInboundRouteRuntime<
|
|
|
743
746
|
);
|
|
744
747
|
}
|
|
745
748
|
};
|
|
749
|
+
const createAdmissionReceipts = (
|
|
750
|
+
queueKind: Queue.TelegramQueueItemKind,
|
|
751
|
+
sources: readonly unknown[],
|
|
752
|
+
): Queue.TelegramQueueAdmissionReceipt[] => {
|
|
753
|
+
const sourceUpdateIds =
|
|
754
|
+
Updates.collectTelegramAdmissionSourceUpdateIds(sources);
|
|
755
|
+
if (sourceUpdateIds.length === 0) return [];
|
|
756
|
+
const receipt = Queue.createTelegramQueueAdmissionReceipt({
|
|
757
|
+
queueKind,
|
|
758
|
+
scope: deps.getAdmissionScope?.() ?? "",
|
|
759
|
+
sourceUpdateIds,
|
|
760
|
+
});
|
|
761
|
+
const journalBindingKey = deps.getAdmissionJournalBinding?.();
|
|
762
|
+
return receipt
|
|
763
|
+
? [{
|
|
764
|
+
...receipt,
|
|
765
|
+
...(journalBindingKey ? { journalBindingKey } : {}),
|
|
766
|
+
}]
|
|
767
|
+
: [];
|
|
768
|
+
};
|
|
769
|
+
const reportQueueAdmission = (
|
|
770
|
+
sources: readonly unknown[],
|
|
771
|
+
receipts: readonly Queue.TelegramQueueAdmissionReceipt[],
|
|
772
|
+
): void => {
|
|
773
|
+
Updates.reportTelegramQueueAdmission(sources, receipts);
|
|
774
|
+
};
|
|
746
775
|
const prunePendingUnboundReroutes = () => {
|
|
747
776
|
const nowMs = Date.now();
|
|
748
777
|
for (const [id, entry] of pendingUnboundReroutes) {
|
|
@@ -811,10 +840,15 @@ export function createTelegramInboundRouteRuntime<
|
|
|
811
840
|
prompt: string,
|
|
812
841
|
ctx: TContext,
|
|
813
842
|
target?: Queue.TelegramQueueTarget,
|
|
843
|
+
source?: unknown,
|
|
814
844
|
) => {
|
|
815
845
|
const chatId = target?.chatId ?? deps.configStore.getAllowedUserId();
|
|
816
846
|
if (typeof chatId !== "number") return;
|
|
817
847
|
const order = deps.bridgeRuntime.queue.allocateItemOrder();
|
|
848
|
+
const admissionReceipts = createAdmissionReceipts(
|
|
849
|
+
"prompt",
|
|
850
|
+
source === undefined ? [] : [source],
|
|
851
|
+
);
|
|
818
852
|
const turn: Queue.PendingTelegramTurn = {
|
|
819
853
|
kind: "prompt",
|
|
820
854
|
chatId,
|
|
@@ -833,8 +867,13 @@ export function createTelegramInboundRouteRuntime<
|
|
|
833
867
|
],
|
|
834
868
|
historyText: Turns.truncateTelegramQueueSummary(prompt),
|
|
835
869
|
statusSummary: Turns.truncateTelegramQueueSummary(prompt),
|
|
870
|
+
...(admissionReceipts.length > 0 ? { admissionReceipts } : {}),
|
|
836
871
|
};
|
|
837
872
|
deps.queueMutationRuntime.append(turn, ctx);
|
|
873
|
+
reportQueueAdmission(
|
|
874
|
+
source === undefined ? [] : [source],
|
|
875
|
+
admissionReceipts,
|
|
876
|
+
);
|
|
838
877
|
deps.updateStatus(ctx);
|
|
839
878
|
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
840
879
|
},
|
|
@@ -845,17 +884,22 @@ export function createTelegramInboundRouteRuntime<
|
|
|
845
884
|
): TMessage[] => {
|
|
846
885
|
return messages.map(
|
|
847
886
|
(message) =>
|
|
848
|
-
(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
887
|
+
Updates.carryTelegramUpdateExecutionFence(
|
|
888
|
+
message,
|
|
889
|
+
{
|
|
890
|
+
...message,
|
|
891
|
+
message_id: 0,
|
|
892
|
+
message_thread_id: threadId,
|
|
893
|
+
reply_to_message: undefined,
|
|
894
|
+
} as TMessage,
|
|
895
|
+
),
|
|
854
896
|
);
|
|
855
897
|
};
|
|
856
898
|
const applyThreadCleanupPlan = async (
|
|
857
899
|
plan: ThreadReconciler.ThreadReconciliationPlan,
|
|
900
|
+
assertExecutionCurrent?: () => void,
|
|
858
901
|
): Promise<boolean> => {
|
|
902
|
+
assertExecutionCurrent?.();
|
|
859
903
|
deps.recordThreadReconciliationPlan?.(plan);
|
|
860
904
|
const result = await ThreadReconciler.applyThreadReconciliationPlan(plan, {
|
|
861
905
|
callApi: deps.callApi,
|
|
@@ -871,10 +915,12 @@ export function createTelegramInboundRouteRuntime<
|
|
|
871
915
|
getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
|
|
872
916
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
873
917
|
});
|
|
918
|
+
assertExecutionCurrent?.();
|
|
874
919
|
return (result.incompleteActions?.length ?? 0) === 0;
|
|
875
920
|
};
|
|
876
921
|
const dismissRerouteChooserMessage = async (
|
|
877
922
|
query: TCallbackQuery,
|
|
923
|
+
assertExecutionCurrent?: () => void,
|
|
878
924
|
): Promise<boolean> => {
|
|
879
925
|
const chatId = query.message?.chat?.id;
|
|
880
926
|
const messageId = query.message?.message_id;
|
|
@@ -886,7 +932,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
886
932
|
return false;
|
|
887
933
|
}
|
|
888
934
|
try {
|
|
935
|
+
assertExecutionCurrent?.();
|
|
889
936
|
await deps.deleteMessage(chatId, messageId);
|
|
937
|
+
assertExecutionCurrent?.();
|
|
890
938
|
return true;
|
|
891
939
|
} catch (error) {
|
|
892
940
|
deps.recordRuntimeEvent?.("telegram", error, {
|
|
@@ -901,6 +949,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
901
949
|
const closeReroutedUnboundTopic = async (
|
|
902
950
|
target: { chatId: number; threadId: number } | undefined,
|
|
903
951
|
messageId: number | undefined,
|
|
952
|
+
assertExecutionCurrent?: () => void,
|
|
904
953
|
): Promise<boolean> => {
|
|
905
954
|
if (!target || !deps.threadStore) return true;
|
|
906
955
|
const nowMs = Date.now();
|
|
@@ -923,10 +972,11 @@ export function createTelegramInboundRouteRuntime<
|
|
|
923
972
|
},
|
|
924
973
|
],
|
|
925
974
|
});
|
|
926
|
-
return applyThreadCleanupPlan(plan);
|
|
975
|
+
return applyThreadCleanupPlan(plan, assertExecutionCurrent);
|
|
927
976
|
};
|
|
928
977
|
const closePreviousLeaderThread = async (
|
|
929
978
|
target: { chatId: number; threadId: number } | undefined,
|
|
979
|
+
assertExecutionCurrent?: () => void,
|
|
930
980
|
): Promise<boolean> => {
|
|
931
981
|
if (!target || !deps.threadStore) return true;
|
|
932
982
|
const currentLeaderEpoch = deps.getCurrentLeaderEpoch?.();
|
|
@@ -942,11 +992,12 @@ export function createTelegramInboundRouteRuntime<
|
|
|
942
992
|
: {}),
|
|
943
993
|
},
|
|
944
994
|
],
|
|
945
|
-
});
|
|
995
|
+
}, assertExecutionCurrent);
|
|
946
996
|
};
|
|
947
997
|
const closeReplacedFollowerThread = async (
|
|
948
998
|
target: { chatId: number; threadId: number } | undefined,
|
|
949
999
|
instanceId: string | undefined,
|
|
1000
|
+
assertExecutionCurrent?: () => void,
|
|
950
1001
|
): Promise<boolean> => {
|
|
951
1002
|
if (!target || !deps.threadStore) return true;
|
|
952
1003
|
const currentLeaderEpoch = deps.getCurrentLeaderEpoch?.();
|
|
@@ -962,18 +1013,27 @@ export function createTelegramInboundRouteRuntime<
|
|
|
962
1013
|
: {}),
|
|
963
1014
|
},
|
|
964
1015
|
],
|
|
965
|
-
});
|
|
1016
|
+
}, assertExecutionCurrent);
|
|
966
1017
|
};
|
|
967
1018
|
const retryPendingRerouteCleanup = async (
|
|
968
1019
|
cleanup: PendingRerouteCleanup,
|
|
1020
|
+
assertExecutionCurrent?: () => void,
|
|
969
1021
|
): Promise<boolean> => {
|
|
970
1022
|
if (cleanup.kind === "unbound") {
|
|
971
|
-
return closeReroutedUnboundTopic(
|
|
1023
|
+
return closeReroutedUnboundTopic(
|
|
1024
|
+
cleanup.target,
|
|
1025
|
+
cleanup.messageId,
|
|
1026
|
+
assertExecutionCurrent,
|
|
1027
|
+
);
|
|
972
1028
|
}
|
|
973
1029
|
if (cleanup.kind === "previous-leader") {
|
|
974
|
-
return closePreviousLeaderThread(cleanup.target);
|
|
1030
|
+
return closePreviousLeaderThread(cleanup.target, assertExecutionCurrent);
|
|
975
1031
|
}
|
|
976
|
-
return closeReplacedFollowerThread(
|
|
1032
|
+
return closeReplacedFollowerThread(
|
|
1033
|
+
cleanup.target,
|
|
1034
|
+
cleanup.instanceId,
|
|
1035
|
+
assertExecutionCurrent,
|
|
1036
|
+
);
|
|
977
1037
|
};
|
|
978
1038
|
let dispatchReroutedCommandMessages:
|
|
979
1039
|
| ((messages: TMessage[], ctx: TContext) => Promise<void>)
|
|
@@ -994,8 +1054,12 @@ export function createTelegramInboundRouteRuntime<
|
|
|
994
1054
|
pending: PendingUnboundReroute,
|
|
995
1055
|
query: TCallbackQuery,
|
|
996
1056
|
successMessage: string,
|
|
1057
|
+
assertExecutionCurrent?: () => void,
|
|
997
1058
|
): Promise<void> => {
|
|
998
|
-
const dismissed = await dismissRerouteChooserMessage(
|
|
1059
|
+
const dismissed = await dismissRerouteChooserMessage(
|
|
1060
|
+
query,
|
|
1061
|
+
assertExecutionCurrent,
|
|
1062
|
+
);
|
|
999
1063
|
if (dismissed) {
|
|
1000
1064
|
pendingUnboundReroutes.delete(rerouteId);
|
|
1001
1065
|
await deps.answerCallbackQuery(query.id, successMessage);
|
|
@@ -1012,7 +1076,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1012
1076
|
instanceId: string,
|
|
1013
1077
|
threadId: number,
|
|
1014
1078
|
ctx: TContext,
|
|
1079
|
+
assertExecutionCurrent?: () => void,
|
|
1015
1080
|
): Promise<boolean> => {
|
|
1081
|
+
assertExecutionCurrent?.();
|
|
1016
1082
|
const forwardMessage = deps.foreignOwnedUpdateForwarder?.forwardMessage;
|
|
1017
1083
|
if (!forwardMessage) return false;
|
|
1018
1084
|
const messages = cloneTelegramMessagesForThread(pending.messages, threadId);
|
|
@@ -1025,9 +1091,15 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1025
1091
|
}),
|
|
1026
1092
|
),
|
|
1027
1093
|
);
|
|
1094
|
+
assertExecutionCurrent?.();
|
|
1028
1095
|
pending.messages = pending.messages.filter((_, index) => {
|
|
1029
1096
|
const outcome = outcomes[index];
|
|
1030
|
-
if (
|
|
1097
|
+
if (
|
|
1098
|
+
outcome?.status === "fulfilled" &&
|
|
1099
|
+
outcome.value.status === "accepted"
|
|
1100
|
+
) {
|
|
1101
|
+
return false;
|
|
1102
|
+
}
|
|
1031
1103
|
if (outcome?.status === "rejected") {
|
|
1032
1104
|
deps.recordRuntimeEvent?.("bus", outcome.reason, {
|
|
1033
1105
|
phase: "reroute-foreign-forward",
|
|
@@ -1100,6 +1172,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1100
1172
|
): Promise<boolean> => {
|
|
1101
1173
|
const parsed = parseTelegramUnboundRerouteCallbackData(query.data);
|
|
1102
1174
|
if (!parsed) return false;
|
|
1175
|
+
const assertExecutionCurrent =
|
|
1176
|
+
Updates.createTelegramUpdateExecutionFenceGuard(query);
|
|
1177
|
+
assertExecutionCurrent();
|
|
1103
1178
|
const chatId = query.message?.chat?.id;
|
|
1104
1179
|
const pending = pendingUnboundReroutes.get(parsed.rerouteId);
|
|
1105
1180
|
if (typeof chatId !== "number" || !deps.threadStore || !pending) {
|
|
@@ -1107,12 +1182,14 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1107
1182
|
return true;
|
|
1108
1183
|
}
|
|
1109
1184
|
await deps.threadStore.load();
|
|
1185
|
+
assertExecutionCurrent();
|
|
1110
1186
|
if (pending.finalizeMessage) {
|
|
1111
1187
|
await finalizePendingReroute(
|
|
1112
1188
|
parsed.rerouteId,
|
|
1113
1189
|
pending,
|
|
1114
1190
|
query,
|
|
1115
1191
|
pending.finalizeMessage,
|
|
1192
|
+
assertExecutionCurrent,
|
|
1116
1193
|
);
|
|
1117
1194
|
return true;
|
|
1118
1195
|
}
|
|
@@ -1123,6 +1200,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1123
1200
|
retry.instanceId,
|
|
1124
1201
|
retry.threadId,
|
|
1125
1202
|
ctx,
|
|
1203
|
+
assertExecutionCurrent,
|
|
1126
1204
|
);
|
|
1127
1205
|
if (!allForwarded) {
|
|
1128
1206
|
await deps.answerCallbackQuery(
|
|
@@ -1135,7 +1213,10 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1135
1213
|
if (retry.cleanup) pending.cleanup = retry.cleanup;
|
|
1136
1214
|
}
|
|
1137
1215
|
if (pending.cleanup) {
|
|
1138
|
-
const cleanupComplete = await retryPendingRerouteCleanup(
|
|
1216
|
+
const cleanupComplete = await retryPendingRerouteCleanup(
|
|
1217
|
+
pending.cleanup,
|
|
1218
|
+
assertExecutionCurrent,
|
|
1219
|
+
);
|
|
1139
1220
|
if (!cleanupComplete) {
|
|
1140
1221
|
await deps.answerCallbackQuery(
|
|
1141
1222
|
query.id,
|
|
@@ -1149,6 +1230,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1149
1230
|
pending,
|
|
1150
1231
|
query,
|
|
1151
1232
|
"Thread cleanup completed.",
|
|
1233
|
+
assertExecutionCurrent,
|
|
1152
1234
|
);
|
|
1153
1235
|
return true;
|
|
1154
1236
|
}
|
|
@@ -1192,11 +1274,13 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1192
1274
|
);
|
|
1193
1275
|
return true;
|
|
1194
1276
|
}
|
|
1277
|
+
assertExecutionCurrent();
|
|
1195
1278
|
const replaced = await deps.replaceFollowerThreadTarget({
|
|
1196
1279
|
record,
|
|
1197
1280
|
target: sourceTarget,
|
|
1198
1281
|
oldTarget: record.target,
|
|
1199
1282
|
});
|
|
1283
|
+
assertExecutionCurrent();
|
|
1200
1284
|
if (!replaced) {
|
|
1201
1285
|
await deps.answerCallbackQuery(
|
|
1202
1286
|
query.id,
|
|
@@ -1207,6 +1291,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1207
1291
|
const nowMs = Date.now();
|
|
1208
1292
|
const slot = record.slot ?? "?";
|
|
1209
1293
|
const threadName = getRestoredThreadName(record, slot);
|
|
1294
|
+
assertExecutionCurrent();
|
|
1210
1295
|
deps.threadStore.markStaleByTarget(
|
|
1211
1296
|
record.target,
|
|
1212
1297
|
"deleted",
|
|
@@ -1224,13 +1309,16 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1224
1309
|
rerouteConfirmedAtMs: nowMs,
|
|
1225
1310
|
});
|
|
1226
1311
|
await deps.threadStore.persist();
|
|
1312
|
+
assertExecutionCurrent();
|
|
1227
1313
|
if (deps.callApi) {
|
|
1228
1314
|
try {
|
|
1315
|
+
assertExecutionCurrent();
|
|
1229
1316
|
await deps.callApi("editForumTopic", {
|
|
1230
1317
|
chat_id: sourceTarget.chatId,
|
|
1231
1318
|
message_thread_id: sourceTarget.threadId,
|
|
1232
1319
|
name: Threads.getTelegramTopicTitleForThreadName(threadName, slot),
|
|
1233
1320
|
});
|
|
1321
|
+
assertExecutionCurrent();
|
|
1234
1322
|
} catch (renameError) {
|
|
1235
1323
|
deps.recordRuntimeEvent?.("telegram", renameError, {
|
|
1236
1324
|
phase: "follower-topic-reroute-restore-rename",
|
|
@@ -1250,6 +1338,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1250
1338
|
record.instanceId!,
|
|
1251
1339
|
sourceTarget.threadId,
|
|
1252
1340
|
ctx,
|
|
1341
|
+
assertExecutionCurrent,
|
|
1253
1342
|
);
|
|
1254
1343
|
if (!allForwarded) {
|
|
1255
1344
|
pending.foreignRetry = {
|
|
@@ -1263,7 +1352,10 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1263
1352
|
);
|
|
1264
1353
|
return true;
|
|
1265
1354
|
}
|
|
1266
|
-
const cleanupComplete = await retryPendingRerouteCleanup(
|
|
1355
|
+
const cleanupComplete = await retryPendingRerouteCleanup(
|
|
1356
|
+
cleanup,
|
|
1357
|
+
assertExecutionCurrent,
|
|
1358
|
+
);
|
|
1267
1359
|
if (!cleanupComplete) {
|
|
1268
1360
|
pending.cleanup = cleanup;
|
|
1269
1361
|
await deps.answerCallbackQuery(
|
|
@@ -1297,6 +1389,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1297
1389
|
record.instanceId,
|
|
1298
1390
|
parsed.threadId,
|
|
1299
1391
|
ctx,
|
|
1392
|
+
assertExecutionCurrent,
|
|
1300
1393
|
);
|
|
1301
1394
|
if (!allForwarded) {
|
|
1302
1395
|
await deps.answerCallbackQuery(
|
|
@@ -1308,6 +1401,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1308
1401
|
const cleanupComplete = await closeReroutedUnboundTopic(
|
|
1309
1402
|
sourceTarget,
|
|
1310
1403
|
sourceMessageId,
|
|
1404
|
+
assertExecutionCurrent,
|
|
1311
1405
|
);
|
|
1312
1406
|
if (!cleanupComplete && sourceTarget) {
|
|
1313
1407
|
pending.cleanup = {
|
|
@@ -1371,11 +1465,13 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1371
1465
|
});
|
|
1372
1466
|
if (deps.callApi) {
|
|
1373
1467
|
try {
|
|
1468
|
+
assertExecutionCurrent();
|
|
1374
1469
|
await deps.callApi("editForumTopic", {
|
|
1375
1470
|
chat_id: sourceTarget.chatId,
|
|
1376
1471
|
message_thread_id: sourceTarget.threadId,
|
|
1377
1472
|
name: Threads.getTelegramTopicTitleForThreadName(threadName, slot),
|
|
1378
1473
|
});
|
|
1474
|
+
assertExecutionCurrent();
|
|
1379
1475
|
} catch (renameError) {
|
|
1380
1476
|
deps.recordRuntimeEvent?.("telegram", renameError, {
|
|
1381
1477
|
phase: "leader-topic-reroute-reclaim-rename",
|
|
@@ -1402,7 +1498,10 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1402
1498
|
ctx,
|
|
1403
1499
|
);
|
|
1404
1500
|
pending.messages = [];
|
|
1405
|
-
const cleanupComplete = await closePreviousLeaderThread(
|
|
1501
|
+
const cleanupComplete = await closePreviousLeaderThread(
|
|
1502
|
+
record.target,
|
|
1503
|
+
assertExecutionCurrent,
|
|
1504
|
+
);
|
|
1406
1505
|
if (!cleanupComplete) {
|
|
1407
1506
|
pending.cleanup = {
|
|
1408
1507
|
kind: "previous-leader",
|
|
@@ -1427,6 +1526,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1427
1526
|
const cleanupComplete = await closeReroutedUnboundTopic(
|
|
1428
1527
|
sourceTarget,
|
|
1429
1528
|
sourceMessageId,
|
|
1529
|
+
assertExecutionCurrent,
|
|
1430
1530
|
);
|
|
1431
1531
|
if (!cleanupComplete && sourceTarget) {
|
|
1432
1532
|
pending.cleanup = {
|
|
@@ -1454,6 +1554,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1454
1554
|
query: TCallbackQuery,
|
|
1455
1555
|
ctx: TContext,
|
|
1456
1556
|
): Promise<void> => {
|
|
1557
|
+
const assertExecutionCurrent =
|
|
1558
|
+
Updates.createTelegramUpdateExecutionFenceGuard(query);
|
|
1559
|
+
assertExecutionCurrent();
|
|
1457
1560
|
if (await handleUnboundRerouteRestoreMenuCallback(query, ctx)) return;
|
|
1458
1561
|
if (await handleUnboundRerouteCallback(query, ctx)) return;
|
|
1459
1562
|
if (deps.buttonActionStore) {
|
|
@@ -1486,31 +1589,43 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1486
1589
|
if (typeof chatId !== "number" || typeof messageId !== "number")
|
|
1487
1590
|
return false;
|
|
1488
1591
|
const queueOrder = deps.bridgeRuntime.queue.allocateItemOrder();
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1592
|
+
const admissionReceipts = createAdmissionReceipts("prompt", [
|
|
1593
|
+
buttonQuery,
|
|
1594
|
+
]);
|
|
1595
|
+
const turn: Queue.PendingTelegramTurn = {
|
|
1596
|
+
...OutboundHandlers.createTelegramButtonPromptTurn({
|
|
1597
|
+
chatId,
|
|
1598
|
+
target:
|
|
1599
|
+
typeof buttonQuery.message?.message_thread_id === "number"
|
|
1600
|
+
? {
|
|
1601
|
+
chatId,
|
|
1602
|
+
threadId: buttonQuery.message.message_thread_id,
|
|
1603
|
+
}
|
|
1604
|
+
: { chatId },
|
|
1605
|
+
replyToMessageId: messageId,
|
|
1606
|
+
queueOrder,
|
|
1607
|
+
action,
|
|
1608
|
+
}),
|
|
1609
|
+
...(admissionReceipts.length > 0 ? { admissionReceipts } : {}),
|
|
1610
|
+
};
|
|
1502
1611
|
const result = Queue.appendTelegramPromptTurnOnce(
|
|
1503
1612
|
deps.telegramQueueStore.getQueuedItems(),
|
|
1504
1613
|
turn,
|
|
1505
1614
|
);
|
|
1506
|
-
if (!result.appended)
|
|
1615
|
+
if (!result.appended) {
|
|
1616
|
+
reportQueueAdmission([buttonQuery], admissionReceipts);
|
|
1617
|
+
return false;
|
|
1618
|
+
}
|
|
1619
|
+
Updates.assertTelegramUpdateExecutionCurrent(buttonQuery);
|
|
1507
1620
|
deps.telegramQueueStore.setQueuedItems(result.items);
|
|
1621
|
+
reportQueueAdmission([buttonQuery], admissionReceipts);
|
|
1508
1622
|
deps.updateStatus(context);
|
|
1509
1623
|
requestDispatchNextQueuedTelegramTurn(context);
|
|
1510
1624
|
return true;
|
|
1511
1625
|
},
|
|
1512
1626
|
},
|
|
1513
1627
|
);
|
|
1628
|
+
assertExecutionCurrent();
|
|
1514
1629
|
if (handled) return;
|
|
1515
1630
|
}
|
|
1516
1631
|
const handledByCompact =
|
|
@@ -1556,13 +1671,16 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1556
1671
|
});
|
|
1557
1672
|
},
|
|
1558
1673
|
});
|
|
1674
|
+
assertExecutionCurrent();
|
|
1559
1675
|
if (handledByCompact) return;
|
|
1560
1676
|
const handledByQueue = await deps.queueMenuCallbackHandler(query, ctx);
|
|
1677
|
+
assertExecutionCurrent();
|
|
1561
1678
|
if (handledByQueue) return;
|
|
1562
1679
|
const handledBySettings = await deps.settingsMenuCallbackHandler?.(
|
|
1563
1680
|
query,
|
|
1564
1681
|
ctx,
|
|
1565
1682
|
);
|
|
1683
|
+
assertExecutionCurrent();
|
|
1566
1684
|
if (handledBySettings) return;
|
|
1567
1685
|
const callbackData = query.data;
|
|
1568
1686
|
if (callbackData && !isTelegramOwnedCallbackData(callbackData)) {
|
|
@@ -1574,6 +1692,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1574
1692
|
typeof query.message?.message_thread_id === "number"
|
|
1575
1693
|
? { chatId, threadId: query.message.message_thread_id }
|
|
1576
1694
|
: { chatId };
|
|
1695
|
+
const admissionReceipts = createAdmissionReceipts("prompt", [query]);
|
|
1577
1696
|
const turn: Queue.PendingTelegramTurn = {
|
|
1578
1697
|
kind: "prompt",
|
|
1579
1698
|
chatId,
|
|
@@ -1587,15 +1706,20 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1587
1706
|
content: [{ type: "text", text: `[callback] ${callbackData}` }],
|
|
1588
1707
|
historyText: callbackData,
|
|
1589
1708
|
statusSummary: callbackData,
|
|
1709
|
+
...(admissionReceipts.length > 0 ? { admissionReceipts } : {}),
|
|
1590
1710
|
};
|
|
1591
1711
|
const result = Queue.appendTelegramPromptTurnOnce(
|
|
1592
1712
|
deps.telegramQueueStore.getQueuedItems(),
|
|
1593
1713
|
turn,
|
|
1594
1714
|
);
|
|
1595
1715
|
if (result.appended) {
|
|
1716
|
+
Updates.assertTelegramUpdateExecutionCurrent(query);
|
|
1596
1717
|
deps.telegramQueueStore.setQueuedItems(result.items);
|
|
1718
|
+
reportQueueAdmission([query], admissionReceipts);
|
|
1597
1719
|
deps.updateStatus(ctx);
|
|
1598
1720
|
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
1721
|
+
} else {
|
|
1722
|
+
reportQueueAdmission([query], admissionReceipts);
|
|
1599
1723
|
}
|
|
1600
1724
|
}
|
|
1601
1725
|
await deps.answerCallbackQuery(query.id);
|
|
@@ -1612,6 +1736,11 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1612
1736
|
processAttachments: deps.inboundHandlerRuntime.process,
|
|
1613
1737
|
resolveTimeLine: deps.resolveTimeLine,
|
|
1614
1738
|
getAllowedUserId: deps.configStore.getAllowedUserId,
|
|
1739
|
+
getAdmissionScope: deps.getAdmissionScope,
|
|
1740
|
+
getAdmissionJournalBinding: deps.getAdmissionJournalBinding,
|
|
1741
|
+
assertExecutionCurrent(message) {
|
|
1742
|
+
Updates.assertTelegramUpdateExecutionCurrent(message);
|
|
1743
|
+
},
|
|
1615
1744
|
|
|
1616
1745
|
// Voice policy resolves missing, invalid, and legacy manual config to hidden.
|
|
1617
1746
|
getVoiceReplyMode: () => getTelegramVoiceReplyMode(deps.configStore.get()),
|
|
@@ -1661,7 +1790,12 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1661
1790
|
laneOrder: deps.bridgeRuntime.queue.allocateControlOrder(),
|
|
1662
1791
|
statusSummary: "continue",
|
|
1663
1792
|
};
|
|
1793
|
+
Updates.assertTelegramUpdateExecutionCurrent(message);
|
|
1664
1794
|
deps.queueMutationRuntime.append(continueTurn, ctx);
|
|
1795
|
+
reportQueueAdmission(
|
|
1796
|
+
[continueMessage],
|
|
1797
|
+
continueTurn.admissionReceipts ?? [],
|
|
1798
|
+
);
|
|
1665
1799
|
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
1666
1800
|
};
|
|
1667
1801
|
const reservedCommandNames = () =>
|
|
@@ -1675,6 +1809,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1675
1809
|
TMessage,
|
|
1676
1810
|
TContext
|
|
1677
1811
|
>({
|
|
1812
|
+
assertExecutionCurrent(message) {
|
|
1813
|
+
Updates.assertTelegramUpdateExecutionCurrent(message);
|
|
1814
|
+
},
|
|
1678
1815
|
hasAbortHandler: deps.bridgeRuntime.abort.hasHandler,
|
|
1679
1816
|
clearPendingModelSwitch: deps.modelSwitchController.clearPendingSwitch,
|
|
1680
1817
|
hasQueuedTelegramItems: deps.telegramQueueStore.hasQueuedItems,
|
|
@@ -1690,6 +1827,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1690
1827
|
setCompactionInProgress:
|
|
1691
1828
|
deps.bridgeRuntime.lifecycle.setCompactionInProgress,
|
|
1692
1829
|
updateStatus: deps.updateStatus,
|
|
1830
|
+
isContextActive: deps.isContextActive,
|
|
1693
1831
|
dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
|
|
1694
1832
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
1695
1833
|
deps.requestDeferredDispatchNextQueuedTelegramTurn,
|
|
@@ -1700,6 +1838,10 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1700
1838
|
allocateItemOrder: deps.bridgeRuntime.queue.allocateItemOrder,
|
|
1701
1839
|
allocateControlOrder: deps.bridgeRuntime.queue.allocateControlOrder,
|
|
1702
1840
|
appendControlItem: deps.queueMutationRuntime.append,
|
|
1841
|
+
getAdmissionScope: deps.getAdmissionScope,
|
|
1842
|
+
getAdmissionJournalBinding: deps.getAdmissionJournalBinding,
|
|
1843
|
+
onControlQueued: (message, receipt) =>
|
|
1844
|
+
reportQueueAdmission([message], [receipt]),
|
|
1703
1845
|
showStatus: deps.menuActions.sendStatusMessage,
|
|
1704
1846
|
openModelMenu: deps.menuActions.openModelMenu,
|
|
1705
1847
|
openThinkingMenu: (message, ctx) => {
|
|
@@ -1720,27 +1862,36 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1720
1862
|
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
1721
1863
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
1722
1864
|
});
|
|
1723
|
-
const
|
|
1724
|
-
TMessage,
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1865
|
+
const promptEnqueueController =
|
|
1866
|
+
Queue.createTelegramPromptEnqueueController<TMessage, TContext>({
|
|
1867
|
+
...deps.telegramQueueStore,
|
|
1868
|
+
getFoldQueuedPromptsIntoHistory:
|
|
1869
|
+
deps.bridgeRuntime.lifecycle.shouldFoldQueuedPromptsIntoHistory,
|
|
1870
|
+
setFoldQueuedPromptsIntoHistory:
|
|
1871
|
+
deps.bridgeRuntime.lifecycle.setFoldQueuedPromptsIntoHistory,
|
|
1872
|
+
createTurn: async (messages, historyTurns, turnCtx) => {
|
|
1873
|
+
const turn = await promptTurnBuilder(messages, historyTurns, turnCtx);
|
|
1874
|
+
return turn.replyToMessageId > 0
|
|
1875
|
+
? turn
|
|
1876
|
+
: { ...turn, replyToMessageId: 0 };
|
|
1877
|
+
},
|
|
1878
|
+
updateStatus: deps.updateStatus,
|
|
1879
|
+
dispatchNextQueuedTelegramTurn: requestDispatchNextQueuedTelegramTurn,
|
|
1880
|
+
assertExecutionCurrent: (messages) =>
|
|
1881
|
+
Updates.assertTelegramUpdateExecutionCurrent(messages[0]),
|
|
1882
|
+
});
|
|
1883
|
+
const promptEnqueue = async (
|
|
1884
|
+
messages: TMessage[],
|
|
1885
|
+
ctx: TContext,
|
|
1886
|
+
): Promise<Queue.PendingTelegramTurn> => {
|
|
1887
|
+
return promptEnqueueController.enqueue(messages, ctx, (turn) => {
|
|
1888
|
+
reportQueueAdmission(messages, turn.admissionReceipts ?? []);
|
|
1889
|
+
});
|
|
1890
|
+
};
|
|
1741
1891
|
const sendUnboundRerouteChooserNow = async (
|
|
1742
1892
|
messages: TMessage[],
|
|
1743
1893
|
ctx: TContext,
|
|
1894
|
+
reportDeferred = true,
|
|
1744
1895
|
): Promise<void> => {
|
|
1745
1896
|
const message = messages[0];
|
|
1746
1897
|
if (!message || !deps.threadStore) return;
|
|
@@ -1775,6 +1926,11 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1775
1926
|
return;
|
|
1776
1927
|
}
|
|
1777
1928
|
const rerouteId = storePendingUnboundReroute(messages);
|
|
1929
|
+
if (reportDeferred) {
|
|
1930
|
+
for (const source of messages) {
|
|
1931
|
+
Updates.reportTelegramUpdateDeferred(source);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1778
1934
|
const text = formatTelegramUnboundRerouteChooserText(activeRecords, {
|
|
1779
1935
|
includeGuidance,
|
|
1780
1936
|
});
|
|
@@ -1821,10 +1977,11 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1821
1977
|
const messages = [...(existing?.messages ?? []), message];
|
|
1822
1978
|
const timer = setTimeout(() => {
|
|
1823
1979
|
pendingUnboundRerouteMediaGroups.delete(groupKey);
|
|
1824
|
-
void sendUnboundRerouteChooserNow(messages, ctx);
|
|
1980
|
+
void sendUnboundRerouteChooserNow(messages, ctx, false);
|
|
1825
1981
|
}, 1200);
|
|
1826
1982
|
timer.unref?.();
|
|
1827
1983
|
pendingUnboundRerouteMediaGroups.set(groupKey, { messages, timer });
|
|
1984
|
+
Updates.reportTelegramUpdateDeferred(message);
|
|
1828
1985
|
};
|
|
1829
1986
|
const getKnownTelegramAllTabCommand = (
|
|
1830
1987
|
text: string,
|
|
@@ -1864,6 +2021,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1864
2021
|
caption: undefined,
|
|
1865
2022
|
} as TMessage;
|
|
1866
2023
|
const rerouteId = storePendingUnboundReroute([commandMessage], "command");
|
|
2024
|
+
Updates.reportTelegramUpdateDeferred(commandMessage);
|
|
1867
2025
|
const text = formatTelegramAllTabMenuChooserText(command.name);
|
|
1868
2026
|
const replyMarkup = buildTelegramUnboundRerouteChooserMarkup(
|
|
1869
2027
|
rerouteId,
|
|
@@ -1917,6 +2075,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1917
2075
|
TContext
|
|
1918
2076
|
>({
|
|
1919
2077
|
extractRawText: Media.extractFirstTelegramMessageText,
|
|
2078
|
+
assertExecutionCurrent(message) {
|
|
2079
|
+
Updates.assertTelegramUpdateExecutionCurrent(message);
|
|
2080
|
+
},
|
|
1920
2081
|
shouldIgnoreMessages: (messages) =>
|
|
1921
2082
|
!Media.hasTelegramMessagesPromptContent(messages),
|
|
1922
2083
|
handleCommand: commandHandler,
|
|
@@ -1926,18 +2087,26 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1926
2087
|
);
|
|
1927
2088
|
if (!extensionCommand) return false;
|
|
1928
2089
|
const sourceTarget = Updates.getTelegramMessageTarget(message);
|
|
2090
|
+
const assertExecutionCurrent =
|
|
2091
|
+
Updates.createTelegramUpdateExecutionFenceGuard(message);
|
|
1929
2092
|
try {
|
|
2093
|
+
assertExecutionCurrent();
|
|
1930
2094
|
await extensionCommand.handler({
|
|
1931
2095
|
name: command.name,
|
|
1932
2096
|
args: command.args,
|
|
1933
|
-
reply: (text) =>
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2097
|
+
reply: async (text) => {
|
|
2098
|
+
assertExecutionCurrent();
|
|
2099
|
+
await deps.sendTextReply(
|
|
2100
|
+
message.chat.id,
|
|
2101
|
+
message.message_id,
|
|
2102
|
+
text,
|
|
2103
|
+
{ target: sourceTarget },
|
|
2104
|
+
);
|
|
2105
|
+
assertExecutionCurrent();
|
|
2106
|
+
},
|
|
2107
|
+
enqueuePrompt: async (prompt) => {
|
|
2108
|
+
assertExecutionCurrent();
|
|
2109
|
+
await promptEnqueue(
|
|
1941
2110
|
[
|
|
1942
2111
|
{
|
|
1943
2112
|
...message,
|
|
@@ -1946,12 +2115,15 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1946
2115
|
} as TMessage,
|
|
1947
2116
|
],
|
|
1948
2117
|
ctx,
|
|
1949
|
-
)
|
|
2118
|
+
);
|
|
2119
|
+
},
|
|
1950
2120
|
});
|
|
2121
|
+
assertExecutionCurrent();
|
|
1951
2122
|
} catch (error) {
|
|
1952
2123
|
deps.recordRuntimeEvent?.("telegram-command", error, {
|
|
1953
2124
|
command: command.name,
|
|
1954
2125
|
});
|
|
2126
|
+
assertExecutionCurrent();
|
|
1955
2127
|
await deps.sendTextReply(
|
|
1956
2128
|
message.chat.id,
|
|
1957
2129
|
message.message_id,
|
|
@@ -1969,7 +2141,9 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1969
2141
|
),
|
|
1970
2142
|
replaceMessageText: (message, text) =>
|
|
1971
2143
|
({ ...message, text, caption: undefined }) as TMessage,
|
|
1972
|
-
enqueueTurn:
|
|
2144
|
+
enqueueTurn: async (messages, ctx) => {
|
|
2145
|
+
await promptEnqueue(messages, ctx);
|
|
2146
|
+
},
|
|
1973
2147
|
});
|
|
1974
2148
|
dispatchReroutedCommandMessages = (messages, ctx) =>
|
|
1975
2149
|
commandOrPrompt.dispatchMessages(messages, ctx);
|
|
@@ -1979,6 +2153,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1979
2153
|
>({
|
|
1980
2154
|
mediaGroups: deps.mediaGroupRuntime,
|
|
1981
2155
|
dispatchMessages: commandOrPrompt.dispatchMessages,
|
|
2156
|
+
onDeferredMessage: Updates.reportTelegramUpdateDeferred,
|
|
1982
2157
|
});
|
|
1983
2158
|
const textDispatch = TextGroups.createTelegramTextGroupDispatchRuntime<
|
|
1984
2159
|
TMessage,
|
|
@@ -1987,6 +2162,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1987
2162
|
textGroups: deps.textGroupRuntime,
|
|
1988
2163
|
dispatchMessages: commandOrPrompt.dispatchMessages,
|
|
1989
2164
|
dispatchSingleMessage: mediaDispatch.handleMessage,
|
|
2165
|
+
onDeferredMessage: Updates.reportTelegramUpdateDeferred,
|
|
1990
2166
|
});
|
|
1991
2167
|
const editRuntime = Turns.createTelegramQueuedPromptEditRuntime<
|
|
1992
2168
|
TMessage,
|
|
@@ -1999,16 +2175,24 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1999
2175
|
lifecycle: Updates.TelegramTopicLifecycleUpdate<TMessage>,
|
|
2000
2176
|
ctx: TContext,
|
|
2001
2177
|
): Promise<void> => {
|
|
2178
|
+
const assertExecutionCurrent =
|
|
2179
|
+
Updates.createTelegramUpdateExecutionFenceGuard(lifecycle.message);
|
|
2180
|
+
assertExecutionCurrent();
|
|
2002
2181
|
await deps.handleTelegramTopicLifecycleUpdate?.(lifecycle, ctx);
|
|
2182
|
+
assertExecutionCurrent();
|
|
2003
2183
|
if (lifecycle.kind !== "created" || !deps.threadStore) {
|
|
2004
2184
|
return;
|
|
2005
2185
|
}
|
|
2006
2186
|
await deps.threadStore.load();
|
|
2187
|
+
assertExecutionCurrent();
|
|
2007
2188
|
};
|
|
2008
2189
|
const handleAuthorizedTelegramGuestMessage = async (
|
|
2009
2190
|
guestMessage: Updates.TelegramGuestMessage & { from: TelegramUser },
|
|
2010
2191
|
ctx: TContext,
|
|
2011
2192
|
): Promise<void> => {
|
|
2193
|
+
const assertExecutionCurrent =
|
|
2194
|
+
Updates.createTelegramUpdateExecutionFenceGuard(guestMessage);
|
|
2195
|
+
assertExecutionCurrent();
|
|
2012
2196
|
const text = guestMessage.text ?? "";
|
|
2013
2197
|
const gm = guestMessage as unknown as Record<string, unknown>;
|
|
2014
2198
|
// Build telegram prefix with guest context
|
|
@@ -2062,14 +2246,17 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2062
2246
|
{ downloadFile: deps.downloadFile },
|
|
2063
2247
|
)
|
|
2064
2248
|
: [];
|
|
2249
|
+
assertExecutionCurrent();
|
|
2065
2250
|
const files = await Media.downloadTelegramMessageFiles([guestMsg], {
|
|
2066
2251
|
downloadFile: deps.downloadFile,
|
|
2067
2252
|
});
|
|
2253
|
+
assertExecutionCurrent();
|
|
2068
2254
|
const processed = await deps.inboundHandlerRuntime.process(
|
|
2069
2255
|
files,
|
|
2070
2256
|
text,
|
|
2071
2257
|
ctx,
|
|
2072
2258
|
);
|
|
2259
|
+
assertExecutionCurrent();
|
|
2073
2260
|
const rawText = processed.rawText || text;
|
|
2074
2261
|
let sourceContext = "";
|
|
2075
2262
|
if (replyMsg) {
|
|
@@ -2099,6 +2286,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2099
2286
|
if (file.isImage && file.mimeType) {
|
|
2100
2287
|
try {
|
|
2101
2288
|
const buffer = await readFile(file.path);
|
|
2289
|
+
assertExecutionCurrent();
|
|
2102
2290
|
content.push({
|
|
2103
2291
|
type: "image",
|
|
2104
2292
|
data: Buffer.from(buffer).toString("base64"),
|
|
@@ -2109,6 +2297,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2109
2297
|
}
|
|
2110
2298
|
}
|
|
2111
2299
|
}
|
|
2300
|
+
const admissionReceipts = createAdmissionReceipts("prompt", [guestMessage]);
|
|
2112
2301
|
const guestTurn: Queue.PendingTelegramTurn = {
|
|
2113
2302
|
kind: "prompt",
|
|
2114
2303
|
chatId: 0,
|
|
@@ -2128,11 +2317,14 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2128
2317
|
statusSummary: Turns.truncateTelegramQueueSummary(
|
|
2129
2318
|
processed.rawText || text,
|
|
2130
2319
|
),
|
|
2320
|
+
...(admissionReceipts.length > 0 ? { admissionReceipts } : {}),
|
|
2131
2321
|
};
|
|
2132
2322
|
const items = deps.telegramQueueStore.getQueuedItems();
|
|
2323
|
+
Updates.assertTelegramUpdateExecutionCurrent(guestMessage);
|
|
2133
2324
|
deps.telegramQueueStore.setQueuedItems(
|
|
2134
2325
|
Queue.appendTelegramQueueItem(items, guestTurn),
|
|
2135
2326
|
);
|
|
2327
|
+
reportQueueAdmission([guestMessage], admissionReceipts);
|
|
2136
2328
|
deps.updateStatus(ctx);
|
|
2137
2329
|
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
2138
2330
|
};
|
|
@@ -2148,17 +2340,20 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2148
2340
|
persistConfig: deps.configStore.persist,
|
|
2149
2341
|
updateStatus: deps.updateStatus,
|
|
2150
2342
|
removePendingMediaGroupMessages: deps.mediaGroupRuntime.removeMessages,
|
|
2343
|
+
flushPendingMediaGroupMessage: deps.mediaGroupRuntime.flushMessage,
|
|
2344
|
+
flushPendingTextGroupMessage: deps.textGroupRuntime.flushMessage,
|
|
2151
2345
|
removeQueuedTelegramTurnsByMessageIds:
|
|
2152
2346
|
deps.queueMutationRuntime.removeByMessageIds,
|
|
2153
|
-
|
|
2154
|
-
deps.queueMutationRuntime.
|
|
2155
|
-
prioritizeQueuedTelegramTurnByMessageId:
|
|
2156
|
-
deps.queueMutationRuntime.prioritizeByMessageId,
|
|
2347
|
+
applyQueuedTelegramTurnReactionByMessageId:
|
|
2348
|
+
deps.queueMutationRuntime.applyReactionByMessageId,
|
|
2157
2349
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
2158
2350
|
answerGuestQuery: deps.answerGuestQuery,
|
|
2159
2351
|
handleAuthorizedTelegramCallbackQuery: callbackHandler,
|
|
2160
2352
|
sendTextReply: deps.sendTextReply,
|
|
2161
2353
|
handleAuthorizedTelegramMessage: async (message, ctx) => {
|
|
2354
|
+
const assertExecutionCurrent =
|
|
2355
|
+
Updates.createTelegramUpdateExecutionFenceGuard(message);
|
|
2356
|
+
assertExecutionCurrent();
|
|
2162
2357
|
if (typeof message.message_thread_id === "number") {
|
|
2163
2358
|
await deps.handleTelegramThreadTargetObserved?.(
|
|
2164
2359
|
{
|
|
@@ -2167,12 +2362,14 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2167
2362
|
},
|
|
2168
2363
|
ctx,
|
|
2169
2364
|
);
|
|
2365
|
+
assertExecutionCurrent();
|
|
2170
2366
|
}
|
|
2171
2367
|
const text = Media.extractFirstTelegramMessageText([
|
|
2172
2368
|
message as TMessage,
|
|
2173
2369
|
]).trim();
|
|
2174
2370
|
if (deps.threadStore && typeof message.message_thread_id !== "number") {
|
|
2175
2371
|
await deps.threadStore.load();
|
|
2372
|
+
assertExecutionCurrent();
|
|
2176
2373
|
if (deps.threadStore.getBotState().threadMode === "disabled") {
|
|
2177
2374
|
await textDispatch.handleMessage(message as TMessage, ctx);
|
|
2178
2375
|
return;
|
|
@@ -2213,6 +2410,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2213
2410
|
"thread-mode-unavailable-threadless-prompt",
|
|
2214
2411
|
});
|
|
2215
2412
|
await deps.threadStore.persist();
|
|
2413
|
+
assertExecutionCurrent();
|
|
2216
2414
|
await textDispatch.handleMessage(message as TMessage, ctx);
|
|
2217
2415
|
return;
|
|
2218
2416
|
}
|
|
@@ -2236,11 +2434,15 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2236
2434
|
handleAuthorizedTelegramEditedMessage: editRuntime.updateFromEditedMessage,
|
|
2237
2435
|
handleAuthorizedTelegramGuestMessage,
|
|
2238
2436
|
handleUnboundTelegramTopicMessage: async (message, ctx) => {
|
|
2437
|
+
const assertExecutionCurrent =
|
|
2438
|
+
Updates.createTelegramUpdateExecutionFenceGuard(message);
|
|
2439
|
+
assertExecutionCurrent();
|
|
2239
2440
|
if (!deps.threadStore) {
|
|
2240
2441
|
await textDispatch.handleMessage(message as TMessage, ctx);
|
|
2241
2442
|
return;
|
|
2242
2443
|
}
|
|
2243
2444
|
await deps.threadStore.load();
|
|
2445
|
+
assertExecutionCurrent();
|
|
2244
2446
|
if (deps.threadStore.getBotState().threadMode === "disabled") {
|
|
2245
2447
|
await textDispatch.handleMessage(message as TMessage, ctx);
|
|
2246
2448
|
return;
|
|
@@ -2280,6 +2482,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2280
2482
|
rerouteConfirmedAtMs: nowMs,
|
|
2281
2483
|
});
|
|
2282
2484
|
await deps.threadStore.persist();
|
|
2485
|
+
assertExecutionCurrent();
|
|
2283
2486
|
}
|
|
2284
2487
|
await textDispatch.handleMessage(message as TMessage, ctx);
|
|
2285
2488
|
return;
|
|
@@ -2350,6 +2553,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2350
2553
|
slot,
|
|
2351
2554
|
});
|
|
2352
2555
|
await deps.threadStore.persist();
|
|
2556
|
+
assertExecutionCurrent();
|
|
2353
2557
|
deps.setCurrentLeaderIdentity?.({
|
|
2354
2558
|
target: { chatId: target.chatId, threadId: target.threadId },
|
|
2355
2559
|
slot,
|
|
@@ -2381,6 +2585,27 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2381
2585
|
);
|
|
2382
2586
|
return;
|
|
2383
2587
|
}
|
|
2588
|
+
const deletedObservation = deps.threadStore
|
|
2589
|
+
.listSyncObservations()
|
|
2590
|
+
.find(
|
|
2591
|
+
(observation) =>
|
|
2592
|
+
observation.syncStatus === "deleted" &&
|
|
2593
|
+
observation.target.chatId === target.chatId &&
|
|
2594
|
+
observation.target.threadId === target.threadId,
|
|
2595
|
+
);
|
|
2596
|
+
if (deletedObservation) {
|
|
2597
|
+
deps.recordRuntimeEvent?.(
|
|
2598
|
+
"inbound-worker",
|
|
2599
|
+
"Discarded update from a confirmed deleted Telegram thread",
|
|
2600
|
+
{
|
|
2601
|
+
phase: "discard-deleted-thread",
|
|
2602
|
+
chatId: target.chatId,
|
|
2603
|
+
threadId: target.threadId,
|
|
2604
|
+
messageId: message.message_id,
|
|
2605
|
+
},
|
|
2606
|
+
);
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2384
2609
|
const reservations = deps.threadStore.listReservations();
|
|
2385
2610
|
const reservation = reservations.find(
|
|
2386
2611
|
(reservation) =>
|
|
@@ -2464,6 +2689,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2464
2689
|
slot,
|
|
2465
2690
|
});
|
|
2466
2691
|
await deps.threadStore.persist();
|
|
2692
|
+
assertExecutionCurrent();
|
|
2467
2693
|
deps.setCurrentLeaderIdentity?.({
|
|
2468
2694
|
target: { chatId: target.chatId, threadId: target.threadId },
|
|
2469
2695
|
slot,
|
|
@@ -2535,6 +2761,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2535
2761
|
slot,
|
|
2536
2762
|
});
|
|
2537
2763
|
await deps.threadStore.persist();
|
|
2764
|
+
assertExecutionCurrent();
|
|
2538
2765
|
deps.setCurrentLeaderIdentity?.({
|
|
2539
2766
|
target: { chatId: target.chatId, threadId: target.threadId },
|
|
2540
2767
|
slot,
|