@llblab/pi-kit 0.21.2 → 0.22.1
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 +12 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-clean-room/CHANGELOG.md +5 -0
- package/node_modules/@llblab/pi-clean-room/README.md +4 -3
- package/node_modules/@llblab/pi-clean-room/index.ts +45 -1
- package/node_modules/@llblab/pi-clean-room/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +2 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +3 -8
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +1 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +0 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +1 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +17 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +5 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +12 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/model.js +49 -35
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +1 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +3 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.js +4 -55
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +36 -10
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +8 -8
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +2 -2
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +7 -4
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +0 -7
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1 -3
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +25 -4
- package/node_modules/@llblab/pi-telegram/lib/menu.ts +14 -9
- package/node_modules/@llblab/pi-telegram/lib/model.ts +77 -44
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +1 -8
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +0 -1
- package/node_modules/@llblab/pi-telegram/lib/runtime.ts +7 -58
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +43 -17
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import {
|
|
31
31
|
type MenuModel,
|
|
32
32
|
type ScopedTelegramModel,
|
|
33
|
+
type TelegramModelSwitchContinuationSource,
|
|
33
34
|
type ThinkingLevel,
|
|
34
35
|
} from "./model.ts";
|
|
35
36
|
import type { TelegramInputRichMessage } from "./telegram-api.ts";
|
|
@@ -194,7 +195,6 @@ export interface TelegramMenuCallbackRuntimeDeps<
|
|
|
194
195
|
text?: string,
|
|
195
196
|
) => Promise<void>;
|
|
196
197
|
isIdle: (ctx: TContext) => boolean;
|
|
197
|
-
hasActiveTelegramTurn: () => boolean;
|
|
198
198
|
hasAbortHandler: () => boolean;
|
|
199
199
|
hasActiveToolExecutions: () => boolean;
|
|
200
200
|
persistScopedModelPatterns?: (
|
|
@@ -206,10 +206,12 @@ export interface TelegramMenuCallbackRuntimeDeps<
|
|
|
206
206
|
stagePendingModelSwitch: (
|
|
207
207
|
selection: ScopedTelegramModel<TModel>,
|
|
208
208
|
ctx: TContext,
|
|
209
|
+
continuationTurn: TelegramModelSwitchContinuationSource,
|
|
209
210
|
) => void;
|
|
210
211
|
restartInterruptedTelegramTurn: (
|
|
211
212
|
selection: ScopedTelegramModel<TModel>,
|
|
212
213
|
ctx: TContext,
|
|
214
|
+
continuationTurn: TelegramModelSwitchContinuationSource,
|
|
213
215
|
) => Promise<boolean> | boolean;
|
|
214
216
|
sectionRegistry?: TelegramSectionRegistry;
|
|
215
217
|
editInteractiveMessage?: (
|
|
@@ -452,7 +454,6 @@ export interface TelegramMenuCallbackRuntimeAdapterDeps<
|
|
|
452
454
|
text?: string,
|
|
453
455
|
) => Promise<void>;
|
|
454
456
|
isIdle: (ctx: TContext) => boolean;
|
|
455
|
-
hasActiveTelegramTurn: () => boolean;
|
|
456
457
|
hasAbortHandler: () => boolean;
|
|
457
458
|
getActiveToolExecutions: () => number;
|
|
458
459
|
persistScopedModelPatterns?: (
|
|
@@ -464,10 +465,12 @@ export interface TelegramMenuCallbackRuntimeAdapterDeps<
|
|
|
464
465
|
stagePendingModelSwitch: (
|
|
465
466
|
selection: ScopedTelegramModel<TModel>,
|
|
466
467
|
ctx: TContext,
|
|
468
|
+
continuationTurn: TelegramModelSwitchContinuationSource,
|
|
467
469
|
) => void;
|
|
468
470
|
restartInterruptedTelegramTurn: (
|
|
469
471
|
selection: ScopedTelegramModel<TModel>,
|
|
470
472
|
ctx: TContext,
|
|
473
|
+
continuationTurn: TelegramModelSwitchContinuationSource,
|
|
471
474
|
) => Promise<boolean> | boolean;
|
|
472
475
|
sectionRegistry?: TelegramSectionRegistry;
|
|
473
476
|
editInteractiveMessage?: (
|
|
@@ -527,7 +530,6 @@ export function createTelegramMenuCallbackHandlerForContext<
|
|
|
527
530
|
updateSettingsMenuMessage: deps.updateSettingsMenuMessage,
|
|
528
531
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
529
532
|
isIdle: deps.isIdle,
|
|
530
|
-
hasActiveTelegramTurn: deps.hasActiveTelegramTurn,
|
|
531
533
|
hasAbortHandler: deps.hasAbortHandler,
|
|
532
534
|
hasActiveToolExecutions: () => deps.getActiveToolExecutions() > 0,
|
|
533
535
|
persistScopedModelPatterns: deps.persistScopedModelPatterns,
|
|
@@ -715,8 +717,7 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
715
717
|
activeModel: deps.getActiveModel(ctx),
|
|
716
718
|
currentThinkingLevel: deps.getThinkingLevel(),
|
|
717
719
|
isIdle: deps.isIdle(ctx),
|
|
718
|
-
canRestartBusyRun:
|
|
719
|
-
deps.hasActiveTelegramTurn() && deps.hasAbortHandler(),
|
|
720
|
+
canRestartBusyRun: deps.hasAbortHandler(),
|
|
720
721
|
hasActiveToolExecutions: deps.hasActiveToolExecutions(),
|
|
721
722
|
},
|
|
722
723
|
{
|
|
@@ -733,11 +734,15 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
733
734
|
deps.setThinkingLevel(level);
|
|
734
735
|
deps.updateStatus(ctx);
|
|
735
736
|
},
|
|
736
|
-
stagePendingModelSwitch: (selection) => {
|
|
737
|
-
deps.stagePendingModelSwitch(selection, ctx);
|
|
737
|
+
stagePendingModelSwitch: (selection, continuationTurn) => {
|
|
738
|
+
deps.stagePendingModelSwitch(selection, ctx, continuationTurn);
|
|
738
739
|
},
|
|
739
|
-
restartInterruptedTelegramTurn: (selection) =>
|
|
740
|
-
deps.restartInterruptedTelegramTurn(
|
|
740
|
+
restartInterruptedTelegramTurn: (selection, continuationTurn) =>
|
|
741
|
+
deps.restartInterruptedTelegramTurn(
|
|
742
|
+
selection,
|
|
743
|
+
ctx,
|
|
744
|
+
continuationTurn,
|
|
745
|
+
),
|
|
741
746
|
},
|
|
742
747
|
);
|
|
743
748
|
} catch (error) {
|
|
@@ -374,7 +374,6 @@ export interface PendingModelSwitchStore<TSelection> {
|
|
|
374
374
|
|
|
375
375
|
export interface TelegramInFlightModelSwitchState {
|
|
376
376
|
isIdle: boolean;
|
|
377
|
-
hasActiveTelegramTurn: boolean;
|
|
378
377
|
hasAbortHandler: boolean;
|
|
379
378
|
}
|
|
380
379
|
|
|
@@ -394,21 +393,21 @@ export function createPendingModelSwitchStore<
|
|
|
394
393
|
};
|
|
395
394
|
}
|
|
396
395
|
|
|
397
|
-
export function
|
|
396
|
+
export function canRestartAgentRunForTelegramModelSwitch(
|
|
398
397
|
state: TelegramInFlightModelSwitchState,
|
|
399
398
|
): boolean {
|
|
400
|
-
return !state.isIdle && state.
|
|
399
|
+
return !state.isIdle && state.hasAbortHandler;
|
|
401
400
|
}
|
|
402
401
|
|
|
403
402
|
export function shouldTriggerPendingTelegramModelSwitchAbort(state: {
|
|
404
403
|
hasPendingModelSwitch: boolean;
|
|
405
|
-
|
|
404
|
+
hasContinuationTurn: boolean;
|
|
406
405
|
hasAbortHandler: boolean;
|
|
407
406
|
activeToolExecutions: number;
|
|
408
407
|
}): boolean {
|
|
409
408
|
return (
|
|
410
409
|
state.hasPendingModelSwitch &&
|
|
411
|
-
state.
|
|
410
|
+
state.hasContinuationTurn &&
|
|
412
411
|
state.hasAbortHandler &&
|
|
413
412
|
state.activeToolExecutions === 0
|
|
414
413
|
);
|
|
@@ -458,13 +457,18 @@ export function buildTelegramModelSwitchContinuationText<
|
|
|
458
457
|
const thinkingSuffix = thinkingLevel
|
|
459
458
|
? ` Keep the selected thinking level (${thinkingLevel}) if it still applies.`
|
|
460
459
|
: "";
|
|
461
|
-
return `${telegramPrefix} Continue the interrupted previous
|
|
460
|
+
return `${telegramPrefix} Continue the interrupted previous request using the newly selected model (${modelLabel}). Resume from the last unfinished step instead of restarting from scratch unless necessary.${thinkingSuffix}`;
|
|
462
461
|
}
|
|
463
462
|
|
|
463
|
+
export type TelegramModelSwitchContinuationSource = Pick<
|
|
464
|
+
PendingTelegramTurn,
|
|
465
|
+
"chatId" | "replyToMessageId" | "target"
|
|
466
|
+
>;
|
|
467
|
+
|
|
464
468
|
export function buildTelegramModelSwitchContinuationTurn<
|
|
465
469
|
TModel extends MenuModel,
|
|
466
470
|
>(options: {
|
|
467
|
-
turn:
|
|
471
|
+
turn: TelegramModelSwitchContinuationSource;
|
|
468
472
|
selection: ScopedTelegramModel<TModel>;
|
|
469
473
|
telegramPrefix?: string;
|
|
470
474
|
queueOrder: number;
|
|
@@ -494,7 +498,7 @@ export function buildTelegramModelSwitchContinuationTurn<
|
|
|
494
498
|
),
|
|
495
499
|
},
|
|
496
500
|
],
|
|
497
|
-
historyText: `Continue interrupted
|
|
501
|
+
historyText: `Continue interrupted request on ${modelLabel}`,
|
|
498
502
|
statusSummary: `↻ ${statusLabel || "continue"}`,
|
|
499
503
|
};
|
|
500
504
|
}
|
|
@@ -506,7 +510,7 @@ export function createTelegramModelSwitchContinuationTurnBuilder<
|
|
|
506
510
|
allocateItemOrder: () => number;
|
|
507
511
|
allocateControlOrder: () => number;
|
|
508
512
|
}): (options: {
|
|
509
|
-
turn:
|
|
513
|
+
turn: TelegramModelSwitchContinuationSource;
|
|
510
514
|
selection: ScopedTelegramModel<TModel>;
|
|
511
515
|
}) => PendingTelegramTurn {
|
|
512
516
|
return (options) =>
|
|
@@ -523,11 +527,15 @@ export function createTelegramModelSwitchContinuationQueue<
|
|
|
523
527
|
TSelection extends ScopedTelegramModel,
|
|
524
528
|
>(deps: {
|
|
525
529
|
createContinuationTurn: (options: {
|
|
526
|
-
turn:
|
|
530
|
+
turn: TelegramModelSwitchContinuationSource;
|
|
527
531
|
selection: TSelection;
|
|
528
532
|
}) => PendingTelegramTurn;
|
|
529
533
|
appendQueuedItem: (item: PendingTelegramTurn, ctx: TContext) => void;
|
|
530
|
-
}): (
|
|
534
|
+
}): (
|
|
535
|
+
turn: TelegramModelSwitchContinuationSource,
|
|
536
|
+
selection: TSelection,
|
|
537
|
+
ctx: TContext,
|
|
538
|
+
) => void {
|
|
531
539
|
return (turn, selection, ctx) => {
|
|
532
540
|
deps.appendQueuedItem(
|
|
533
541
|
deps.createContinuationTurn({ turn, selection }),
|
|
@@ -544,7 +552,11 @@ export function createTelegramModelSwitchContinuationQueueRuntime<
|
|
|
544
552
|
allocateItemOrder: () => number;
|
|
545
553
|
allocateControlOrder: () => number;
|
|
546
554
|
appendQueuedItem: (item: PendingTelegramTurn, ctx: TContext) => void;
|
|
547
|
-
}): (
|
|
555
|
+
}): (
|
|
556
|
+
turn: TelegramModelSwitchContinuationSource,
|
|
557
|
+
selection: TSelection,
|
|
558
|
+
ctx: TContext,
|
|
559
|
+
) => void {
|
|
548
560
|
return createTelegramModelSwitchContinuationQueue<TContext, TSelection>({
|
|
549
561
|
createContinuationTurn: createTelegramModelSwitchContinuationTurnBuilder({
|
|
550
562
|
telegramPrefix: deps.telegramPrefix,
|
|
@@ -564,7 +576,7 @@ export interface TelegramModelSwitchControllerDeps<TContext, TSelection> {
|
|
|
564
576
|
hasAbortHandler: () => boolean;
|
|
565
577
|
getActiveToolExecutions: () => number;
|
|
566
578
|
queueContinuation: (
|
|
567
|
-
turn:
|
|
579
|
+
turn: TelegramModelSwitchContinuationSource,
|
|
568
580
|
selection: TSelection,
|
|
569
581
|
ctx: TContext,
|
|
570
582
|
) => void;
|
|
@@ -573,15 +585,23 @@ export interface TelegramModelSwitchControllerDeps<TContext, TSelection> {
|
|
|
573
585
|
|
|
574
586
|
export interface TelegramModelSwitchController<TContext, TSelection> {
|
|
575
587
|
canOfferInFlightSwitch: (ctx: TContext) => boolean;
|
|
576
|
-
stagePendingSwitch: (
|
|
588
|
+
stagePendingSwitch: (
|
|
589
|
+
selection: TSelection,
|
|
590
|
+
ctx: TContext,
|
|
591
|
+
continuationTurn?: TelegramModelSwitchContinuationSource,
|
|
592
|
+
) => void;
|
|
577
593
|
clearPendingSwitch: () => void;
|
|
578
594
|
queueContinuation: (
|
|
579
|
-
turn:
|
|
595
|
+
turn: TelegramModelSwitchContinuationSource,
|
|
580
596
|
selection: TSelection,
|
|
581
597
|
ctx: TContext,
|
|
582
598
|
) => void;
|
|
583
599
|
triggerPendingAbort: (ctx: TContext) => boolean;
|
|
584
|
-
restartInterruptedTurn: (
|
|
600
|
+
restartInterruptedTurn: (
|
|
601
|
+
selection: TSelection,
|
|
602
|
+
ctx: TContext,
|
|
603
|
+
continuationTurn?: TelegramModelSwitchContinuationSource,
|
|
604
|
+
) => boolean;
|
|
585
605
|
}
|
|
586
606
|
|
|
587
607
|
export interface TelegramModelSwitchControllerRuntimeDeps<
|
|
@@ -624,49 +644,62 @@ export function createTelegramModelSwitchControllerRuntime<
|
|
|
624
644
|
export function createTelegramModelSwitchController<TContext, TSelection>(
|
|
625
645
|
deps: TelegramModelSwitchControllerDeps<TContext, TSelection>,
|
|
626
646
|
): TelegramModelSwitchController<TContext, TSelection> {
|
|
647
|
+
let pendingContinuationTurn:
|
|
648
|
+
| TelegramModelSwitchContinuationSource
|
|
649
|
+
| undefined;
|
|
650
|
+
const triggerPendingAbort = (ctx: TContext): boolean => {
|
|
651
|
+
const turn = pendingContinuationTurn ?? deps.getActiveTurn();
|
|
652
|
+
if (
|
|
653
|
+
!shouldTriggerPendingTelegramModelSwitchAbort({
|
|
654
|
+
hasPendingModelSwitch: !!deps.getPendingModelSwitch(),
|
|
655
|
+
hasContinuationTurn: !!turn,
|
|
656
|
+
hasAbortHandler: deps.hasAbortHandler(),
|
|
657
|
+
activeToolExecutions: deps.getActiveToolExecutions(),
|
|
658
|
+
})
|
|
659
|
+
) {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
const selection = deps.getPendingModelSwitch();
|
|
663
|
+
const abort = deps.getAbortHandler();
|
|
664
|
+
if (!selection || !turn || !abort) return false;
|
|
665
|
+
pendingContinuationTurn = undefined;
|
|
666
|
+
deps.setPendingModelSwitch(undefined);
|
|
667
|
+
deps.queueContinuation(turn, selection, ctx);
|
|
668
|
+
abort();
|
|
669
|
+
return true;
|
|
670
|
+
};
|
|
627
671
|
return {
|
|
628
672
|
canOfferInFlightSwitch: (ctx) =>
|
|
629
|
-
|
|
673
|
+
canRestartAgentRunForTelegramModelSwitch({
|
|
630
674
|
isIdle: deps.isIdle(ctx),
|
|
631
|
-
hasActiveTelegramTurn: !!deps.getActiveTurn(),
|
|
632
675
|
hasAbortHandler: deps.hasAbortHandler(),
|
|
633
676
|
}),
|
|
634
|
-
stagePendingSwitch: (selection, ctx) => {
|
|
677
|
+
stagePendingSwitch: (selection, ctx, continuationTurn) => {
|
|
678
|
+
pendingContinuationTurn = deps.getActiveTurn() ?? continuationTurn;
|
|
635
679
|
deps.setPendingModelSwitch(selection);
|
|
636
|
-
|
|
680
|
+
try {
|
|
681
|
+
deps.updateStatus(ctx);
|
|
682
|
+
} finally {
|
|
683
|
+
triggerPendingAbort(ctx);
|
|
684
|
+
}
|
|
637
685
|
},
|
|
638
686
|
clearPendingSwitch: () => {
|
|
687
|
+
pendingContinuationTurn = undefined;
|
|
639
688
|
deps.setPendingModelSwitch(undefined);
|
|
640
689
|
},
|
|
641
690
|
queueContinuation: deps.queueContinuation,
|
|
642
|
-
triggerPendingAbort
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
hasActiveTelegramTurn: !!deps.getActiveTurn(),
|
|
647
|
-
hasAbortHandler: deps.hasAbortHandler(),
|
|
648
|
-
activeToolExecutions: deps.getActiveToolExecutions(),
|
|
649
|
-
})
|
|
650
|
-
) {
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
const selection = deps.getPendingModelSwitch();
|
|
654
|
-
const turn = deps.getActiveTurn();
|
|
655
|
-
const abort = deps.getAbortHandler();
|
|
656
|
-
if (!selection || !turn || !abort) return false;
|
|
657
|
-
deps.setPendingModelSwitch(undefined);
|
|
658
|
-
deps.queueContinuation(turn, selection, ctx);
|
|
659
|
-
abort();
|
|
660
|
-
return true;
|
|
661
|
-
},
|
|
662
|
-
restartInterruptedTurn: (selection, ctx) =>
|
|
663
|
-
restartTelegramModelSwitchContinuation({
|
|
664
|
-
activeTurn: deps.getActiveTurn(),
|
|
691
|
+
triggerPendingAbort,
|
|
692
|
+
restartInterruptedTurn: (selection, ctx, continuationTurn) => {
|
|
693
|
+
const restarted = restartTelegramModelSwitchContinuation({
|
|
694
|
+
activeTurn: deps.getActiveTurn() ?? continuationTurn,
|
|
665
695
|
abort: deps.getAbortHandler(),
|
|
666
696
|
selection,
|
|
667
697
|
queueContinuation: (turn, nextSelection) => {
|
|
668
698
|
deps.queueContinuation(turn, nextSelection, ctx);
|
|
669
699
|
},
|
|
670
|
-
})
|
|
700
|
+
});
|
|
701
|
+
if (restarted) pendingContinuationTurn = undefined;
|
|
702
|
+
return restarted;
|
|
703
|
+
},
|
|
671
704
|
};
|
|
672
705
|
}
|
|
@@ -1253,7 +1253,6 @@ export interface TelegramAgentStartHookRuntimeDeps<
|
|
|
1253
1253
|
export type TelegramAgentStartHookEvent = unknown;
|
|
1254
1254
|
|
|
1255
1255
|
export interface TelegramToolExecutionRuntimeDeps {
|
|
1256
|
-
hasActiveTurn: () => boolean;
|
|
1257
1256
|
getActiveToolExecutions: () => number;
|
|
1258
1257
|
setActiveToolExecutions: (count: number) => void;
|
|
1259
1258
|
}
|
|
@@ -1361,11 +1360,9 @@ export function createTelegramAgentStartHook<
|
|
|
1361
1360
|
}
|
|
1362
1361
|
|
|
1363
1362
|
export function getNextTelegramToolExecutionCount(options: {
|
|
1364
|
-
hasActiveTurn: boolean;
|
|
1365
1363
|
currentCount: number;
|
|
1366
1364
|
event: "start" | "end";
|
|
1367
1365
|
}): number {
|
|
1368
|
-
if (!options.hasActiveTurn) return options.currentCount;
|
|
1369
1366
|
if (options.event === "start") {
|
|
1370
1367
|
return options.currentCount + 1;
|
|
1371
1368
|
}
|
|
@@ -1377,7 +1374,6 @@ export function handleTelegramToolExecutionStartRuntime(
|
|
|
1377
1374
|
): void {
|
|
1378
1375
|
deps.setActiveToolExecutions(
|
|
1379
1376
|
getNextTelegramToolExecutionCount({
|
|
1380
|
-
hasActiveTurn: deps.hasActiveTurn(),
|
|
1381
1377
|
currentCount: deps.getActiveToolExecutions(),
|
|
1382
1378
|
event: "start",
|
|
1383
1379
|
}),
|
|
@@ -1387,15 +1383,13 @@ export function handleTelegramToolExecutionStartRuntime(
|
|
|
1387
1383
|
export function handleTelegramToolExecutionEndRuntime(
|
|
1388
1384
|
deps: TelegramToolExecutionEndRuntimeDeps,
|
|
1389
1385
|
): void {
|
|
1390
|
-
const hasActiveTurn = deps.hasActiveTurn();
|
|
1391
1386
|
deps.setActiveToolExecutions(
|
|
1392
1387
|
getNextTelegramToolExecutionCount({
|
|
1393
|
-
hasActiveTurn,
|
|
1394
1388
|
currentCount: deps.getActiveToolExecutions(),
|
|
1395
1389
|
event: "end",
|
|
1396
1390
|
}),
|
|
1397
1391
|
);
|
|
1398
|
-
|
|
1392
|
+
deps.triggerPendingModelSwitchAbort();
|
|
1399
1393
|
}
|
|
1400
1394
|
|
|
1401
1395
|
export type TelegramAgentLifecycleHooksRuntimeDeps<
|
|
@@ -1485,7 +1479,6 @@ export function createTelegramToolExecutionHooks<TContext>(
|
|
|
1485
1479
|
ctx: TContext,
|
|
1486
1480
|
): void => {
|
|
1487
1481
|
handleTelegramToolExecutionEndRuntime({
|
|
1488
|
-
hasActiveTurn: deps.hasActiveTurn,
|
|
1489
1482
|
getActiveToolExecutions: deps.getActiveToolExecutions,
|
|
1490
1483
|
setActiveToolExecutions: deps.setActiveToolExecutions,
|
|
1491
1484
|
triggerPendingModelSwitchAbort: () => {
|
|
@@ -1010,7 +1010,6 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1010
1010
|
updateSettingsMenuMessage: deps.updateSettingsMenuMessage,
|
|
1011
1011
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
1012
1012
|
isIdle: deps.isIdle,
|
|
1013
|
-
hasActiveTelegramTurn: deps.activeTurnRuntime.has,
|
|
1014
1013
|
hasAbortHandler: deps.bridgeRuntime.abort.hasHandler,
|
|
1015
1014
|
getActiveToolExecutions:
|
|
1016
1015
|
deps.bridgeRuntime.lifecycle.getActiveToolExecutions,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Owns small session-local runtime primitives that are shared by orchestration but are not specific to queueing, rendering, polling, or Telegram transport
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const TELEGRAM_TYPING_ACTION_INTERVAL_MS =
|
|
7
|
+
const TELEGRAM_TYPING_ACTION_INTERVAL_MS = 3_000;
|
|
8
8
|
const TELEGRAM_TYPING_IDLE_DRAIN_MAX_MS = 250;
|
|
9
9
|
|
|
10
10
|
export interface TelegramRuntimeQueueCounters {
|
|
@@ -319,7 +319,6 @@ export interface TelegramTypingLoopDeps {
|
|
|
319
319
|
chatId: number,
|
|
320
320
|
options?: { message_thread_id?: number },
|
|
321
321
|
) => Promise<unknown>;
|
|
322
|
-
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
|
323
322
|
shouldContinue?: () => boolean;
|
|
324
323
|
onStopped?: () => void;
|
|
325
324
|
}
|
|
@@ -364,7 +363,6 @@ export interface TelegramTypingLoopStarterDeps<
|
|
|
364
363
|
chatId: number,
|
|
365
364
|
options?: { message_thread_id?: number },
|
|
366
365
|
) => Promise<unknown>;
|
|
367
|
-
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
|
368
366
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
369
367
|
isContextActive?: (ctx: TContext) => boolean;
|
|
370
368
|
isTransportAvailable?: () => boolean;
|
|
@@ -378,7 +376,7 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
378
376
|
ctx: TContext,
|
|
379
377
|
chatId?: number,
|
|
380
378
|
options?: { target?: TelegramTypingLoopTarget },
|
|
381
|
-
) =>
|
|
379
|
+
) => boolean {
|
|
382
380
|
return (ctx, chatId, options) => {
|
|
383
381
|
const transportAuthority = deps.getTransportAuthority?.();
|
|
384
382
|
const hasTransport = (): boolean =>
|
|
@@ -386,9 +384,9 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
386
384
|
? transportAuthority !== undefined &&
|
|
387
385
|
Object.is(deps.getTransportAuthority(), transportAuthority)
|
|
388
386
|
: deps.isTransportAvailable?.() !== false;
|
|
389
|
-
if (!hasTransport()) return;
|
|
387
|
+
if (!hasTransport()) return false;
|
|
390
388
|
let active = true;
|
|
391
|
-
deps.typing.start({
|
|
389
|
+
return deps.typing.start({
|
|
392
390
|
chatId: chatId ?? deps.getDefaultChatId(),
|
|
393
391
|
target: options?.target,
|
|
394
392
|
intervalMs: deps.intervalMs ?? TELEGRAM_TYPING_ACTION_INTERVAL_MS,
|
|
@@ -407,14 +405,6 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
407
405
|
deps.typing.stop();
|
|
408
406
|
return;
|
|
409
407
|
}
|
|
410
|
-
const message =
|
|
411
|
-
error instanceof Error ? error.message : String(error);
|
|
412
|
-
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
413
|
-
error: message,
|
|
414
|
-
category: "typing",
|
|
415
|
-
phase: "status-update",
|
|
416
|
-
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
417
|
-
});
|
|
418
408
|
try {
|
|
419
409
|
deps.recordRuntimeEvent?.("typing", error, {
|
|
420
410
|
chatId: targetChatId,
|
|
@@ -424,41 +414,6 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
424
414
|
}
|
|
425
415
|
}
|
|
426
416
|
},
|
|
427
|
-
sendAggregateTypingAction: deps.sendAggregateTypingAction
|
|
428
|
-
? async (targetChatId) => {
|
|
429
|
-
if (!active) return;
|
|
430
|
-
if (!hasTransport()) {
|
|
431
|
-
deps.typing.stop();
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
try {
|
|
435
|
-
await deps.sendAggregateTypingAction?.(targetChatId);
|
|
436
|
-
} catch (error) {
|
|
437
|
-
if (deps.isContextActive?.(ctx) === false) return;
|
|
438
|
-
if (!active) return;
|
|
439
|
-
if (!hasTransport()) {
|
|
440
|
-
deps.typing.stop();
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
const message =
|
|
444
|
-
error instanceof Error ? error.message : String(error);
|
|
445
|
-
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
446
|
-
error: message,
|
|
447
|
-
category: "typing",
|
|
448
|
-
phase: "status-update",
|
|
449
|
-
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
450
|
-
});
|
|
451
|
-
try {
|
|
452
|
-
deps.recordRuntimeEvent?.("typing", error, {
|
|
453
|
-
chatId: targetChatId,
|
|
454
|
-
aggregate: true,
|
|
455
|
-
});
|
|
456
|
-
} catch {
|
|
457
|
-
// Typing diagnostics cannot escape the in-flight action owner.
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
: undefined,
|
|
462
417
|
shouldContinue: hasTransport,
|
|
463
418
|
onStopped: () => {
|
|
464
419
|
active = false;
|
|
@@ -502,12 +457,7 @@ export function startTelegramTypingLoop(
|
|
|
502
457
|
const targetChatId = activeDeps.chatId;
|
|
503
458
|
const threadParams = getTelegramTypingLoopThreadParams(activeDeps.target);
|
|
504
459
|
const typing = Promise.resolve()
|
|
505
|
-
.then(
|
|
506
|
-
await activeDeps.sendTypingAction(targetChatId, threadParams);
|
|
507
|
-
if (threadParams?.message_thread_id !== undefined) {
|
|
508
|
-
await activeDeps.sendAggregateTypingAction?.(targetChatId);
|
|
509
|
-
}
|
|
510
|
-
})
|
|
460
|
+
.then(() => activeDeps.sendTypingAction(targetChatId, threadParams))
|
|
511
461
|
.then(() => undefined)
|
|
512
462
|
.catch(() => undefined);
|
|
513
463
|
state.typingInFlight = typing;
|
|
@@ -602,7 +552,7 @@ export interface TelegramPromptDispatchLifecycleDeps<
|
|
|
602
552
|
ctx: TContext,
|
|
603
553
|
chatId?: number,
|
|
604
554
|
options?: { target?: TelegramTypingLoopTarget },
|
|
605
|
-
) => void;
|
|
555
|
+
) => boolean | void;
|
|
606
556
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
607
557
|
}
|
|
608
558
|
|
|
@@ -616,7 +566,6 @@ export interface TelegramPromptDispatchRuntimeDeps<
|
|
|
616
566
|
chatId: number,
|
|
617
567
|
options?: { message_thread_id?: number },
|
|
618
568
|
) => Promise<unknown>;
|
|
619
|
-
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
|
620
569
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
621
570
|
isContextActive?: (ctx: TContext) => boolean;
|
|
622
571
|
isTransportAvailable?: () => boolean;
|
|
@@ -629,7 +578,7 @@ export interface TelegramPromptDispatchRuntime<TContext> {
|
|
|
629
578
|
ctx: TContext,
|
|
630
579
|
chatId?: number,
|
|
631
580
|
options?: { target?: TelegramTypingLoopTarget },
|
|
632
|
-
) => void;
|
|
581
|
+
) => boolean | void;
|
|
633
582
|
onPromptDispatchStart: (ctx: TContext, chatId?: number) => void;
|
|
634
583
|
onPromptDispatchFailure: (ctx: TContext, message: string) => void;
|
|
635
584
|
}
|
|
@@ -1866,14 +1866,16 @@ export function createTelegramBridgeApiRuntime(
|
|
|
1866
1866
|
deps.chatActionMinIntervalMs ?? 2_000,
|
|
1867
1867
|
);
|
|
1868
1868
|
const chatActionMaxGates = Math.max(1, deps.chatActionMaxGates ?? 256);
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
const
|
|
1869
|
+
type ChatActionGate = {
|
|
1870
|
+
inFlight?: Promise<unknown>;
|
|
1871
|
+
notBeforeMs: number;
|
|
1872
|
+
};
|
|
1873
|
+
const chatActionGates = new Map<string, ChatActionGate>();
|
|
1874
|
+
const chatActionChatGates = new Map<string, ChatActionGate>();
|
|
1875
|
+
const getChatActionKeys = (
|
|
1874
1876
|
method: string,
|
|
1875
1877
|
body: Record<string, unknown>,
|
|
1876
|
-
): string | undefined => {
|
|
1878
|
+
): { action: string; chat: string } | undefined => {
|
|
1877
1879
|
if (method !== "sendChatAction") return undefined;
|
|
1878
1880
|
const chatId = body.chat_id;
|
|
1879
1881
|
const action = body.action;
|
|
@@ -1883,12 +1885,16 @@ export function createTelegramBridgeApiRuntime(
|
|
|
1883
1885
|
) {
|
|
1884
1886
|
return undefined;
|
|
1885
1887
|
}
|
|
1888
|
+
const chat = String(chatId);
|
|
1886
1889
|
const threadId = body.message_thread_id;
|
|
1887
|
-
return
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1890
|
+
return {
|
|
1891
|
+
action: `${chat}:${
|
|
1892
|
+
typeof threadId === "number" || typeof threadId === "string"
|
|
1893
|
+
? String(threadId)
|
|
1894
|
+
: "all"
|
|
1895
|
+
}:${action}`,
|
|
1896
|
+
chat,
|
|
1897
|
+
};
|
|
1892
1898
|
};
|
|
1893
1899
|
const callRecorded = async <TResponse>(
|
|
1894
1900
|
method: string,
|
|
@@ -1896,22 +1902,38 @@ export function createTelegramBridgeApiRuntime(
|
|
|
1896
1902
|
options?: TelegramApiCallOptions,
|
|
1897
1903
|
): Promise<TResponse> => {
|
|
1898
1904
|
const recoverError = deps.captureRequestErrorHandler?.(body);
|
|
1899
|
-
const
|
|
1900
|
-
if (
|
|
1905
|
+
const chatActionKeys = getChatActionKeys(method, body);
|
|
1906
|
+
if (chatActionKeys) {
|
|
1901
1907
|
const nowMs = now();
|
|
1902
1908
|
for (const [key, candidate] of chatActionGates) {
|
|
1903
1909
|
if (!candidate.inFlight && nowMs >= candidate.notBeforeMs) {
|
|
1904
1910
|
chatActionGates.delete(key);
|
|
1905
1911
|
}
|
|
1906
1912
|
}
|
|
1907
|
-
|
|
1913
|
+
for (const [key, candidate] of chatActionChatGates) {
|
|
1914
|
+
if (!candidate.inFlight && nowMs >= candidate.notBeforeMs) {
|
|
1915
|
+
chatActionChatGates.delete(key);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
let gate = chatActionGates.get(chatActionKeys.action);
|
|
1908
1919
|
if (!gate) {
|
|
1909
1920
|
if (chatActionGates.size >= chatActionMaxGates) return true as TResponse;
|
|
1910
1921
|
gate = { notBeforeMs: 0 };
|
|
1911
|
-
chatActionGates.set(
|
|
1922
|
+
chatActionGates.set(chatActionKeys.action, gate);
|
|
1923
|
+
}
|
|
1924
|
+
let chatGate = chatActionChatGates.get(chatActionKeys.chat);
|
|
1925
|
+
if (!chatGate) {
|
|
1926
|
+
if (chatActionChatGates.size >= chatActionMaxGates) {
|
|
1927
|
+
return true as TResponse;
|
|
1928
|
+
}
|
|
1929
|
+
chatGate = { notBeforeMs: 0 };
|
|
1930
|
+
chatActionChatGates.set(chatActionKeys.chat, chatGate);
|
|
1912
1931
|
}
|
|
1913
1932
|
if (gate.inFlight) return (await gate.inFlight) as TResponse;
|
|
1914
|
-
if (
|
|
1933
|
+
if (chatGate.inFlight) return true as TResponse;
|
|
1934
|
+
if (nowMs < gate.notBeforeMs || nowMs < chatGate.notBeforeMs) {
|
|
1935
|
+
return true as TResponse;
|
|
1936
|
+
}
|
|
1915
1937
|
let request: Promise<TResponse>;
|
|
1916
1938
|
request = Promise.resolve()
|
|
1917
1939
|
.then(() =>
|
|
@@ -1931,7 +1953,9 @@ export function createTelegramBridgeApiRuntime(
|
|
|
1931
1953
|
chatActionMinIntervalMs,
|
|
1932
1954
|
(error.retryAfterSeconds ?? 0) * 1_000,
|
|
1933
1955
|
);
|
|
1934
|
-
|
|
1956
|
+
const notBeforeMs = now() + retryAfterMs;
|
|
1957
|
+
gate.notBeforeMs = notBeforeMs;
|
|
1958
|
+
chatGate.notBeforeMs = Math.max(chatGate.notBeforeMs, notBeforeMs);
|
|
1935
1959
|
deps.recordRuntimeEvent(
|
|
1936
1960
|
"api",
|
|
1937
1961
|
error,
|
|
@@ -1952,8 +1976,10 @@ export function createTelegramBridgeApiRuntime(
|
|
|
1952
1976
|
})
|
|
1953
1977
|
.finally(() => {
|
|
1954
1978
|
if (gate.inFlight === request) gate.inFlight = undefined;
|
|
1979
|
+
if (chatGate.inFlight === request) chatGate.inFlight = undefined;
|
|
1955
1980
|
});
|
|
1956
1981
|
gate.inFlight = request;
|
|
1982
|
+
chatGate.inFlight = request;
|
|
1957
1983
|
return request;
|
|
1958
1984
|
}
|
|
1959
1985
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@llblab/pi-actors": "0.53.0",
|
|
44
|
-
"@llblab/pi-clean-room": "0.
|
|
44
|
+
"@llblab/pi-clean-room": "0.2.0",
|
|
45
45
|
"@llblab/pi-codex-usage": "0.10.0",
|
|
46
46
|
"@llblab/pi-grow-loop": "0.8.1",
|
|
47
47
|
"@llblab/pi-state-flow": "0.18.1",
|
|
48
|
-
"@llblab/pi-telegram": "0.51.
|
|
48
|
+
"@llblab/pi-telegram": "0.51.2",
|
|
49
49
|
"@llblab/skills": "1.15.0"
|
|
50
50
|
},
|
|
51
51
|
"bundledDependencies": [
|