@llblab/pi-telegram 0.10.6 → 0.10.7
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/CHANGELOG.md +7 -0
- package/lib/commands.ts +19 -3
- package/lib/config.ts +13 -1
- package/lib/queue.ts +28 -4
- package/lib/updates.ts +77 -65
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.7: Stale Context Hardening Hotfix
|
|
4
|
+
|
|
5
|
+
- `[Session Reloads]` Context-sensitive command, pairing, queue, session-start, and update-dispatch paths now ignore only stale-session/stale-context failures instead of swallowing broad runtime errors. Impact: the bridge survives ctx replacement/fork/reload races while real bugs still surface for diagnostics.
|
|
6
|
+
- `[Runtime Status]` Restored status update error propagation so existing polling/dispatch safety wrappers can record stale status failures as structured runtime events instead of losing diagnostics inside the status domain.
|
|
7
|
+
- `[Release]` Added a tag-triggered GitHub Actions release workflow that verifies the `vX.Y.Z` tag matches `package.json`, extracts the matching `CHANGELOG.md` section, and publishes a GitHub Release automatically.
|
|
8
|
+
- `[Tests]` Added focused regressions proving the newly guarded call sites tolerate stale context errors and still rethrow unrelated failures.
|
|
9
|
+
|
|
3
10
|
## 0.10.6: Native Typing Keepalive Hotfix
|
|
4
11
|
|
|
5
12
|
- `[Typing]` Telegram native `typing` chat actions now refresh every 2.5s instead of every 4s. Impact: the bot's Telegram-side typing animation has more headroom to stay visible during model retries, transient model/API errors, and other long-running agent work.
|
package/lib/commands.ts
CHANGED
|
@@ -579,7 +579,7 @@ export interface TelegramCommandRuntimeDeps<
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
export const TELEGRAM_APP_MENU_INTRO_HTML = [
|
|
582
|
-
"<b>π Telegram
|
|
582
|
+
"<b>π Telegram</b>",
|
|
583
583
|
"",
|
|
584
584
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
585
585
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
@@ -826,18 +826,34 @@ export async function handleTelegramCompactCommand(
|
|
|
826
826
|
await deps.sendTextReply("Compaction started.");
|
|
827
827
|
}
|
|
828
828
|
|
|
829
|
+
function isTelegramStaleContextError(error: unknown): boolean {
|
|
830
|
+
return (
|
|
831
|
+
error instanceof Error &&
|
|
832
|
+
(error.message.includes("stale after session") ||
|
|
833
|
+
error.message.includes("stale ctx"))
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
|
|
829
837
|
export async function handleTelegramStatusCommand<TContext>(deps: {
|
|
830
838
|
ctx: TContext;
|
|
831
839
|
showStatus: (ctx: TContext) => Promise<void>;
|
|
832
840
|
}): Promise<void> {
|
|
833
|
-
|
|
841
|
+
try {
|
|
842
|
+
await deps.showStatus(deps.ctx);
|
|
843
|
+
} catch (error) {
|
|
844
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
845
|
+
}
|
|
834
846
|
}
|
|
835
847
|
|
|
836
848
|
export async function handleTelegramModelCommand<TContext>(deps: {
|
|
837
849
|
ctx: TContext;
|
|
838
850
|
openModelMenu: (ctx: TContext) => Promise<void>;
|
|
839
851
|
}): Promise<void> {
|
|
840
|
-
|
|
852
|
+
try {
|
|
853
|
+
await deps.openModelMenu(deps.ctx);
|
|
854
|
+
} catch (error) {
|
|
855
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
856
|
+
}
|
|
841
857
|
}
|
|
842
858
|
|
|
843
859
|
export async function executeTelegramCommandAction<TMessage, TContext>(
|
package/lib/config.ts
CHANGED
|
@@ -185,6 +185,14 @@ export function getTelegramAuthorizationState(
|
|
|
185
185
|
return { kind: "deny" };
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
function isTelegramStaleContextError(error: unknown): boolean {
|
|
189
|
+
return (
|
|
190
|
+
error instanceof Error &&
|
|
191
|
+
(error.message.includes("stale after session") ||
|
|
192
|
+
error.message.includes("stale ctx"))
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
188
196
|
export async function pairTelegramUserIfNeeded<TContext>(
|
|
189
197
|
userId: number,
|
|
190
198
|
deps: TelegramUserPairingDeps<TContext>,
|
|
@@ -196,7 +204,11 @@ export async function pairTelegramUserIfNeeded<TContext>(
|
|
|
196
204
|
if (authorization.kind !== "pair") return false;
|
|
197
205
|
deps.setAllowedUserId(authorization.userId);
|
|
198
206
|
await deps.persistConfig();
|
|
199
|
-
|
|
207
|
+
try {
|
|
208
|
+
deps.updateStatus(deps.ctx);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
211
|
+
}
|
|
200
212
|
return true;
|
|
201
213
|
}
|
|
202
214
|
|
package/lib/queue.ts
CHANGED
|
@@ -1289,6 +1289,14 @@ export interface TelegramPromptEnqueueController<TMessage, TContext = unknown> {
|
|
|
1289
1289
|
enqueue: (messages: TMessage[], ctx: TContext) => Promise<void>;
|
|
1290
1290
|
}
|
|
1291
1291
|
|
|
1292
|
+
function isTelegramStaleContextError(error: unknown): boolean {
|
|
1293
|
+
return (
|
|
1294
|
+
error instanceof Error &&
|
|
1295
|
+
(error.message.includes("stale after session") ||
|
|
1296
|
+
error.message.includes("stale ctx"))
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1292
1300
|
export function buildTelegramSessionStartState<TModel = unknown>(
|
|
1293
1301
|
currentModel: TModel | undefined,
|
|
1294
1302
|
): TelegramSessionStartState<TModel> {
|
|
@@ -1326,7 +1334,11 @@ export async function startTelegramSessionRuntime<TContext, TModel = unknown>(
|
|
|
1326
1334
|
await deps.loadConfig();
|
|
1327
1335
|
deps.applyState(buildTelegramSessionStartState(deps.currentModel));
|
|
1328
1336
|
await deps.prepareTempDir();
|
|
1329
|
-
|
|
1337
|
+
try {
|
|
1338
|
+
deps.bindDeferredDispatchContext?.(deps.ctx);
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
1341
|
+
}
|
|
1330
1342
|
deps.updateStatus();
|
|
1331
1343
|
}
|
|
1332
1344
|
|
|
@@ -1478,7 +1490,11 @@ export function reorderTelegramQueueItemsRuntime<TContext>(
|
|
|
1478
1490
|
deps.setQueuedItems(
|
|
1479
1491
|
[...deps.getQueuedItems()].sort(compareTelegramQueueItems),
|
|
1480
1492
|
);
|
|
1481
|
-
|
|
1493
|
+
try {
|
|
1494
|
+
deps.updateStatus(deps.ctx);
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
1497
|
+
}
|
|
1482
1498
|
}
|
|
1483
1499
|
|
|
1484
1500
|
export function clearTelegramQueueItemsRuntime<TContext>(
|
|
@@ -1487,7 +1503,11 @@ export function clearTelegramQueueItemsRuntime<TContext>(
|
|
|
1487
1503
|
const removedCount = deps.getQueuedItems().length;
|
|
1488
1504
|
if (removedCount === 0) return 0;
|
|
1489
1505
|
deps.setQueuedItems([]);
|
|
1490
|
-
|
|
1506
|
+
try {
|
|
1507
|
+
deps.updateStatus(deps.ctx);
|
|
1508
|
+
} catch (error) {
|
|
1509
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
1510
|
+
}
|
|
1491
1511
|
return removedCount;
|
|
1492
1512
|
}
|
|
1493
1513
|
|
|
@@ -1501,7 +1521,11 @@ export function removeTelegramQueueItemsByMessageIdsRuntime<TContext>(
|
|
|
1501
1521
|
);
|
|
1502
1522
|
if (removedCount === 0) return 0;
|
|
1503
1523
|
deps.setQueuedItems(items);
|
|
1504
|
-
|
|
1524
|
+
try {
|
|
1525
|
+
deps.updateStatus(deps.ctx);
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
1528
|
+
}
|
|
1505
1529
|
return removedCount;
|
|
1506
1530
|
}
|
|
1507
1531
|
|
package/lib/updates.ts
CHANGED
|
@@ -746,6 +746,14 @@ export async function handleAuthorizedTelegramReactionUpdate<TContext>(
|
|
|
746
746
|
);
|
|
747
747
|
}
|
|
748
748
|
|
|
749
|
+
function isTelegramStaleContextError(error: unknown): boolean {
|
|
750
|
+
return (
|
|
751
|
+
error instanceof Error &&
|
|
752
|
+
(error.message.includes("stale after session") ||
|
|
753
|
+
error.message.includes("stale ctx"))
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
749
757
|
export async function executeTelegramUpdatePlan<
|
|
750
758
|
TContext = unknown,
|
|
751
759
|
TReactionUpdate extends TelegramMessageReactionUpdated =
|
|
@@ -761,81 +769,85 @@ export async function executeTelegramUpdatePlan<
|
|
|
761
769
|
TMessage
|
|
762
770
|
>,
|
|
763
771
|
): Promise<void> {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
}
|
|
770
|
-
if (plan.kind === "reaction") {
|
|
771
|
-
await deps.handleAuthorizedTelegramReactionUpdate(
|
|
772
|
-
plan.reactionUpdate,
|
|
773
|
-
deps.ctx,
|
|
774
|
-
);
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
if (plan.kind === "callback") {
|
|
778
|
-
if (plan.shouldPair) {
|
|
779
|
-
await deps.pairTelegramUserIfNeeded(plan.query.from.id, deps.ctx);
|
|
780
|
-
}
|
|
781
|
-
if (plan.shouldDeny) {
|
|
782
|
-
const callbackQueryId = getTelegramCallbackQueryId(plan.query);
|
|
783
|
-
if (callbackQueryId) {
|
|
784
|
-
await deps.answerCallbackQuery(
|
|
785
|
-
callbackQueryId,
|
|
786
|
-
"This bot is not authorized for your account.",
|
|
787
|
-
);
|
|
788
|
-
}
|
|
772
|
+
try {
|
|
773
|
+
if (plan.kind === "ignore") return;
|
|
774
|
+
if (plan.kind === "deleted") {
|
|
775
|
+
deps.removePendingMediaGroupMessages(plan.messageIds);
|
|
776
|
+
deps.removeQueuedTelegramTurnsByMessageIds(plan.messageIds, deps.ctx);
|
|
789
777
|
return;
|
|
790
778
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
if (plan.shouldDeny) {
|
|
796
|
-
await deps.answerGuestQuery(
|
|
797
|
-
plan.guestMessage.guest_query_id,
|
|
798
|
-
"Access denied.",
|
|
779
|
+
if (plan.kind === "reaction") {
|
|
780
|
+
await deps.handleAuthorizedTelegramReactionUpdate(
|
|
781
|
+
plan.reactionUpdate,
|
|
782
|
+
deps.ctx,
|
|
799
783
|
);
|
|
800
784
|
return;
|
|
801
785
|
}
|
|
802
|
-
if (
|
|
803
|
-
|
|
804
|
-
plan.
|
|
805
|
-
|
|
806
|
-
)
|
|
786
|
+
if (plan.kind === "callback") {
|
|
787
|
+
if (plan.shouldPair) {
|
|
788
|
+
await deps.pairTelegramUserIfNeeded(plan.query.from.id, deps.ctx);
|
|
789
|
+
}
|
|
790
|
+
if (plan.shouldDeny) {
|
|
791
|
+
const callbackQueryId = getTelegramCallbackQueryId(plan.query);
|
|
792
|
+
if (callbackQueryId) {
|
|
793
|
+
await deps.answerCallbackQuery(
|
|
794
|
+
callbackQueryId,
|
|
795
|
+
"This bot is not authorized for your account.",
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
await deps.handleAuthorizedTelegramCallbackQuery(plan.query, deps.ctx);
|
|
801
|
+
return;
|
|
807
802
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
803
|
+
if (plan.kind === "guest") {
|
|
804
|
+
if (plan.shouldDeny) {
|
|
805
|
+
await deps.answerGuestQuery(
|
|
806
|
+
plan.guestMessage.guest_query_id,
|
|
807
|
+
"Access denied.",
|
|
808
|
+
);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
if (deps.handleAuthorizedTelegramGuestMessage) {
|
|
812
|
+
await deps.handleAuthorizedTelegramGuestMessage(
|
|
813
|
+
plan.guestMessage,
|
|
814
|
+
deps.ctx,
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const pairedNow = plan.shouldPair
|
|
820
|
+
? await deps.pairTelegramUserIfNeeded(plan.message.from.id, deps.ctx)
|
|
821
|
+
: false;
|
|
822
|
+
const replyTarget = getTelegramMessageReplyTarget(plan.message);
|
|
823
|
+
if (
|
|
824
|
+
plan.kind === "message" &&
|
|
825
|
+
pairedNow &&
|
|
826
|
+
plan.shouldNotifyPaired &&
|
|
827
|
+
replyTarget
|
|
828
|
+
) {
|
|
828
829
|
await deps.sendTextReply(
|
|
829
830
|
replyTarget.chatId,
|
|
830
831
|
replyTarget.messageId,
|
|
831
|
-
"
|
|
832
|
+
"Telegram bridge paired with this account.",
|
|
832
833
|
);
|
|
833
834
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
835
|
+
if (plan.shouldDeny) {
|
|
836
|
+
if (replyTarget) {
|
|
837
|
+
await deps.sendTextReply(
|
|
838
|
+
replyTarget.chatId,
|
|
839
|
+
replyTarget.messageId,
|
|
840
|
+
"This bot is not authorized for your account.",
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
if (plan.kind === "edited-message") {
|
|
846
|
+
await deps.handleAuthorizedTelegramEditedMessage(plan.message, deps.ctx);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
await deps.handleAuthorizedTelegramMessage(plan.message, deps.ctx);
|
|
850
|
+
} catch (error) {
|
|
851
|
+
if (!isTelegramStaleContextError(error)) throw error;
|
|
839
852
|
}
|
|
840
|
-
await deps.handleAuthorizedTelegramMessage(plan.message, deps.ctx);
|
|
841
853
|
}
|