@llblab/pi-telegram 0.54.3 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -2
- package/BACKLOG.md +1 -1
- package/CHANGELOG.md +21 -0
- package/README.md +5 -3
- package/dist/lib/activity.d.ts +5 -1
- package/dist/lib/activity.js +31 -2
- package/dist/lib/bindings.d.ts +5 -1
- package/dist/lib/bindings.js +9 -2
- package/dist/lib/bus-api.d.ts +2 -2
- package/dist/lib/bus-api.js +7 -2
- package/dist/lib/bus-follower.d.ts +49 -3
- package/dist/lib/bus-follower.js +313 -146
- package/dist/lib/bus-leader.d.ts +3 -0
- package/dist/lib/bus-leader.js +99 -18
- package/dist/lib/bus.d.ts +5 -3
- package/dist/lib/bus.js +13 -10
- package/dist/lib/delivery.d.ts +4 -0
- package/dist/lib/delivery.js +48 -15
- package/dist/lib/extension.js +56 -6
- package/dist/lib/lifecycle.d.ts +68 -0
- package/dist/lib/lifecycle.js +178 -58
- package/dist/lib/machine.d.ts +23 -0
- package/dist/lib/machine.js +37 -0
- package/dist/lib/media.d.ts +114 -28
- package/dist/lib/media.js +292 -119
- package/dist/lib/model.d.ts +54 -15
- package/dist/lib/model.js +98 -53
- package/dist/lib/polling.js +2 -0
- package/dist/lib/preview.d.ts +128 -113
- package/dist/lib/preview.js +448 -366
- package/dist/lib/queue.d.ts +2 -2
- package/dist/lib/queue.js +3 -3
- package/dist/lib/routing.d.ts +14 -2
- package/dist/lib/routing.js +546 -207
- package/dist/lib/runtime.d.ts +77 -11
- package/dist/lib/runtime.js +174 -92
- package/dist/lib/sections.js +0 -1
- package/dist/lib/telegram-api.d.ts +11 -9
- package/dist/lib/telegram-api.js +53 -4
- package/dist/lib/text-groups.d.ts +0 -26
- package/dist/lib/text-groups.js +51 -98
- package/dist/lib/thread-display.js +14 -6
- package/dist/lib/threads.d.ts +20 -5
- package/dist/lib/threads.js +111 -32
- package/dist/lib/turns.d.ts +2 -0
- package/dist/lib/turns.js +6 -1
- package/dist/lib/updates.d.ts +11 -4
- package/dist/lib/updates.js +54 -19
- package/dist/package.json +1 -1
- package/docs/activity.md +1 -1
- package/docs/architecture.md +16 -15
- package/docs/delivery.md +7 -5
- package/docs/multi-instance-bus.md +18 -8
- package/docs/public-api.md +2 -2
- package/docs/ui-style.md +8 -6
- package/lib/activity.ts +29 -5
- package/lib/bindings.ts +13 -1
- package/lib/bus-api.ts +11 -3
- package/lib/bus-follower.ts +417 -203
- package/lib/bus-leader.ts +88 -14
- package/lib/bus.ts +17 -15
- package/lib/delivery.ts +47 -19
- package/lib/extension.ts +59 -9
- package/lib/lifecycle.ts +229 -55
- package/lib/machine.ts +60 -0
- package/lib/media.ts +438 -172
- package/lib/model.ts +169 -74
- package/lib/polling.ts +2 -0
- package/lib/preview.ts +715 -566
- package/lib/queue.ts +5 -5
- package/lib/routing.ts +644 -283
- package/lib/runtime.ts +260 -104
- package/lib/sections.ts +0 -2
- package/lib/telegram-api.ts +61 -8
- package/lib/text-groups.ts +75 -136
- package/lib/thread-display.ts +15 -4
- package/lib/threads.ts +130 -38
- package/lib/turns.ts +8 -1
- package/lib/updates.ts +58 -21
- package/package.json +1 -1
package/lib/updates.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { isDeepStrictEqual } from "node:util";
|
|
9
|
+
import { withoutTelegramTurnReplyAnchor } from "./turns.ts";
|
|
9
10
|
|
|
10
11
|
import type {
|
|
11
12
|
TelegramBusEnvelope,
|
|
@@ -552,7 +553,10 @@ interface TelegramUpdateAdmissionBinding {
|
|
|
552
553
|
prepareLiveInput?: (
|
|
553
554
|
sourceUpdateIds: readonly number[],
|
|
554
555
|
isCurrent: () => boolean,
|
|
556
|
+
onTransferred?: () => void,
|
|
555
557
|
) => TelegramLiveDeferredInputPreparation | undefined;
|
|
558
|
+
/** Internal notification after the worker's exact donor-removal ACK; never issues completion itself. */
|
|
559
|
+
acknowledgeTransferred?: () => void;
|
|
556
560
|
prepareQueueAdmission?: () =>
|
|
557
561
|
TelegramDeferredQueueAdmissionPreparation | undefined;
|
|
558
562
|
prepareSourceCompletion?: () =>
|
|
@@ -614,6 +618,7 @@ export function bindTelegramUpdateAdmissionSource<
|
|
|
614
618
|
| "inspectSourceSnapshot"
|
|
615
619
|
| "inspectCompletion"
|
|
616
620
|
| "isSourceUnsettled"
|
|
621
|
+
| "acknowledgeTransferred"
|
|
617
622
|
| "prepareLiveInput"
|
|
618
623
|
| "prepareQueueAdmission"
|
|
619
624
|
| "prepareSourceCompletion"
|
|
@@ -761,20 +766,24 @@ export function prepareTelegramLiveDeferredInput(
|
|
|
761
766
|
)
|
|
762
767
|
)
|
|
763
768
|
return undefined;
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
);
|
|
769
|
+
const bindings = carriers.map(getTelegramUpdateAdmissionBinding);
|
|
770
|
+
const acknowledgers = bindings.map(binding => binding?.acknowledgeTransferred);
|
|
771
|
+
const bindingsCurrent = () => carriers.every((value, index) =>
|
|
772
|
+
getTelegramUpdateAdmissionBinding(value) === bindings[index] && bindings[index]?.acknowledgeTransferred === acknowledgers[index]);
|
|
773
|
+
const unsettled = () => bindingsCurrent() && bindings.every(binding => binding?.isSourceUnsettled?.() === true);
|
|
770
774
|
if (!unsettled()) return undefined;
|
|
775
|
+
let transferred = false;
|
|
771
776
|
const prepared = getTelegramUpdateAdmissionBinding(first)?.prepareLiveInput?.(
|
|
772
777
|
ids,
|
|
773
778
|
() =>
|
|
774
|
-
isCurrent() &&
|
|
779
|
+
bindingsCurrent() && isCurrent() &&
|
|
775
780
|
carriers.every(
|
|
776
781
|
(value) => getTelegramUpdateExecutionFence(value)?.isCurrent() === true,
|
|
777
782
|
),
|
|
783
|
+
() => {
|
|
784
|
+
transferred = true;
|
|
785
|
+
for (let index = 0; index < bindings.length; index++) acknowledgers[index]?.call(bindings[index]);
|
|
786
|
+
},
|
|
778
787
|
);
|
|
779
788
|
return (
|
|
780
789
|
prepared && {
|
|
@@ -785,7 +794,7 @@ export function prepareTelegramLiveDeferredInput(
|
|
|
785
794
|
unsettled() && prepared.beginRelease(() => unsettled() && canRelease()),
|
|
786
795
|
settleTransferred: (canSettle) =>
|
|
787
796
|
unsettled()
|
|
788
|
-
? prepared.settleTransferred(() => unsettled() && canSettle())
|
|
797
|
+
? prepared.settleTransferred(() => (transferred || unsettled()) && canSettle())
|
|
789
798
|
: "protected",
|
|
790
799
|
cancel: () => unsettled() && prepared.cancel(),
|
|
791
800
|
}
|
|
@@ -2350,6 +2359,7 @@ export interface TelegramUpdateWorkerRuntimeDeps<TContext> {
|
|
|
2350
2359
|
update: TelegramJournaledUpdate,
|
|
2351
2360
|
ctx: TContext,
|
|
2352
2361
|
signal: AbortSignal,
|
|
2362
|
+
target?: TelegramTarget & { threadId: number },
|
|
2353
2363
|
) => Promise<TelegramUpdateAdmissionOutcome> | TelegramUpdateAdmissionOutcome;
|
|
2354
2364
|
/** Native source-only binding, installed by the admission-worker assembly rather than ordinary dispatch. */
|
|
2355
2365
|
admitPreparedLiveInput?: (
|
|
@@ -2466,7 +2476,7 @@ export interface TelegramLiveInputPreparation {
|
|
|
2466
2476
|
prepareStatusConsumption?(input: TelegramLiveStatusConsumption): boolean;
|
|
2467
2477
|
/** Native owner availability before recipient append; not admission or future target activation. */
|
|
2468
2478
|
canPrepareStatusConsumption?(): boolean;
|
|
2469
|
-
release(canRelease: () => boolean): boolean;
|
|
2479
|
+
release(canRelease: () => boolean, target?: TelegramTarget & { threadId: number }): boolean;
|
|
2470
2480
|
/** Ends a failed pre-append attempt only when no selected input was saved. */
|
|
2471
2481
|
cancelEmpty(): boolean;
|
|
2472
2482
|
/** One fenced removal of this never-dispatched recipient copy; unknown issuance cannot be retried. */
|
|
@@ -2532,6 +2542,7 @@ export interface TelegramUpdateWorkerRuntime<TContext> {
|
|
|
2532
2542
|
sourceUpdateIds: readonly number[];
|
|
2533
2543
|
signal: AbortSignal;
|
|
2534
2544
|
isCurrent(): boolean;
|
|
2545
|
+
onTransferred?(): void;
|
|
2535
2546
|
}) => TelegramLiveDeferredInputPreparation | undefined;
|
|
2536
2547
|
prepareDeferredQueueAdmission?: (input: {
|
|
2537
2548
|
updateId: number;
|
|
@@ -2652,6 +2663,7 @@ interface TelegramLiveInputPreparationState<TContext> {
|
|
|
2652
2663
|
saved?: TelegramUpdateWorkerJournalSnapshot["entries"][number][];
|
|
2653
2664
|
discardIssued?: true;
|
|
2654
2665
|
released?: true;
|
|
2666
|
+
target?: TelegramTarget & { threadId: number };
|
|
2655
2667
|
status?: {
|
|
2656
2668
|
current(): boolean;
|
|
2657
2669
|
assertCurrent(): void;
|
|
@@ -3707,6 +3719,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
3707
3719
|
expectedOwner: TelegramUpdateWorkerOwner<TContext>,
|
|
3708
3720
|
update: TelegramJournaledUpdate,
|
|
3709
3721
|
preparedExecute?: TelegramUpdateWorkerRuntimeDeps<TContext>["executeUpdate"],
|
|
3722
|
+
target?: TelegramTarget & { threadId: number },
|
|
3710
3723
|
): Promise<
|
|
3711
3724
|
| TelegramUpdateWorkerExecutionSettlement
|
|
3712
3725
|
| typeof TELEGRAM_UPDATE_WORKER_EXECUTION_ABORTED
|
|
@@ -3735,6 +3748,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
3735
3748
|
update,
|
|
3736
3749
|
expectedOwner.ctx,
|
|
3737
3750
|
expectedOwner.controller.signal,
|
|
3751
|
+
target,
|
|
3738
3752
|
),
|
|
3739
3753
|
);
|
|
3740
3754
|
const settlement: Promise<TelegramUpdateWorkerExecutionSettlement> =
|
|
@@ -4739,6 +4753,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
4739
4753
|
return admitted.outcome;
|
|
4740
4754
|
}
|
|
4741
4755
|
: undefined,
|
|
4756
|
+
prepared?.target,
|
|
4742
4757
|
);
|
|
4743
4758
|
if (execution === TELEGRAM_UPDATE_WORKER_EXECUTION_ABORTED) {
|
|
4744
4759
|
return "aborted";
|
|
@@ -4821,6 +4836,11 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
4821
4836
|
snapshotInvalidated = true;
|
|
4822
4837
|
break;
|
|
4823
4838
|
}
|
|
4839
|
+
if (prepared?.target) {
|
|
4840
|
+
prepared.ids = new Set([...prepared.ids].filter(id => id !== entry.updateId));
|
|
4841
|
+
if (!prepared.ids.size && livePreparation === prepared) livePreparation = undefined;
|
|
4842
|
+
updateClaimCounts();
|
|
4843
|
+
}
|
|
4824
4844
|
const postExecutionAuthority = checkAuthority(
|
|
4825
4845
|
expectedOwner,
|
|
4826
4846
|
entry.updateId,
|
|
@@ -5270,6 +5290,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
5270
5290
|
prepareDeferredLiveInput(input) {
|
|
5271
5291
|
const expectedOwner = owner,
|
|
5272
5292
|
binding = deps.getJournalBindingKey?.(),
|
|
5293
|
+
onTransferred = input.onTransferred,
|
|
5273
5294
|
ids = [...input.sourceUpdateIds];
|
|
5274
5295
|
const sources = ids.map((id) => deferredSources.get(id));
|
|
5275
5296
|
const ownerCurrent = () =>
|
|
@@ -5282,7 +5303,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
5282
5303
|
deps.hasAuthority(expectedOwner.ctx) &&
|
|
5283
5304
|
deps.isContextCurrent?.(expectedOwner.ctx) !== false &&
|
|
5284
5305
|
isQueueOwnerIdentityCurrent(expectedOwner) &&
|
|
5285
|
-
input.isCurrent();
|
|
5306
|
+
input.onTransferred === onTransferred && input.isCurrent();
|
|
5286
5307
|
const sourcesCurrent = () =>
|
|
5287
5308
|
ownerCurrent() &&
|
|
5288
5309
|
sources.every(
|
|
@@ -5381,13 +5402,11 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
5381
5402
|
undefined,
|
|
5382
5403
|
() => current() && canSettle(),
|
|
5383
5404
|
);
|
|
5384
|
-
if (
|
|
5385
|
-
!current() ||
|
|
5386
|
-
!canSettle() ||
|
|
5387
|
-
result.removedUpdateIds.length !== ids.length ||
|
|
5388
|
-
!ids.every((id) => result.removedUpdateIds.includes(id))
|
|
5389
|
-
)
|
|
5405
|
+
if (result.removedUpdateIds.length !== ids.length || !ids.every(id => result.removedUpdateIds.includes(id)))
|
|
5390
5406
|
return "unknown";
|
|
5407
|
+
// A consumed routing clock is expected only after this exact ACK, never after uncertain removal.
|
|
5408
|
+
onTransferred?.call(input);
|
|
5409
|
+
if (!current() || !canSettle()) return "unknown";
|
|
5391
5410
|
} catch (error) {
|
|
5392
5411
|
recordRuntimeEvent(error, {
|
|
5393
5412
|
phase: "live-deferred-transfer-settlement",
|
|
@@ -5511,7 +5530,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
5511
5530
|
}
|
|
5512
5531
|
};
|
|
5513
5532
|
const finish = () => {
|
|
5514
|
-
if (!held.released || !held.status) livePreparation = undefined;
|
|
5533
|
+
if (!held.released || !held.status && !held.target) livePreparation = undefined;
|
|
5515
5534
|
updateClaimCounts();
|
|
5516
5535
|
notifyStateChange();
|
|
5517
5536
|
blocked = false;
|
|
@@ -5659,7 +5678,7 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
5659
5678
|
};
|
|
5660
5679
|
return held.status.current();
|
|
5661
5680
|
},
|
|
5662
|
-
release(canRelease) {
|
|
5681
|
+
release(canRelease, target) {
|
|
5663
5682
|
if (
|
|
5664
5683
|
!current() ||
|
|
5665
5684
|
held.discardIssued ||
|
|
@@ -5676,6 +5695,14 @@ export function createTelegramUpdateWorkerRuntime<TContext>(
|
|
|
5676
5695
|
!current()
|
|
5677
5696
|
)
|
|
5678
5697
|
return false;
|
|
5698
|
+
if (target) {
|
|
5699
|
+
if (!Number.isSafeInteger(target.chatId) || target.chatId === 0 || !Number.isSafeInteger(target.threadId) || target.threadId <= 0 ||
|
|
5700
|
+
entries.some(entry => {
|
|
5701
|
+
const message = (entry.update.message ?? entry.update.edited_message) as { chat?: { id?: number } } | undefined;
|
|
5702
|
+
return message?.chat?.id !== target.chatId;
|
|
5703
|
+
})) return false;
|
|
5704
|
+
if (!held.status) held.target = { ...target };
|
|
5705
|
+
}
|
|
5679
5706
|
released = true;
|
|
5680
5707
|
held.released = true;
|
|
5681
5708
|
finish();
|
|
@@ -8846,7 +8873,8 @@ function createTelegramUpdateSourceAdmissionHandle<
|
|
|
8846
8873
|
signal,
|
|
8847
8874
|
})
|
|
8848
8875
|
: undefined,
|
|
8849
|
-
|
|
8876
|
+
acknowledgeTransferred: () => { settlementReported = true; },
|
|
8877
|
+
prepareLiveInput: (sourceUpdateIds, isCurrent, onTransferred) =>
|
|
8850
8878
|
execution.isCurrent() &&
|
|
8851
8879
|
!immediate &&
|
|
8852
8880
|
!settlementReported &&
|
|
@@ -8856,6 +8884,7 @@ function createTelegramUpdateSourceAdmissionHandle<
|
|
|
8856
8884
|
sourceUpdateIds,
|
|
8857
8885
|
signal,
|
|
8858
8886
|
isCurrent: () => execution.isCurrent() && isCurrent(),
|
|
8887
|
+
onTransferred,
|
|
8859
8888
|
})
|
|
8860
8889
|
: undefined,
|
|
8861
8890
|
isHistorical: (matchesOriginal) =>
|
|
@@ -10599,11 +10628,19 @@ export function createTelegramUpdateAdmissionWorkerRuntime<
|
|
|
10599
10628
|
),
|
|
10600
10629
|
}
|
|
10601
10630
|
: {}),
|
|
10602
|
-
async executeUpdate(update, ctx, signal) {
|
|
10631
|
+
async executeUpdate(update, ctx, signal, target) {
|
|
10603
10632
|
const typedUpdate = update as TUpdate;
|
|
10633
|
+
const prepared = deps.prepareUpdateForExecution?.(typedUpdate) ?? typedUpdate;
|
|
10634
|
+
// Journal/source evidence remains the original; only the warm released handler projection changes destination.
|
|
10635
|
+
const routed = target ? { ...prepared,
|
|
10636
|
+
...(prepared.message ? { message: prepared.message.message_thread_id === target.threadId ? { ...prepared.message }
|
|
10637
|
+
: withoutTelegramTurnReplyAnchor({ ...prepared.message, message_thread_id: target.threadId, reply_to_message: undefined }) } : {}),
|
|
10638
|
+
...(prepared.edited_message ? { edited_message: prepared.edited_message.message_thread_id === target.threadId ? { ...prepared.edited_message }
|
|
10639
|
+
: withoutTelegramTurnReplyAnchor({ ...prepared.edited_message, message_thread_id: target.threadId, reply_to_message: undefined }) } : {}),
|
|
10640
|
+
} as TUpdate : prepared;
|
|
10604
10641
|
try {
|
|
10605
10642
|
return await executeUpdate(
|
|
10606
|
-
|
|
10643
|
+
routed,
|
|
10607
10644
|
ctx,
|
|
10608
10645
|
signal,
|
|
10609
10646
|
);
|