@llblab/pi-telegram 0.10.4 → 0.10.5
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/index.ts +2 -0
- package/lib/commands.ts +31 -2
- package/lib/routing.ts +5 -0
- package/lib/runtime.ts +36 -3
- package/lib/text-groups.ts +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.5: Queue Continuity And Input Resilience Hotfix
|
|
4
|
+
|
|
5
|
+
- `[Compaction]` `/compact` completion and failure callbacks now request deferred queue dispatch instead of dispatching immediately. Impact: queued Telegram turns resume after compaction state and π idle/pending-message state have a chance to settle.
|
|
6
|
+
- `[Text Groups]` Long-text split recovery is more aggressive where Telegram chunking actually drifts: the debounce is rounded to 1s, the conservative 3600-character start threshold is preserved, and continuation messages can span a much wider message-id gap while staying scoped to the same chat/user and non-command text. Impact: very large pasted prompts are more likely to arrive as one agent turn instead of several fragmented turns.
|
|
7
|
+
- `[Runtime Status]` Typing-loop and prompt-dispatch status updates are now best-effort and record stale-context failures as structured runtime events. Impact: status/Running indicators remain resilient after error paths without hiding diagnostics.
|
|
8
|
+
- `[Tests]` Added regressions for deferred compact dispatch, stale status failures in typing/dispatch paths, and many-part split-text grouping.
|
|
9
|
+
|
|
3
10
|
## 0.10.4: Polling Status Resilience Hotfix
|
|
4
11
|
|
|
5
12
|
- `[Polling]` Status-bar updates from the polling loop are now best-effort and no longer crash the extension when a captured session context becomes stale after session reload. Failures are recorded as structured polling runtime events with `phase: "status-update"`. Impact: polling cleanup and retry status updates stay resilient without changing the Telegram API, config, or operator workflow.
|
package/index.ts
CHANGED
|
@@ -352,6 +352,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
352
352
|
inboundHandlerRuntime,
|
|
353
353
|
updateStatus,
|
|
354
354
|
dispatchNextQueuedTelegramTurn,
|
|
355
|
+
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
356
|
+
deferredQueueDispatchRuntime.request,
|
|
355
357
|
answerCallbackQuery,
|
|
356
358
|
editInteractiveMessage,
|
|
357
359
|
sendInteractiveMessage,
|
package/lib/commands.ts
CHANGED
|
@@ -317,6 +317,9 @@ export interface TelegramCompactCommandDeps extends TelegramRuntimeEventRecorder
|
|
|
317
317
|
setCompactionInProgress: (inProgress: boolean) => void;
|
|
318
318
|
updateStatus: () => void;
|
|
319
319
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
320
|
+
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
321
|
+
dispatch: () => void,
|
|
322
|
+
) => void;
|
|
320
323
|
compact: (callbacks: {
|
|
321
324
|
onComplete: () => void;
|
|
322
325
|
onError: (error: unknown) => void;
|
|
@@ -547,6 +550,9 @@ export interface TelegramCommandRuntimeDeps<
|
|
|
547
550
|
setCompactionInProgress: (inProgress: boolean) => void;
|
|
548
551
|
updateStatus: (ctx: TContext) => void;
|
|
549
552
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
553
|
+
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
554
|
+
dispatch: (ctx: TContext) => void,
|
|
555
|
+
) => void;
|
|
550
556
|
enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
551
557
|
compact: (
|
|
552
558
|
ctx: TContext,
|
|
@@ -758,6 +764,22 @@ export async function handleTelegramContinueCommand<TMessage, TContext>(
|
|
|
758
764
|
await deps.enqueueContinueTurn(message, ctx);
|
|
759
765
|
}
|
|
760
766
|
|
|
767
|
+
function dispatchNextQueuedTelegramTurnAfterCompact(
|
|
768
|
+
deps: Pick<
|
|
769
|
+
TelegramCompactCommandDeps,
|
|
770
|
+
| "dispatchNextQueuedTelegramTurn"
|
|
771
|
+
| "requestDeferredDispatchNextQueuedTelegramTurn"
|
|
772
|
+
>,
|
|
773
|
+
): void {
|
|
774
|
+
if (deps.requestDeferredDispatchNextQueuedTelegramTurn) {
|
|
775
|
+
deps.requestDeferredDispatchNextQueuedTelegramTurn(
|
|
776
|
+
deps.dispatchNextQueuedTelegramTurn,
|
|
777
|
+
);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
deps.dispatchNextQueuedTelegramTurn();
|
|
781
|
+
}
|
|
782
|
+
|
|
761
783
|
export async function handleTelegramCompactCommand(
|
|
762
784
|
deps: TelegramCompactCommandDeps,
|
|
763
785
|
): Promise<void> {
|
|
@@ -781,13 +803,13 @@ export async function handleTelegramCompactCommand(
|
|
|
781
803
|
onComplete: () => {
|
|
782
804
|
deps.setCompactionInProgress(false);
|
|
783
805
|
deps.updateStatus();
|
|
784
|
-
deps
|
|
806
|
+
dispatchNextQueuedTelegramTurnAfterCompact(deps);
|
|
785
807
|
void deps.sendTextReply("Compaction completed.");
|
|
786
808
|
},
|
|
787
809
|
onError: (error) => {
|
|
788
810
|
deps.setCompactionInProgress(false);
|
|
789
811
|
deps.updateStatus();
|
|
790
|
-
deps
|
|
812
|
+
dispatchNextQueuedTelegramTurnAfterCompact(deps);
|
|
791
813
|
deps.recordRuntimeEvent?.("compact", error);
|
|
792
814
|
const errorMessage = getTelegramCommandErrorMessage(error);
|
|
793
815
|
void deps.sendTextReply(`Compaction failed: ${errorMessage}`);
|
|
@@ -1069,6 +1091,13 @@ async function handleTelegramCommandRuntime<
|
|
|
1069
1091
|
updateStatus: updateStatusFor(commandCtx),
|
|
1070
1092
|
dispatchNextQueuedTelegramTurn: () =>
|
|
1071
1093
|
deps.dispatchNextQueuedTelegramTurn(commandCtx),
|
|
1094
|
+
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
1095
|
+
deps.requestDeferredDispatchNextQueuedTelegramTurn
|
|
1096
|
+
? (dispatch) =>
|
|
1097
|
+
deps.requestDeferredDispatchNextQueuedTelegramTurn?.(() =>
|
|
1098
|
+
dispatch(),
|
|
1099
|
+
)
|
|
1100
|
+
: undefined,
|
|
1072
1101
|
compact: (callbacks) => deps.compact(commandCtx, callbacks),
|
|
1073
1102
|
sendTextReply: sendReplyFor(nextMessage),
|
|
1074
1103
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
package/lib/routing.ts
CHANGED
|
@@ -79,6 +79,9 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
79
79
|
inboundHandlerRuntime: TelegramInboundHandlerRuntime<TContext>;
|
|
80
80
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
81
81
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
82
|
+
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
83
|
+
dispatch: (ctx: TContext) => void,
|
|
84
|
+
) => void;
|
|
82
85
|
answerCallbackQuery: (
|
|
83
86
|
callbackQueryId: string,
|
|
84
87
|
text?: string,
|
|
@@ -339,6 +342,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
339
342
|
deps.bridgeRuntime.lifecycle.setCompactionInProgress,
|
|
340
343
|
updateStatus: deps.updateStatus,
|
|
341
344
|
dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
|
|
345
|
+
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
346
|
+
deps.requestDeferredDispatchNextQueuedTelegramTurn,
|
|
342
347
|
enqueueContinueTurn,
|
|
343
348
|
compact: deps.compact,
|
|
344
349
|
allocateItemOrder: deps.bridgeRuntime.queue.allocateItemOrder,
|
package/lib/runtime.ts
CHANGED
|
@@ -328,6 +328,25 @@ export interface TelegramRuntimeEventRecorderPort {
|
|
|
328
328
|
) => void;
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
function updateTelegramRuntimeStatusSafely<TContext>(
|
|
332
|
+
updateStatus: (ctx: TContext, error?: string) => void,
|
|
333
|
+
ctx: TContext,
|
|
334
|
+
options: {
|
|
335
|
+
error?: string;
|
|
336
|
+
category: string;
|
|
337
|
+
phase: string;
|
|
338
|
+
recordRuntimeEvent?: TelegramRuntimeEventRecorderPort["recordRuntimeEvent"];
|
|
339
|
+
},
|
|
340
|
+
): void {
|
|
341
|
+
try {
|
|
342
|
+
updateStatus(ctx, options.error);
|
|
343
|
+
} catch (statusError) {
|
|
344
|
+
options.recordRuntimeEvent?.(options.category, statusError, {
|
|
345
|
+
phase: options.phase,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
331
350
|
export interface TelegramTypingLoopStarterDeps<
|
|
332
351
|
TContext,
|
|
333
352
|
> extends TelegramRuntimeEventRecorderPort {
|
|
@@ -351,7 +370,12 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
351
370
|
} catch (error) {
|
|
352
371
|
const message =
|
|
353
372
|
error instanceof Error ? error.message : String(error);
|
|
354
|
-
deps.updateStatus
|
|
373
|
+
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
374
|
+
error: message,
|
|
375
|
+
category: "typing",
|
|
376
|
+
phase: "status-update",
|
|
377
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
378
|
+
});
|
|
355
379
|
deps.recordRuntimeEvent?.("typing", error, {
|
|
356
380
|
chatId: targetChatId,
|
|
357
381
|
});
|
|
@@ -468,13 +492,22 @@ export function createTelegramPromptDispatchLifecycle<TContext>(
|
|
|
468
492
|
onPromptDispatchStart: (ctx: TContext, chatId?: number): void => {
|
|
469
493
|
deps.lifecycle.setDispatchPending(true);
|
|
470
494
|
deps.startTypingLoop(ctx, chatId);
|
|
471
|
-
deps.updateStatus
|
|
495
|
+
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
496
|
+
category: "dispatch",
|
|
497
|
+
phase: "status-update",
|
|
498
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
499
|
+
});
|
|
472
500
|
},
|
|
473
501
|
onPromptDispatchFailure: (ctx: TContext, message: string): void => {
|
|
474
502
|
deps.lifecycle.clearDispatchPending();
|
|
475
503
|
deps.typing.stop();
|
|
476
504
|
deps.recordRuntimeEvent?.("dispatch", new Error(message));
|
|
477
|
-
deps.updateStatus
|
|
505
|
+
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
506
|
+
error: `dispatch failed: ${message}`,
|
|
507
|
+
category: "dispatch",
|
|
508
|
+
phase: "status-update",
|
|
509
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
510
|
+
});
|
|
478
511
|
},
|
|
479
512
|
};
|
|
480
513
|
}
|
package/lib/text-groups.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Owns conservative delayed grouping for Telegram text messages that look like automatic long-message splits
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const TELEGRAM_TEXT_GROUP_DEBOUNCE_MS =
|
|
7
|
+
const TELEGRAM_TEXT_GROUP_DEBOUNCE_MS = 1000;
|
|
8
8
|
const TELEGRAM_TEXT_GROUP_MIN_SPLIT_LENGTH = 3600;
|
|
9
|
+
const TELEGRAM_TEXT_GROUP_MAX_MESSAGE_ID_GAP = 10;
|
|
9
10
|
|
|
10
11
|
export interface TelegramTextGroupMessage {
|
|
11
12
|
message_id: number;
|
|
@@ -91,7 +92,7 @@ function canAppendTelegramTextGroupMessage<
|
|
|
91
92
|
return (
|
|
92
93
|
!!previous &&
|
|
93
94
|
message.message_id > previous.message_id &&
|
|
94
|
-
message.message_id <= previous.message_id +
|
|
95
|
+
message.message_id <= previous.message_id + TELEGRAM_TEXT_GROUP_MAX_MESSAGE_ID_GAP &&
|
|
95
96
|
text.length > 0 &&
|
|
96
97
|
!isTelegramTextGroupCommand(text)
|
|
97
98
|
);
|