@llblab/pi-kit 0.6.0 → 0.7.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 +8 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-actors/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-actors/README.md +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +3 -0
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +14 -1
- package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +45 -3
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +16 -3
- package/node_modules/@llblab/pi-actors/dist/lib/observability.js +92 -7
- package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +0 -1
- package/node_modules/@llblab/pi-actors/dist/lib/pi.js +15 -24
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.d.ts +17 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.js +44 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +4 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +102 -4
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +58 -39
- package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +14 -6
- package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/async-runs.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +18 -23
- package/node_modules/@llblab/pi-actors/lib/async-runs.ts +18 -1
- package/node_modules/@llblab/pi-actors/lib/command-templates.ts +41 -3
- package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +1 -1
- package/node_modules/@llblab/pi-actors/lib/observability.ts +119 -5
- package/node_modules/@llblab/pi-actors/lib/pi.ts +15 -28
- package/node_modules/@llblab/pi-actors/lib/run-delivery-lineage.ts +68 -0
- package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +120 -4
- package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +69 -44
- package/node_modules/@llblab/pi-actors/lib/runtime.ts +17 -6
- package/node_modules/@llblab/pi-actors/package.json +1 -1
- package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +33 -12
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +125 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +87 -45
- package/node_modules/@llblab/pi-state-flow/README.md +203 -107
- package/node_modules/@llblab/pi-state-flow/docs/README.md +4 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +188 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +42 -0
- package/node_modules/@llblab/pi-state-flow/index.ts +164 -2
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +138 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +273 -0
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +48 -0
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +18 -5
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +268 -0
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +117 -0
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +562 -0
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +24 -12
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +606 -70
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +666 -0
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +95 -0
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +141 -0
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +52 -0
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +88 -0
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +296 -0
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +23 -7
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +79 -0
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +264 -0
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +6 -0
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +99 -7
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +305 -48
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +74 -7
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +125 -6
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +196 -0
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +233 -0
- package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +70 -24
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +254 -29
- package/node_modules/@llblab/pi-state-flow/package.json +8 -2
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +128 -0
- package/node_modules/@llblab/pi-telegram/AGENTS.md +14 -9
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +23 -5
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +18 -0
- package/node_modules/@llblab/pi-telegram/README.md +13 -9
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +227 -23
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +70 -19
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +6 -6
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +13 -6
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +3 -1
- package/node_modules/@llblab/pi-telegram/index.ts +4 -1418
- package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +6 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +46 -1
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +32 -19
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +600 -135
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +962 -55
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +355 -26
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +718 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +237 -11
- package/node_modules/@llblab/pi-telegram/lib/config.ts +242 -26
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1851 -0
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +20 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +2184 -126
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +44 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +152 -13
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +97 -10
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +29 -0
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +85 -17
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +17 -0
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +6 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +118 -26
- package/node_modules/@llblab/pi-telegram/lib/rendering.ts +4 -1
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +21 -2
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +344 -112
- package/node_modules/@llblab/pi-telegram/lib/setup.ts +44 -4
- package/node_modules/@llblab/pi-telegram/lib/status.ts +51 -4
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +308 -39
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +353 -22
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +664 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +226 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +118 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1686 -129
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +7 -0
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1319 -97
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +1643 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +968 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +84 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/screenshot.png +0 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-bus.mjs +83 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +101 -0
- package/node_modules/@llblab/{skills → pi-telegram/skills}/show-me/SKILL.md +28 -6
- package/node_modules/@llblab/pi-telegram/skills/show-me/references/telegram-surfaces.md +43 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/delivery-and-threads.md +1 -1
- package/node_modules/@llblab/skills/package.json +2 -3
- package/package.json +6 -5
- /package/node_modules/@llblab/pi-telegram/lib/{logs.ts → logging.ts} +0 -0
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
type TelegramQueueHandoffPayload,
|
|
50
50
|
} from "./queue.ts";
|
|
51
51
|
import type { TelegramTarget } from "./target.ts";
|
|
52
|
+
import type { TelegramThreadDisplayMode } from "./config.ts";
|
|
52
53
|
import { isProcessAlive } from "./locks.ts";
|
|
53
54
|
import { resolveAgentDir } from "./paths.ts";
|
|
54
55
|
|
|
@@ -152,6 +153,22 @@ export function getTelegramProcessLiveness(
|
|
|
152
153
|
return proof.identity === owner.processBirthId ? "alive" : "dead";
|
|
153
154
|
}
|
|
154
155
|
|
|
156
|
+
export function getTelegramProcessBirthIdentityLiveness(
|
|
157
|
+
processBirthId: string,
|
|
158
|
+
options: TelegramProcessLivenessOptions = {},
|
|
159
|
+
): TelegramProcessLiveness {
|
|
160
|
+
const match = /^(\d+):(start|generation):(.+)$/u.exec(processBirthId);
|
|
161
|
+
if (!match) return "unverifiable";
|
|
162
|
+
const processId = Number(match[1]);
|
|
163
|
+
if (!Number.isSafeInteger(processId) || processId <= 0) return "unverifiable";
|
|
164
|
+
const processAlive = options.isProcessAlive ?? isProcessAlive;
|
|
165
|
+
if (!processAlive(processId)) return "dead";
|
|
166
|
+
if (match[2] === "generation") return "unverifiable";
|
|
167
|
+
const proof = getTelegramProcessBirthProof(processId, options);
|
|
168
|
+
if (proof.status === "unverifiable") return "unverifiable";
|
|
169
|
+
return proof.identity === processBirthId ? "alive" : "dead";
|
|
170
|
+
}
|
|
171
|
+
|
|
155
172
|
export function createCurrentTelegramBusProcessRuntime(input: {
|
|
156
173
|
getActiveProfileName: () => string | undefined;
|
|
157
174
|
pid?: number;
|
|
@@ -208,6 +225,13 @@ export const TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION =
|
|
|
208
225
|
"durable-follower-admission-v1" as const;
|
|
209
226
|
export const TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF =
|
|
210
227
|
"queue-handoff-v1" as const;
|
|
228
|
+
export const TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE =
|
|
229
|
+
"input-custody-reference-v1" as const;
|
|
230
|
+
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME =
|
|
231
|
+
"workspace-thread-rename-v1" as const;
|
|
232
|
+
export const TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE = "thread-display-mode-v1" as const;
|
|
233
|
+
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT =
|
|
234
|
+
"workspace-follower-auto-connect-v1" as const;
|
|
211
235
|
|
|
212
236
|
export interface TelegramBusProtocolIdentity {
|
|
213
237
|
protocolVersion: number;
|
|
@@ -269,6 +293,19 @@ export function hasTelegramBusCapability(
|
|
|
269
293
|
return identity?.capabilities.includes(capability) ?? false;
|
|
270
294
|
}
|
|
271
295
|
|
|
296
|
+
export function getTelegramInputCustodyPeerReadiness(
|
|
297
|
+
followers: readonly Pick<TelegramBusFollowerView,
|
|
298
|
+
"registrationGeneration" | "protocol">[],
|
|
299
|
+
): ("ready" | "legacy" | "unknown")[] {
|
|
300
|
+
return followers.map(follower => {
|
|
301
|
+
if (!follower.registrationGeneration || !follower.protocol) return "unknown";
|
|
302
|
+
return hasTelegramBusCapability(follower.protocol,
|
|
303
|
+
TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION) &&
|
|
304
|
+
hasTelegramBusCapability(follower.protocol,
|
|
305
|
+
TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE) ? "ready" : "legacy";
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
272
309
|
export function getTelegramBusProtocolCompatibility(input: {
|
|
273
310
|
local: TelegramBusProtocolIdentity;
|
|
274
311
|
remote?: TelegramBusProtocolIdentity;
|
|
@@ -361,6 +398,7 @@ export function getTelegramFollowerTargetOwnership(input: {
|
|
|
361
398
|
instanceId: string;
|
|
362
399
|
ownerGeneration: string;
|
|
363
400
|
recipientBindingKey: string;
|
|
401
|
+
protocolIdentity: TelegramBusProtocolIdentity;
|
|
364
402
|
} | undefined {
|
|
365
403
|
const liveFollower = input.followers.find((follower) => {
|
|
366
404
|
return (
|
|
@@ -379,6 +417,7 @@ export function getTelegramFollowerTargetOwnership(input: {
|
|
|
379
417
|
instanceId: liveFollower.instanceId,
|
|
380
418
|
ownerGeneration: liveFollower.registrationGeneration,
|
|
381
419
|
recipientBindingKey: liveFollower.profileKey,
|
|
420
|
+
protocolIdentity: liveFollower.protocol,
|
|
382
421
|
};
|
|
383
422
|
}
|
|
384
423
|
// Persisted records are restart hints, not live routing authority. Only an
|
|
@@ -622,12 +661,19 @@ export interface TelegramBusFollowerDeliveryIdentity {
|
|
|
622
661
|
deliveryId: string;
|
|
623
662
|
sourceUpdateId: number;
|
|
624
663
|
recipientBindingKey: string;
|
|
664
|
+
sourceRecoveryKey?: string;
|
|
665
|
+
sourceClaim?: {
|
|
666
|
+
acquisitionId: string;
|
|
667
|
+
handoffId: string;
|
|
668
|
+
};
|
|
625
669
|
}
|
|
626
670
|
|
|
627
671
|
export type TelegramBusForeignUpdateFailureClass =
|
|
628
672
|
| "source-update-identity-missing"
|
|
629
673
|
| "recipient-binding-missing"
|
|
630
674
|
| "recipient-generation-missing"
|
|
675
|
+
| "source-reference-missing"
|
|
676
|
+
| "recipient-ownership-stale"
|
|
631
677
|
| "transport-failed"
|
|
632
678
|
| "acknowledgement-missing"
|
|
633
679
|
| "acknowledgement-rejected"
|
|
@@ -653,14 +699,23 @@ export function createTelegramBusFollowerDeliveryIdentity(input: {
|
|
|
653
699
|
| "leader.forwardCallback"
|
|
654
700
|
| "leader.forwardReaction"
|
|
655
701
|
| "leader.forwardMessage"
|
|
656
|
-
| "leader.forwardEditedMessage"
|
|
702
|
+
| "leader.forwardEditedMessage"
|
|
703
|
+
| "leader.wakeInputCustody";
|
|
657
704
|
recipientBindingKey: string;
|
|
658
705
|
sourceUpdateId: number;
|
|
706
|
+
sourceRecoveryKey?: string;
|
|
707
|
+
sourceClaim?: {
|
|
708
|
+
acquisitionId: string;
|
|
709
|
+
handoffId: string;
|
|
710
|
+
};
|
|
659
711
|
}): TelegramBusFollowerDeliveryIdentity {
|
|
660
712
|
if (
|
|
661
713
|
!input.recipientBindingKey ||
|
|
662
714
|
!Number.isSafeInteger(input.sourceUpdateId) ||
|
|
663
|
-
input.sourceUpdateId < 0
|
|
715
|
+
input.sourceUpdateId < 0 ||
|
|
716
|
+
(input.sourceClaim !== undefined &&
|
|
717
|
+
(!input.sourceClaim.acquisitionId || input.sourceClaim.acquisitionId.length > 256 ||
|
|
718
|
+
!input.sourceClaim.handoffId || input.sourceClaim.handoffId.length > 256))
|
|
664
719
|
) {
|
|
665
720
|
throw new Error("Telegram follower delivery identity is incomplete.");
|
|
666
721
|
}
|
|
@@ -678,15 +733,48 @@ export function createTelegramBusFollowerDeliveryIdentity(input: {
|
|
|
678
733
|
deliveryId: `telegram-follower-v1-${deliveryId}`,
|
|
679
734
|
sourceUpdateId: input.sourceUpdateId,
|
|
680
735
|
recipientBindingKey: input.recipientBindingKey,
|
|
736
|
+
...(input.sourceRecoveryKey ? { sourceRecoveryKey: input.sourceRecoveryKey } : {}),
|
|
737
|
+
...(input.sourceClaim ? { sourceClaim: { ...input.sourceClaim } } : {}),
|
|
681
738
|
};
|
|
682
739
|
}
|
|
683
740
|
|
|
741
|
+
export function canUseTelegramBusInputCustodyReference(input: {
|
|
742
|
+
local?: TelegramBusProtocolIdentity;
|
|
743
|
+
remote?: TelegramBusProtocolIdentity;
|
|
744
|
+
}): boolean {
|
|
745
|
+
return hasTelegramBusCapability(input.local, TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE) &&
|
|
746
|
+
hasTelegramBusCapability(input.remote, TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
export function createTelegramBusFollowerSourceReferenceDeliveryIdentity(input: {
|
|
750
|
+
kind: "leader.forwardCallback" | "leader.forwardReaction" |
|
|
751
|
+
"leader.forwardMessage" | "leader.forwardEditedMessage" | "leader.wakeInputCustody";
|
|
752
|
+
recipientBindingKey: string;
|
|
753
|
+
sourceRecoveryKey: string;
|
|
754
|
+
source: { updateId: number; owner: { acquisitionId: string; handoffId?: string } };
|
|
755
|
+
}): TelegramBusFollowerDeliveryIdentity {
|
|
756
|
+
if (!input.sourceRecoveryKey || !input.source.owner.handoffId) throw new Error(
|
|
757
|
+
"Telegram follower source-reference delivery requires an accepted handoff.",
|
|
758
|
+
);
|
|
759
|
+
return createTelegramBusFollowerDeliveryIdentity({ kind: input.kind,
|
|
760
|
+
recipientBindingKey: input.recipientBindingKey,
|
|
761
|
+
sourceUpdateId: input.source.updateId,
|
|
762
|
+
sourceRecoveryKey: input.sourceRecoveryKey,
|
|
763
|
+
sourceClaim: { acquisitionId: input.source.owner.acquisitionId,
|
|
764
|
+
handoffId: input.source.owner.handoffId } });
|
|
765
|
+
}
|
|
766
|
+
|
|
684
767
|
export type TelegramBusEnvelope = (
|
|
685
768
|
| {
|
|
686
769
|
kind: "follower.register";
|
|
687
770
|
requestId: string;
|
|
688
771
|
registration: TelegramBusInstanceRegistration;
|
|
689
772
|
}
|
|
773
|
+
| {
|
|
774
|
+
kind: "follower.restoreWorkspace";
|
|
775
|
+
requestId: string;
|
|
776
|
+
registration: TelegramBusInstanceRegistration;
|
|
777
|
+
}
|
|
690
778
|
| {
|
|
691
779
|
kind: "follower.heartbeat";
|
|
692
780
|
requestId: string;
|
|
@@ -701,6 +789,30 @@ export type TelegramBusEnvelope = (
|
|
|
701
789
|
registrationGeneration?: string;
|
|
702
790
|
sentAtMs: number;
|
|
703
791
|
}
|
|
792
|
+
| {
|
|
793
|
+
kind: "follower.setThreadDisplayMode";
|
|
794
|
+
requestId: string;
|
|
795
|
+
instanceId: string;
|
|
796
|
+
registrationGeneration: string;
|
|
797
|
+
mode: TelegramThreadDisplayMode;
|
|
798
|
+
}
|
|
799
|
+
| {
|
|
800
|
+
kind: "follower.renameThread";
|
|
801
|
+
requestId: string;
|
|
802
|
+
instanceId: string;
|
|
803
|
+
registrationGeneration: string;
|
|
804
|
+
target: TelegramTarget & { threadId: number };
|
|
805
|
+
threadName: string;
|
|
806
|
+
sentAtMs: number;
|
|
807
|
+
}
|
|
808
|
+
| {
|
|
809
|
+
kind: "follower.resetThreadName";
|
|
810
|
+
requestId: string;
|
|
811
|
+
instanceId: string;
|
|
812
|
+
registrationGeneration: string;
|
|
813
|
+
target: TelegramTarget & { threadId: number };
|
|
814
|
+
sentAtMs: number;
|
|
815
|
+
}
|
|
704
816
|
| {
|
|
705
817
|
kind: "leader.forwardCallback";
|
|
706
818
|
requestId: string;
|
|
@@ -738,6 +850,25 @@ export type TelegramBusEnvelope = (
|
|
|
738
850
|
message: unknown;
|
|
739
851
|
sentAtMs: number;
|
|
740
852
|
}
|
|
853
|
+
| {
|
|
854
|
+
kind: "leader.offerInputCustodyHandoff";
|
|
855
|
+
requestId: string;
|
|
856
|
+
recipientInstanceId: string;
|
|
857
|
+
recipientRegistrationGeneration: string;
|
|
858
|
+
recipientBindingKey: string;
|
|
859
|
+
sourceRecoveryKey: string;
|
|
860
|
+
source: { journalBindingKey: string; tokenSha256: string; updateId: number };
|
|
861
|
+
handoffId: string;
|
|
862
|
+
sentAtMs: number;
|
|
863
|
+
}
|
|
864
|
+
| {
|
|
865
|
+
kind: "leader.wakeInputCustody";
|
|
866
|
+
requestId: string;
|
|
867
|
+
recipientInstanceId: string;
|
|
868
|
+
recipientRegistrationGeneration: string;
|
|
869
|
+
delivery: TelegramBusFollowerDeliveryIdentity;
|
|
870
|
+
sentAtMs: number;
|
|
871
|
+
}
|
|
741
872
|
| {
|
|
742
873
|
kind: "leader.replaceFollowerTarget";
|
|
743
874
|
requestId: string;
|
|
@@ -817,7 +948,8 @@ export type TelegramBusEnvelope = (
|
|
|
817
948
|
| "request-id-collision"
|
|
818
949
|
| "ledger-overloaded"
|
|
819
950
|
| "incompatible-protocol"
|
|
820
|
-
| "stale-target"
|
|
951
|
+
| "stale-target"
|
|
952
|
+
| "workspace-binding-unavailable";
|
|
821
953
|
method?: string;
|
|
822
954
|
chatId?: number;
|
|
823
955
|
threadId?: number;
|
|
@@ -833,7 +965,10 @@ export type TelegramBusEnvelopeTrafficClass =
|
|
|
833
965
|
export function getTelegramBusEnvelopeTrafficClass(
|
|
834
966
|
envelope: TelegramBusEnvelope,
|
|
835
967
|
): TelegramBusEnvelopeTrafficClass {
|
|
836
|
-
if (
|
|
968
|
+
if (
|
|
969
|
+
envelope.kind === "follower.register" ||
|
|
970
|
+
envelope.kind === "follower.restoreWorkspace"
|
|
971
|
+
) return "bootstrap";
|
|
837
972
|
if (envelope.kind === "bus.ack") return "response";
|
|
838
973
|
return "generation-fenced";
|
|
839
974
|
}
|
|
@@ -879,7 +1014,8 @@ export function parseTelegramBusEnvelope(
|
|
|
879
1014
|
let envelope: TelegramBusEnvelope | undefined;
|
|
880
1015
|
switch (kind) {
|
|
881
1016
|
case "follower.register":
|
|
882
|
-
|
|
1017
|
+
case "follower.restoreWorkspace":
|
|
1018
|
+
envelope = parseRegisterEnvelope(value, requestId, kind);
|
|
883
1019
|
break;
|
|
884
1020
|
case "follower.heartbeat":
|
|
885
1021
|
envelope = parseHeartbeatEnvelope(value, requestId);
|
|
@@ -887,12 +1023,46 @@ export function parseTelegramBusEnvelope(
|
|
|
887
1023
|
case "follower.disconnect":
|
|
888
1024
|
envelope = parseDisconnectEnvelope(value, requestId);
|
|
889
1025
|
break;
|
|
1026
|
+
case "follower.setThreadDisplayMode":
|
|
1027
|
+
if (typeof value.instanceId === "string" &&
|
|
1028
|
+
typeof value.registrationGeneration === "string" &&
|
|
1029
|
+
(value.mode === "letters" || value.mode === "names" || value.mode === "directories")) {
|
|
1030
|
+
envelope = { kind, requestId, instanceId: value.instanceId,
|
|
1031
|
+
registrationGeneration: value.registrationGeneration, mode: value.mode };
|
|
1032
|
+
}
|
|
1033
|
+
break;
|
|
1034
|
+
case "follower.renameThread":
|
|
1035
|
+
envelope = parseRenameThreadEnvelope(value, requestId);
|
|
1036
|
+
break;
|
|
1037
|
+
case "follower.resetThreadName": {
|
|
1038
|
+
const target = parseTarget(value.target);
|
|
1039
|
+
if (typeof value.instanceId === "string" &&
|
|
1040
|
+
typeof value.registrationGeneration === "string" &&
|
|
1041
|
+
typeof value.sentAtMs === "number" &&
|
|
1042
|
+
target?.threadId !== undefined) {
|
|
1043
|
+
envelope = {
|
|
1044
|
+
kind,
|
|
1045
|
+
requestId,
|
|
1046
|
+
instanceId: value.instanceId,
|
|
1047
|
+
registrationGeneration: value.registrationGeneration,
|
|
1048
|
+
target: { chatId: target.chatId, threadId: target.threadId },
|
|
1049
|
+
sentAtMs: value.sentAtMs,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
break;
|
|
1053
|
+
}
|
|
890
1054
|
case "leader.forwardCallback":
|
|
891
1055
|
envelope = parseForwardCallbackEnvelope(value, requestId);
|
|
892
1056
|
break;
|
|
893
1057
|
case "leader.forwardReaction":
|
|
894
1058
|
envelope = parseForwardReactionEnvelope(value, requestId);
|
|
895
1059
|
break;
|
|
1060
|
+
case "leader.offerInputCustodyHandoff":
|
|
1061
|
+
envelope = parseOfferInputCustodyHandoffEnvelope(value, requestId);
|
|
1062
|
+
break;
|
|
1063
|
+
case "leader.wakeInputCustody":
|
|
1064
|
+
envelope = parseWakeInputCustodyEnvelope(value, requestId);
|
|
1065
|
+
break;
|
|
896
1066
|
case "leader.forwardMessage":
|
|
897
1067
|
envelope = parseForwardMessageEnvelope(
|
|
898
1068
|
value,
|
|
@@ -1031,6 +1201,14 @@ export interface TelegramBusForeignOwnedForwarderDeps<TMessage = unknown> {
|
|
|
1031
1201
|
getForwardCommentBatchPosition?: (
|
|
1032
1202
|
message: TMessage,
|
|
1033
1203
|
) => "comment" | "forward" | undefined;
|
|
1204
|
+
localProtocolIdentity?: TelegramBusProtocolIdentity;
|
|
1205
|
+
validateForwardOwnership?: (ownership: TelegramBusForwardOwnership) => boolean;
|
|
1206
|
+
resolveInputCustodyReference?: (input: {
|
|
1207
|
+
sourceUpdateId: number;
|
|
1208
|
+
recipientBindingKey: string;
|
|
1209
|
+
}) => { sourceRecoveryKey: string; source: {
|
|
1210
|
+
updateId: number; owner: { acquisitionId: string; handoffId?: string };
|
|
1211
|
+
} } | undefined;
|
|
1034
1212
|
recordRuntimeEvent?: (
|
|
1035
1213
|
category: string,
|
|
1036
1214
|
error: unknown,
|
|
@@ -1038,10 +1216,21 @@ export interface TelegramBusForeignOwnedForwarderDeps<TMessage = unknown> {
|
|
|
1038
1216
|
) => void;
|
|
1039
1217
|
}
|
|
1040
1218
|
|
|
1041
|
-
interface TelegramBusForwardOwnership {
|
|
1219
|
+
export interface TelegramBusForwardOwnership {
|
|
1042
1220
|
instanceId: string;
|
|
1043
1221
|
ownerGeneration?: string;
|
|
1044
1222
|
recipientBindingKey?: string;
|
|
1223
|
+
protocolIdentity?: TelegramBusProtocolIdentity;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
export function isTelegramBusForwardOwnershipCurrent(
|
|
1227
|
+
expected: TelegramBusForwardOwnership,
|
|
1228
|
+
current: TelegramBusForwardOwnership | undefined,
|
|
1229
|
+
): boolean {
|
|
1230
|
+
return Boolean(current && current.instanceId === expected.instanceId &&
|
|
1231
|
+
current.ownerGeneration === expected.ownerGeneration &&
|
|
1232
|
+
current.recipientBindingKey === expected.recipientBindingKey &&
|
|
1233
|
+
JSON.stringify(current.protocolIdentity) === JSON.stringify(expected.protocolIdentity));
|
|
1045
1234
|
}
|
|
1046
1235
|
|
|
1047
1236
|
type TelegramBusDurableForwardEnvelope = Extract<
|
|
@@ -1051,7 +1240,8 @@ type TelegramBusDurableForwardEnvelope = Extract<
|
|
|
1051
1240
|
| "leader.forwardCallback"
|
|
1052
1241
|
| "leader.forwardReaction"
|
|
1053
1242
|
| "leader.forwardMessage"
|
|
1054
|
-
| "leader.forwardEditedMessage"
|
|
1243
|
+
| "leader.forwardEditedMessage"
|
|
1244
|
+
| "leader.wakeInputCustody";
|
|
1055
1245
|
}
|
|
1056
1246
|
> & {
|
|
1057
1247
|
recipientRegistrationGeneration: string;
|
|
@@ -1145,13 +1335,15 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1145
1335
|
return settlement;
|
|
1146
1336
|
};
|
|
1147
1337
|
const prepare = (
|
|
1148
|
-
kind:
|
|
1338
|
+
kind: "leader.forwardCallback" | "leader.forwardReaction" |
|
|
1339
|
+
"leader.forwardMessage" | "leader.forwardEditedMessage",
|
|
1149
1340
|
value: unknown,
|
|
1150
1341
|
ownership: TelegramBusForwardOwnership,
|
|
1151
1342
|
):
|
|
1152
1343
|
| {
|
|
1153
1344
|
delivery: TelegramBusFollowerDeliveryIdentity;
|
|
1154
1345
|
recipientRegistrationGeneration: string;
|
|
1346
|
+
sourceReference: boolean;
|
|
1155
1347
|
}
|
|
1156
1348
|
| { settlement: TelegramBusForeignUpdateSettlement } => {
|
|
1157
1349
|
const sourceUpdateId = getTelegramBusForwardSourceUpdateId(value);
|
|
@@ -1178,11 +1370,19 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1178
1370
|
}),
|
|
1179
1371
|
};
|
|
1180
1372
|
}
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
ownership.recipientBindingKey
|
|
1185
|
-
)
|
|
1373
|
+
const sourceReference = canUseTelegramBusInputCustodyReference({
|
|
1374
|
+
local: deps.localProtocolIdentity, remote: ownership.protocolIdentity });
|
|
1375
|
+
const reference = sourceReference ? deps.resolveInputCustodyReference?.({
|
|
1376
|
+
sourceUpdateId, recipientBindingKey: ownership.recipientBindingKey }) : undefined;
|
|
1377
|
+
if (sourceReference && !reference) return { settlement: reject({
|
|
1378
|
+
status: "retryable", failureClass: "source-reference-missing",
|
|
1379
|
+
message: "Forwarded Telegram update has no exact custody reference.",
|
|
1380
|
+
envelopeKind: kind, ownership, sourceUpdateId }) };
|
|
1381
|
+
const delivery = reference
|
|
1382
|
+
? createTelegramBusFollowerSourceReferenceDeliveryIdentity({
|
|
1383
|
+
kind: "leader.wakeInputCustody", recipientBindingKey: ownership.recipientBindingKey,
|
|
1384
|
+
sourceRecoveryKey: reference.sourceRecoveryKey, source: reference.source })
|
|
1385
|
+
: createTelegramBusForwardDelivery(kind, sourceUpdateId, ownership.recipientBindingKey);
|
|
1186
1386
|
if (!ownership.ownerGeneration) {
|
|
1187
1387
|
return {
|
|
1188
1388
|
settlement: reject({
|
|
@@ -1198,12 +1398,17 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1198
1398
|
return {
|
|
1199
1399
|
delivery,
|
|
1200
1400
|
recipientRegistrationGeneration: ownership.ownerGeneration,
|
|
1401
|
+
sourceReference,
|
|
1201
1402
|
};
|
|
1202
1403
|
};
|
|
1203
1404
|
const send = async (
|
|
1204
1405
|
envelope: TelegramBusDurableForwardEnvelope,
|
|
1205
1406
|
ownership: TelegramBusForwardOwnership,
|
|
1206
1407
|
): Promise<TelegramBusForeignUpdateSettlement> => {
|
|
1408
|
+
if (deps.validateForwardOwnership && !deps.validateForwardOwnership(ownership)) return reject({
|
|
1409
|
+
status: "retryable", failureClass: "recipient-ownership-stale",
|
|
1410
|
+
message: "Telegram follower ownership changed before forwarding.",
|
|
1411
|
+
envelopeKind: envelope.kind, ownership, delivery: envelope.delivery });
|
|
1207
1412
|
if (deps.getAuthSecret) envelope.auth = deps.getAuthSecret();
|
|
1208
1413
|
const socketPath = resolveTelegramBusSocketPath(deps.socketPath);
|
|
1209
1414
|
let response: TelegramBusEnvelope | undefined;
|
|
@@ -1295,6 +1500,10 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1295
1500
|
forwardCallback: ({ query, ownership }) => {
|
|
1296
1501
|
const prepared = prepare("leader.forwardCallback", query, ownership);
|
|
1297
1502
|
if ("settlement" in prepared) return Promise.resolve(prepared.settlement);
|
|
1503
|
+
if (prepared.sourceReference) return send({ kind: "leader.wakeInputCustody",
|
|
1504
|
+
requestId: deps.createRequestId(), recipientInstanceId: ownership.instanceId,
|
|
1505
|
+
recipientRegistrationGeneration: prepared.recipientRegistrationGeneration,
|
|
1506
|
+
delivery: prepared.delivery, sentAtMs: getNowMs() }, ownership);
|
|
1298
1507
|
return send(
|
|
1299
1508
|
{
|
|
1300
1509
|
kind: "leader.forwardCallback",
|
|
@@ -1316,6 +1525,10 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1316
1525
|
ownership,
|
|
1317
1526
|
);
|
|
1318
1527
|
if ("settlement" in prepared) return Promise.resolve(prepared.settlement);
|
|
1528
|
+
if (prepared.sourceReference) return send({ kind: "leader.wakeInputCustody",
|
|
1529
|
+
requestId: deps.createRequestId(), recipientInstanceId: ownership.instanceId,
|
|
1530
|
+
recipientRegistrationGeneration: prepared.recipientRegistrationGeneration,
|
|
1531
|
+
delivery: prepared.delivery, sentAtMs: getNowMs() }, ownership);
|
|
1319
1532
|
return send(
|
|
1320
1533
|
{
|
|
1321
1534
|
kind: "leader.forwardReaction",
|
|
@@ -1333,6 +1546,10 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1333
1546
|
forwardMessage: ({ message, ownership }) => {
|
|
1334
1547
|
const prepared = prepare("leader.forwardMessage", message, ownership);
|
|
1335
1548
|
if ("settlement" in prepared) return Promise.resolve(prepared.settlement);
|
|
1549
|
+
if (prepared.sourceReference) return send({ kind: "leader.wakeInputCustody",
|
|
1550
|
+
requestId: deps.createRequestId(), recipientInstanceId: ownership.instanceId,
|
|
1551
|
+
recipientRegistrationGeneration: prepared.recipientRegistrationGeneration,
|
|
1552
|
+
delivery: prepared.delivery, sentAtMs: getNowMs() }, ownership);
|
|
1336
1553
|
return send(
|
|
1337
1554
|
{
|
|
1338
1555
|
kind: "leader.forwardMessage",
|
|
@@ -1360,6 +1577,10 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
|
|
|
1360
1577
|
ownership,
|
|
1361
1578
|
);
|
|
1362
1579
|
if ("settlement" in prepared) return Promise.resolve(prepared.settlement);
|
|
1580
|
+
if (prepared.sourceReference) return send({ kind: "leader.wakeInputCustody",
|
|
1581
|
+
requestId: deps.createRequestId(), recipientInstanceId: ownership.instanceId,
|
|
1582
|
+
recipientRegistrationGeneration: prepared.recipientRegistrationGeneration,
|
|
1583
|
+
delivery: prepared.delivery, sentAtMs: getNowMs() }, ownership);
|
|
1363
1584
|
return send(
|
|
1364
1585
|
{
|
|
1365
1586
|
kind: "leader.forwardEditedMessage",
|
|
@@ -1512,7 +1733,8 @@ export function createTelegramBusLocalServer(
|
|
|
1512
1733
|
);
|
|
1513
1734
|
const getRequestLedgerKey = (envelope: TelegramBusEnvelope): string => {
|
|
1514
1735
|
const identity =
|
|
1515
|
-
envelope.kind === "follower.register"
|
|
1736
|
+
envelope.kind === "follower.register" ||
|
|
1737
|
+
envelope.kind === "follower.restoreWorkspace"
|
|
1516
1738
|
? envelope.registration.instanceId
|
|
1517
1739
|
: "instanceId" in envelope
|
|
1518
1740
|
? envelope.instanceId
|
|
@@ -1823,21 +2045,32 @@ function sendTelegramBusLocalEnvelopeOnce(
|
|
|
1823
2045
|
const socket = createConnection(options.socketPath);
|
|
1824
2046
|
let settled = false;
|
|
1825
2047
|
let buffer = "";
|
|
2048
|
+
let timeoutFinalizer: ReturnType<typeof setImmediate> | undefined;
|
|
1826
2049
|
const settle = (callback: () => void) => {
|
|
1827
2050
|
if (settled) return;
|
|
1828
2051
|
settled = true;
|
|
1829
2052
|
clearTimeout(timeout);
|
|
2053
|
+
if (timeoutFinalizer) clearImmediate(timeoutFinalizer);
|
|
1830
2054
|
socket.destroy();
|
|
1831
2055
|
callback();
|
|
1832
2056
|
};
|
|
1833
2057
|
const timeout = setTimeout(() => {
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
2058
|
+
// A long synchronous Pi/TUI turn can resume in the timers phase after
|
|
2059
|
+
// the peer acknowledgement is already buffered. Give pending socket I/O
|
|
2060
|
+
// two poll phases before converting elapsed wall time into a transport
|
|
2061
|
+
// failure; Darwin can surface buffered Unix-socket input only on the
|
|
2062
|
+
// second cycle after a long stall, while a silent peer stays bounded.
|
|
2063
|
+
timeoutFinalizer = setImmediate(() => {
|
|
2064
|
+
timeoutFinalizer = setImmediate(() => {
|
|
2065
|
+
settle(() =>
|
|
2066
|
+
reject(
|
|
2067
|
+
createTelegramBusTransportTimeoutError(
|
|
2068
|
+
"Timed out waiting for Telegram bus response",
|
|
2069
|
+
),
|
|
2070
|
+
),
|
|
2071
|
+
);
|
|
2072
|
+
});
|
|
2073
|
+
});
|
|
1841
2074
|
}, timeoutMs);
|
|
1842
2075
|
socket.setEncoding("utf8");
|
|
1843
2076
|
socket.once("connect", () => {
|
|
@@ -1911,6 +2144,21 @@ export interface TelegramBusFollowerRegistry {
|
|
|
1911
2144
|
) => TelegramBusFollowerView[];
|
|
1912
2145
|
}
|
|
1913
2146
|
|
|
2147
|
+
export function createTelegramBusForwardOwnershipValidator(
|
|
2148
|
+
registry: Pick<TelegramBusFollowerRegistry, "get">,
|
|
2149
|
+
): (ownership: TelegramBusForwardOwnership) => boolean {
|
|
2150
|
+
return ownership => {
|
|
2151
|
+
const follower = registry.get(ownership.instanceId);
|
|
2152
|
+
return isTelegramBusForwardOwnershipCurrent(ownership,
|
|
2153
|
+
follower?.registrationGeneration && follower.profileKey && follower.protocol ? {
|
|
2154
|
+
instanceId: follower.instanceId,
|
|
2155
|
+
ownerGeneration: follower.registrationGeneration,
|
|
2156
|
+
recipientBindingKey: follower.profileKey,
|
|
2157
|
+
protocolIdentity: follower.protocol,
|
|
2158
|
+
} : undefined);
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
|
|
1914
2162
|
export function createTelegramBusFollowerRegistry(): TelegramBusFollowerRegistry {
|
|
1915
2163
|
const followers = new Map<string, TelegramBusFollowerView>();
|
|
1916
2164
|
const clone = (
|
|
@@ -2047,11 +2295,10 @@ async function handleTelegramBusSocketLine(
|
|
|
2047
2295
|
function parseRegisterEnvelope(
|
|
2048
2296
|
value: Record<string, unknown>,
|
|
2049
2297
|
requestId: string,
|
|
2298
|
+
kind: "follower.register" | "follower.restoreWorkspace",
|
|
2050
2299
|
): TelegramBusEnvelope | undefined {
|
|
2051
2300
|
const registration = parseRegistration(value.registration);
|
|
2052
|
-
return registration
|
|
2053
|
-
? { kind: "follower.register", requestId, registration }
|
|
2054
|
-
: undefined;
|
|
2301
|
+
return registration ? { kind, requestId, registration } : undefined;
|
|
2055
2302
|
}
|
|
2056
2303
|
|
|
2057
2304
|
function parseHeartbeatEnvelope(
|
|
@@ -2090,6 +2337,28 @@ function parseDisconnectEnvelope(
|
|
|
2090
2337
|
: undefined;
|
|
2091
2338
|
}
|
|
2092
2339
|
|
|
2340
|
+
function parseRenameThreadEnvelope(
|
|
2341
|
+
value: Record<string, unknown>,
|
|
2342
|
+
requestId: string,
|
|
2343
|
+
): TelegramBusEnvelope | undefined {
|
|
2344
|
+
const target = parseTarget(value.target);
|
|
2345
|
+
return typeof value.instanceId === "string" &&
|
|
2346
|
+
typeof value.registrationGeneration === "string" &&
|
|
2347
|
+
target?.threadId !== undefined &&
|
|
2348
|
+
typeof value.threadName === "string" &&
|
|
2349
|
+
typeof value.sentAtMs === "number"
|
|
2350
|
+
? {
|
|
2351
|
+
kind: "follower.renameThread",
|
|
2352
|
+
requestId,
|
|
2353
|
+
instanceId: value.instanceId,
|
|
2354
|
+
registrationGeneration: value.registrationGeneration,
|
|
2355
|
+
target: { chatId: target.chatId, threadId: target.threadId },
|
|
2356
|
+
threadName: value.threadName,
|
|
2357
|
+
sentAtMs: value.sentAtMs,
|
|
2358
|
+
}
|
|
2359
|
+
: undefined;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2093
2362
|
function parseTelegramBusFollowerDeliveryIdentity(
|
|
2094
2363
|
value: unknown,
|
|
2095
2364
|
): TelegramBusFollowerDeliveryIdentity | undefined {
|
|
@@ -2100,7 +2369,16 @@ function parseTelegramBusFollowerDeliveryIdentity(
|
|
|
2100
2369
|
!Number.isSafeInteger(value.sourceUpdateId) ||
|
|
2101
2370
|
(value.sourceUpdateId as number) < 0 ||
|
|
2102
2371
|
typeof value.recipientBindingKey !== "string" ||
|
|
2103
|
-
!value.recipientBindingKey
|
|
2372
|
+
!value.recipientBindingKey ||
|
|
2373
|
+
(value.sourceRecoveryKey !== undefined &&
|
|
2374
|
+
(typeof value.sourceRecoveryKey !== "string" || !value.sourceRecoveryKey ||
|
|
2375
|
+
value.sourceRecoveryKey.length > 1_024)) ||
|
|
2376
|
+
(value.sourceClaim !== undefined &&
|
|
2377
|
+
(!isRecord(value.sourceClaim) ||
|
|
2378
|
+
typeof value.sourceClaim.acquisitionId !== "string" ||
|
|
2379
|
+
!value.sourceClaim.acquisitionId || value.sourceClaim.acquisitionId.length > 256 ||
|
|
2380
|
+
typeof value.sourceClaim.handoffId !== "string" ||
|
|
2381
|
+
!value.sourceClaim.handoffId || value.sourceClaim.handoffId.length > 256))
|
|
2104
2382
|
) {
|
|
2105
2383
|
return undefined;
|
|
2106
2384
|
}
|
|
@@ -2108,6 +2386,11 @@ function parseTelegramBusFollowerDeliveryIdentity(
|
|
|
2108
2386
|
deliveryId: value.deliveryId,
|
|
2109
2387
|
sourceUpdateId: value.sourceUpdateId as number,
|
|
2110
2388
|
recipientBindingKey: value.recipientBindingKey,
|
|
2389
|
+
...(typeof value.sourceRecoveryKey === "string"
|
|
2390
|
+
? { sourceRecoveryKey: value.sourceRecoveryKey } : {}),
|
|
2391
|
+
...(isRecord(value.sourceClaim)
|
|
2392
|
+
? { sourceClaim: { acquisitionId: value.sourceClaim.acquisitionId as string,
|
|
2393
|
+
handoffId: value.sourceClaim.handoffId as string } } : {}),
|
|
2111
2394
|
};
|
|
2112
2395
|
}
|
|
2113
2396
|
|
|
@@ -2182,6 +2465,46 @@ function parseForwardMessageEnvelope(
|
|
|
2182
2465
|
: undefined;
|
|
2183
2466
|
}
|
|
2184
2467
|
|
|
2468
|
+
function parseOfferInputCustodyHandoffEnvelope(
|
|
2469
|
+
value: Record<string, unknown>, requestId: string,
|
|
2470
|
+
): TelegramBusEnvelope | undefined {
|
|
2471
|
+
const source = value.source;
|
|
2472
|
+
if (!isRecord(source) || typeof value.recipientInstanceId !== "string" ||
|
|
2473
|
+
typeof value.recipientRegistrationGeneration !== "string" ||
|
|
2474
|
+
typeof value.recipientBindingKey !== "string" || !value.recipientBindingKey ||
|
|
2475
|
+
typeof value.sourceRecoveryKey !== "string" || !value.sourceRecoveryKey ||
|
|
2476
|
+
value.sourceRecoveryKey.length > 1_024 ||
|
|
2477
|
+
typeof source.journalBindingKey !== "string" || !source.journalBindingKey ||
|
|
2478
|
+
source.journalBindingKey !== value.sourceRecoveryKey ||
|
|
2479
|
+
typeof source.tokenSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(source.tokenSha256) ||
|
|
2480
|
+
!Number.isSafeInteger(source.updateId) || (source.updateId as number) < 0 ||
|
|
2481
|
+
typeof value.handoffId !== "string" || !value.handoffId || value.handoffId.length > 256 ||
|
|
2482
|
+
typeof value.sentAtMs !== "number") return undefined;
|
|
2483
|
+
return { kind: "leader.offerInputCustodyHandoff", requestId,
|
|
2484
|
+
recipientInstanceId: value.recipientInstanceId,
|
|
2485
|
+
recipientRegistrationGeneration: value.recipientRegistrationGeneration,
|
|
2486
|
+
recipientBindingKey: value.recipientBindingKey,
|
|
2487
|
+
sourceRecoveryKey: value.sourceRecoveryKey,
|
|
2488
|
+
source: { journalBindingKey: source.journalBindingKey,
|
|
2489
|
+
tokenSha256: source.tokenSha256, updateId: source.updateId as number },
|
|
2490
|
+
handoffId: value.handoffId, sentAtMs: value.sentAtMs };
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
function parseWakeInputCustodyEnvelope(
|
|
2494
|
+
value: Record<string, unknown>, requestId: string,
|
|
2495
|
+
): TelegramBusEnvelope | undefined {
|
|
2496
|
+
const delivery = parseTelegramBusFollowerDeliveryIdentity(value.delivery);
|
|
2497
|
+
return delivery && delivery.sourceRecoveryKey && delivery.sourceClaim &&
|
|
2498
|
+
typeof value.recipientInstanceId === "string" &&
|
|
2499
|
+
typeof value.recipientRegistrationGeneration === "string" &&
|
|
2500
|
+
typeof value.sentAtMs === "number"
|
|
2501
|
+
? { kind: "leader.wakeInputCustody", requestId,
|
|
2502
|
+
recipientInstanceId: value.recipientInstanceId,
|
|
2503
|
+
recipientRegistrationGeneration: value.recipientRegistrationGeneration,
|
|
2504
|
+
delivery, sentAtMs: value.sentAtMs }
|
|
2505
|
+
: undefined;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2185
2508
|
function parseReplaceFollowerTargetEnvelope(
|
|
2186
2509
|
value: Record<string, unknown>,
|
|
2187
2510
|
requestId: string,
|
|
@@ -2280,6 +2603,8 @@ function parseQueueHandoffPayload(
|
|
|
2280
2603
|
!Number.isSafeInteger(value.replyToMessageId) ||
|
|
2281
2604
|
(value.guestQueryId !== undefined &&
|
|
2282
2605
|
typeof value.guestQueryId !== "string") ||
|
|
2606
|
+
(value.guestInlineMessageId !== undefined &&
|
|
2607
|
+
typeof value.guestInlineMessageId !== "string") ||
|
|
2283
2608
|
!Number.isSafeInteger(value.queueOrder) ||
|
|
2284
2609
|
(value.queueLane !== "control" &&
|
|
2285
2610
|
value.queueLane !== "priority" &&
|
|
@@ -2309,6 +2634,9 @@ function parseQueueHandoffPayload(
|
|
|
2309
2634
|
...(typeof value.guestQueryId === "string"
|
|
2310
2635
|
? { guestQueryId: value.guestQueryId }
|
|
2311
2636
|
: {}),
|
|
2637
|
+
...(typeof value.guestInlineMessageId === "string"
|
|
2638
|
+
? { guestInlineMessageId: value.guestInlineMessageId }
|
|
2639
|
+
: {}),
|
|
2312
2640
|
queueOrder: value.queueOrder as number,
|
|
2313
2641
|
queueLane,
|
|
2314
2642
|
laneOrder: value.laneOrder as number,
|
|
@@ -2578,7 +2906,8 @@ function parseAckEnvelope(
|
|
|
2578
2906
|
code === "request-id-collision" ||
|
|
2579
2907
|
code === "ledger-overloaded" ||
|
|
2580
2908
|
code === "incompatible-protocol" ||
|
|
2581
|
-
code === "stale-target"
|
|
2909
|
+
code === "stale-target" ||
|
|
2910
|
+
code === "workspace-binding-unavailable"
|
|
2582
2911
|
) {
|
|
2583
2912
|
const chatId = value.error.chatId;
|
|
2584
2913
|
const threadId = value.error.threadId;
|