@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
|
@@ -280,11 +280,12 @@ export type TelegramDurablePollingRuntimeAssemblyDeps<
|
|
|
280
280
|
"appendUpdateBatch" | "getJournalEntryCount" | "signalUpdateWorker"
|
|
281
281
|
> & {
|
|
282
282
|
canStart?: (ctx: TContext) => boolean;
|
|
283
|
+
prepareUpdateBatch?: (updates: readonly TUpdate[]) => void;
|
|
283
284
|
journal: {
|
|
284
285
|
appendBatch: (
|
|
285
286
|
updates: readonly TUpdate[],
|
|
286
287
|
acceptedThroughUpdateId?: number,
|
|
287
|
-
) =>
|
|
288
|
+
) => { nonExcludedUpdateIds: readonly number[] };
|
|
288
289
|
getAcceptedThroughUpdateId: () => number | undefined;
|
|
289
290
|
prepareCursorCutover?: () => MaybePromise<void>;
|
|
290
291
|
getEntryCount: () => number;
|
|
@@ -303,7 +304,30 @@ export function createTelegramDurablePollingRuntimeAssembly<
|
|
|
303
304
|
): TelegramDurablePollingRuntimeAssembly<TContext> {
|
|
304
305
|
const controller = createTelegramPollingControllerRuntime({
|
|
305
306
|
...deps,
|
|
306
|
-
appendUpdateBatch
|
|
307
|
+
appendUpdateBatch(updates, cursor) {
|
|
308
|
+
const result = deps.journal.appendBatch(updates, cursor);
|
|
309
|
+
if (!deps.prepareUpdateBatch) return result;
|
|
310
|
+
// No await: an already-draining worker must not observe the new batch before preparation.
|
|
311
|
+
try {
|
|
312
|
+
const included = new Set(result.nonExcludedUpdateIds);
|
|
313
|
+
let batch: TUpdate[] = [];
|
|
314
|
+
for (const update of updates) {
|
|
315
|
+
if (included.has(update.update_id)) batch.push(update);
|
|
316
|
+
else if (batch.length > 0) {
|
|
317
|
+
deps.prepareUpdateBatch(batch);
|
|
318
|
+
batch = [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (batch.length > 0) deps.prepareUpdateBatch(batch);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
try {
|
|
324
|
+
deps.recordRuntimeEvent?.("polling", error, { phase: "batch-preparation", updateCount: updates.length });
|
|
325
|
+
} catch {
|
|
326
|
+
// Already-published input must still reach the worker if diagnostics fail.
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
},
|
|
307
331
|
getAcceptedThroughUpdateId: deps.journal.getAcceptedThroughUpdateId,
|
|
308
332
|
getJournalEntryCount: deps.journal.getEntryCount,
|
|
309
333
|
signalUpdateWorker: deps.journal.signalWorker,
|
|
@@ -393,7 +417,6 @@ export function createTelegramPollingControllerRuntime<
|
|
|
393
417
|
getAcceptedThroughUpdateId: deps.getAcceptedThroughUpdateId,
|
|
394
418
|
getJournalEntryCount: deps.getJournalEntryCount,
|
|
395
419
|
signalUpdateWorker: deps.signalUpdateWorker,
|
|
396
|
-
prepareUpdateBatch: deps.prepareUpdateBatch,
|
|
397
420
|
updateStatus: deps.updateStatus,
|
|
398
421
|
sleep: deps.sleep,
|
|
399
422
|
onPhaseChange(phase, currentUpdateId) {
|
|
@@ -651,6 +674,7 @@ export interface TelegramThreadCapabilityRuntimeDeps<
|
|
|
651
674
|
getPollingStartedWithTelegramBus: () => boolean;
|
|
652
675
|
setPollingStartedWithTelegramBus: (started: boolean) => void;
|
|
653
676
|
setTopicModeUnavailable: (unavailable: boolean) => void;
|
|
677
|
+
suspendLiveThreadTarget?: () => void;
|
|
654
678
|
stopFollowerRegistration: () => void;
|
|
655
679
|
startClassicPolling: (ctx: TContext) => MaybePromise<void>;
|
|
656
680
|
stopClassicPolling: () => MaybePromise<void>;
|
|
@@ -682,6 +706,8 @@ export interface TelegramThreadCapabilityStateRuntime {
|
|
|
682
706
|
isBusRuntimeEnabled(): boolean;
|
|
683
707
|
shouldForceFreshLeaderThread(): boolean;
|
|
684
708
|
setForceFreshLeaderThread(forceFresh: boolean): void;
|
|
709
|
+
getRequestedThreadName(): string | undefined;
|
|
710
|
+
setRequestedThreadName(threadName: string | undefined): void;
|
|
685
711
|
}
|
|
686
712
|
|
|
687
713
|
export type TelegramThreadTargetObservationHandler<TContext> = (
|
|
@@ -715,6 +741,10 @@ export interface TelegramThreadAwarePollingPorts<TContext, TOwner> {
|
|
|
715
741
|
ctx: TContext,
|
|
716
742
|
owner: TOwner,
|
|
717
743
|
) => Promise<boolean | undefined>;
|
|
744
|
+
restoreFollowerWithOwner: (
|
|
745
|
+
ctx: TContext,
|
|
746
|
+
owner: TOwner,
|
|
747
|
+
) => Promise<boolean | undefined>;
|
|
718
748
|
stopFollowerRegistration: () => void;
|
|
719
749
|
}
|
|
720
750
|
|
|
@@ -738,6 +768,11 @@ export interface TelegramThreadAwarePollingDeps<
|
|
|
738
768
|
ctx: TContext,
|
|
739
769
|
owner: TOwner,
|
|
740
770
|
) => Promise<boolean | undefined>;
|
|
771
|
+
restoreFollowerWithLeader?: (
|
|
772
|
+
ctx: TContext,
|
|
773
|
+
owner: TOwner,
|
|
774
|
+
) => Promise<boolean | undefined>;
|
|
775
|
+
hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
|
|
741
776
|
stopFollowerRegistration: () => void;
|
|
742
777
|
}
|
|
743
778
|
|
|
@@ -760,6 +795,12 @@ export interface TelegramThreadCapabilityOrchestrationDeps<
|
|
|
760
795
|
ctx: TContext,
|
|
761
796
|
owner: TOwner,
|
|
762
797
|
) => Promise<boolean | undefined>;
|
|
798
|
+
restoreFollowerWithLeader?: (
|
|
799
|
+
ctx: TContext,
|
|
800
|
+
owner: TOwner,
|
|
801
|
+
) => Promise<boolean | undefined>;
|
|
802
|
+
hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
|
|
803
|
+
suspendLiveThreadTarget?: () => void;
|
|
763
804
|
stopFollowerRegistration: () => void;
|
|
764
805
|
isTopicModeUnavailableError: (error: unknown) => boolean;
|
|
765
806
|
updateStatus: (ctx: TContext) => void;
|
|
@@ -780,6 +821,7 @@ export function createTelegramThreadCapabilityStateRuntime(): TelegramThreadCapa
|
|
|
780
821
|
let busPollingStarted = false;
|
|
781
822
|
let topicModeUnavailable = false;
|
|
782
823
|
let forceFreshLeaderThread = false;
|
|
824
|
+
let requestedThreadName: string | undefined;
|
|
783
825
|
return {
|
|
784
826
|
isBusPollingStarted: () => busPollingStarted,
|
|
785
827
|
setBusPollingStarted(started) {
|
|
@@ -794,6 +836,10 @@ export function createTelegramThreadCapabilityStateRuntime(): TelegramThreadCapa
|
|
|
794
836
|
setForceFreshLeaderThread(forceFresh) {
|
|
795
837
|
forceFreshLeaderThread = forceFresh;
|
|
796
838
|
},
|
|
839
|
+
getRequestedThreadName: () => requestedThreadName,
|
|
840
|
+
setRequestedThreadName(threadName) {
|
|
841
|
+
requestedThreadName = threadName;
|
|
842
|
+
},
|
|
797
843
|
};
|
|
798
844
|
}
|
|
799
845
|
|
|
@@ -818,6 +864,7 @@ export function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(
|
|
|
818
864
|
getPollingStartedWithTelegramBus: deps.state.isBusPollingStarted,
|
|
819
865
|
setPollingStartedWithTelegramBus: deps.state.setBusPollingStarted,
|
|
820
866
|
setTopicModeUnavailable: deps.state.setTopicModeUnavailable,
|
|
867
|
+
suspendLiveThreadTarget: deps.suspendLiveThreadTarget,
|
|
821
868
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
822
869
|
startClassicPolling: deps.startClassicPolling,
|
|
823
870
|
stopClassicPolling: deps.stopClassicPolling,
|
|
@@ -850,6 +897,8 @@ export function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(
|
|
|
850
897
|
startLeaderHealth: deps.startLeaderHealth,
|
|
851
898
|
stopLeaderHealth: deps.stopLeaderHealth,
|
|
852
899
|
registerFollowerWithLeader: deps.registerFollowerWithLeader,
|
|
900
|
+
restoreFollowerWithLeader: deps.restoreFollowerWithLeader,
|
|
901
|
+
hasRememberedWorkspaceBinding: deps.hasRememberedWorkspaceBinding,
|
|
853
902
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
854
903
|
recordEvent: deps.recordEvent,
|
|
855
904
|
setTopicModeUnavailable: deps.state.setTopicModeUnavailable,
|
|
@@ -958,14 +1007,16 @@ export async function applyTelegramThreadCapability<TContext>(
|
|
|
958
1007
|
if (!isCurrent()) return;
|
|
959
1008
|
deps.setTopicModeUnavailable(true);
|
|
960
1009
|
deps.stopFollowerRegistration();
|
|
1010
|
+
const hadLiveThreadTransport = deps.getPollingStartedWithTelegramBus();
|
|
961
1011
|
if (
|
|
962
|
-
|
|
1012
|
+
hadLiveThreadTransport ||
|
|
963
1013
|
hasTelegramClassicRestoreFailure(previousBotState)
|
|
964
1014
|
) {
|
|
965
1015
|
deps.stopLeaderHealth();
|
|
966
1016
|
await deps.stopBusPolling();
|
|
967
1017
|
if (!isCurrent()) return;
|
|
968
1018
|
deps.setPollingStartedWithTelegramBus(false);
|
|
1019
|
+
if (hadLiveThreadTransport) deps.suspendLiveThreadTarget?.();
|
|
969
1020
|
try {
|
|
970
1021
|
await deps.startClassicPolling(ctx);
|
|
971
1022
|
if (!isCurrent()) return;
|
|
@@ -1110,24 +1161,34 @@ export function createTelegramThreadAwarePollingPorts<TContext, TOwner>(
|
|
|
1110
1161
|
}
|
|
1111
1162
|
await deps.stopClassicPolling();
|
|
1112
1163
|
};
|
|
1113
|
-
const
|
|
1114
|
-
ctx: TContext,
|
|
1115
|
-
owner: TOwner,
|
|
1116
|
-
): Promise<boolean | undefined> => {
|
|
1164
|
+
const refreshFollowerState = async (): Promise<boolean> => {
|
|
1117
1165
|
if (deps.topicTargetStore.refresh) {
|
|
1118
1166
|
await deps.topicTargetStore.refresh();
|
|
1119
1167
|
} else {
|
|
1120
1168
|
await deps.topicTargetStore.load();
|
|
1121
1169
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1170
|
+
return deps.topicTargetStore.getBotState().threadMode === "enabled";
|
|
1171
|
+
};
|
|
1172
|
+
const registerFollowerWithOwner = async (
|
|
1173
|
+
ctx: TContext,
|
|
1174
|
+
owner: TOwner,
|
|
1175
|
+
): Promise<boolean | undefined> => {
|
|
1176
|
+
if (!(await refreshFollowerState())) return undefined;
|
|
1125
1177
|
return deps.registerFollowerWithLeader(ctx, owner);
|
|
1126
1178
|
};
|
|
1179
|
+
const restoreFollowerWithOwner = async (
|
|
1180
|
+
ctx: TContext,
|
|
1181
|
+
owner: TOwner,
|
|
1182
|
+
): Promise<boolean | undefined> => {
|
|
1183
|
+
if (!(await refreshFollowerState())) return undefined;
|
|
1184
|
+
if (!deps.hasRememberedWorkspaceBinding?.(ctx)) return undefined;
|
|
1185
|
+
return deps.restoreFollowerWithLeader?.(ctx, owner);
|
|
1186
|
+
};
|
|
1127
1187
|
return {
|
|
1128
1188
|
startPolling,
|
|
1129
1189
|
stopPolling,
|
|
1130
1190
|
registerFollowerWithOwner,
|
|
1191
|
+
restoreFollowerWithOwner,
|
|
1131
1192
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
1132
1193
|
};
|
|
1133
1194
|
}
|
|
@@ -1179,9 +1240,9 @@ export function createTelegramThreadCapabilityMonitor<TContext>(
|
|
|
1179
1240
|
let consecutiveDisabledProbes = 0;
|
|
1180
1241
|
const stop = (): void => {
|
|
1181
1242
|
generation += 1;
|
|
1182
|
-
deps.lifecycle?.invalidate();
|
|
1183
1243
|
if (interval) clearInterval(interval);
|
|
1184
1244
|
interval = undefined;
|
|
1245
|
+
deps.lifecycle?.invalidate();
|
|
1185
1246
|
};
|
|
1186
1247
|
const check = (ctx: TContext): void => {
|
|
1187
1248
|
if (transitionPromise || !canProbeTelegramThreadCapability(ctx, deps)) {
|
|
@@ -1276,8 +1337,19 @@ export function createTelegramThreadCapabilityMonitor<TContext>(
|
|
|
1276
1337
|
return {
|
|
1277
1338
|
start(ctx) {
|
|
1278
1339
|
stop();
|
|
1340
|
+
const expectedGeneration = generation;
|
|
1279
1341
|
interval = setInterval(() => {
|
|
1280
|
-
|
|
1342
|
+
if (generation !== expectedGeneration) return;
|
|
1343
|
+
try {
|
|
1344
|
+
check(ctx);
|
|
1345
|
+
} catch (error) {
|
|
1346
|
+
try { stop(); } catch { /* Timer shutdown must not escape the callback. */ }
|
|
1347
|
+
try {
|
|
1348
|
+
deps.recordEvent("bus", error, { phase: "capability-monitor" });
|
|
1349
|
+
} catch {
|
|
1350
|
+
// Monitor diagnostics cannot create an uncaught interval exception.
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1281
1353
|
}, intervalMs);
|
|
1282
1354
|
interval.unref?.();
|
|
1283
1355
|
},
|
|
@@ -1416,7 +1488,6 @@ export interface TelegramPollLoopDeps<
|
|
|
1416
1488
|
getAcceptedThroughUpdateId?: () => number | undefined;
|
|
1417
1489
|
getJournalEntryCount: () => number;
|
|
1418
1490
|
signalUpdateWorker: () => void;
|
|
1419
|
-
prepareUpdateBatch?: (updates: readonly TUpdate[]) => void;
|
|
1420
1491
|
onErrorStatus: (message: string) => void;
|
|
1421
1492
|
onStatusReset: () => void;
|
|
1422
1493
|
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
@@ -1446,7 +1517,6 @@ export interface TelegramPollLoopRunnerDeps<
|
|
|
1446
1517
|
getAcceptedThroughUpdateId?: () => number | undefined;
|
|
1447
1518
|
getJournalEntryCount: () => number;
|
|
1448
1519
|
signalUpdateWorker: () => void;
|
|
1449
|
-
prepareUpdateBatch?: (updates: readonly TUpdate[]) => void;
|
|
1450
1520
|
updateStatus: (ctx: TContext, message?: string) => void;
|
|
1451
1521
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
1452
1522
|
onPhaseChange?: (
|
|
@@ -1501,7 +1571,6 @@ export function createTelegramPollLoopRunner<
|
|
|
1501
1571
|
getAcceptedThroughUpdateId: deps.getAcceptedThroughUpdateId,
|
|
1502
1572
|
getJournalEntryCount: deps.getJournalEntryCount,
|
|
1503
1573
|
signalUpdateWorker: deps.signalUpdateWorker,
|
|
1504
|
-
prepareUpdateBatch: deps.prepareUpdateBatch,
|
|
1505
1574
|
onErrorStatus: (message) => {
|
|
1506
1575
|
updateTelegramPollingStatusSafely(deps.updateStatus, ctx, {
|
|
1507
1576
|
message,
|
|
@@ -1702,7 +1771,6 @@ export async function runTelegramPollLoop<
|
|
|
1702
1771
|
reportTelegramPollingPhase(deps, "long-poll");
|
|
1703
1772
|
const updates = await requestTelegramUpdatesWithinBudget(deps, request);
|
|
1704
1773
|
reportTelegramPollingResponse(deps, updates.length);
|
|
1705
|
-
deps.prepareUpdateBatch?.(updates);
|
|
1706
1774
|
consecutiveGetUpdatesConflicts = 0;
|
|
1707
1775
|
currentUpdateId = updates[0]?.update_id;
|
|
1708
1776
|
await admitTelegramPollingUpdateBatch({
|
|
@@ -70,6 +70,7 @@ export interface TelegramPreviewActiveTurn {
|
|
|
70
70
|
target?: TelegramTarget;
|
|
71
71
|
voiceReplyPreferred?: boolean;
|
|
72
72
|
voiceReplyRequired?: boolean;
|
|
73
|
+
guestQueryId?: string;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
export interface TelegramAssistantMessagePreviewStartDeps<TMessage> {
|
|
@@ -408,6 +409,17 @@ export function createTelegramAssistantMessagePreviewHooks<TMessage>(
|
|
|
408
409
|
};
|
|
409
410
|
}
|
|
410
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Returns true when the active turn is a Telegram Guest Mode query. A guest
|
|
414
|
+
* query allows exactly one answer within a limited Telegram response window,
|
|
415
|
+
* so it must never emit streaming draft previews.
|
|
416
|
+
*/
|
|
417
|
+
export function shouldSuppressPreviewForGuestTurn(
|
|
418
|
+
turn: { guestQueryId?: string } | null | undefined,
|
|
419
|
+
): boolean {
|
|
420
|
+
return !!turn?.guestQueryId;
|
|
421
|
+
}
|
|
422
|
+
|
|
411
423
|
export async function handleTelegramAssistantMessagePreviewStart<TMessage>(
|
|
412
424
|
message: TMessage,
|
|
413
425
|
deps: TelegramAssistantMessagePreviewStartDeps<TMessage>,
|
|
@@ -422,6 +434,10 @@ export async function handleTelegramAssistantMessagePreviewStart<TMessage>(
|
|
|
422
434
|
deps.setState(undefined);
|
|
423
435
|
return;
|
|
424
436
|
}
|
|
437
|
+
if (shouldSuppressPreviewForGuestTurn(turn)) {
|
|
438
|
+
deps.setState(undefined);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
425
441
|
const state = deps.getState();
|
|
426
442
|
sealTelegramPreviewState(state);
|
|
427
443
|
const next = deps.createPreviewState();
|
|
@@ -443,6 +459,7 @@ export async function handleTelegramAssistantMessagePreviewUpdate<TMessage>(
|
|
|
443
459
|
return;
|
|
444
460
|
}
|
|
445
461
|
if (shouldSuppressPreviewForVoice(turn)) return;
|
|
462
|
+
if (shouldSuppressPreviewForGuestTurn(turn)) return;
|
|
446
463
|
let state = deps.getState();
|
|
447
464
|
if (!state) {
|
|
448
465
|
state = deps.createPreviewState();
|
|
@@ -14,7 +14,7 @@ export const TELEGRAM_DISCONNECTED_CONTEXT_MESSAGE =
|
|
|
14
14
|
|
|
15
15
|
const LOCAL_SYSTEM_PROMPT_SUFFIX = `
|
|
16
16
|
|
|
17
|
-
${TELEGRAM_CONNECTED_CONTEXT_MESSAGE} For Telegram work, consult bundled Skills in routing order: \`telegram-bridge\` for the transport and turn protocol, \`generated-control-surface\` when contextual controls materially shorten feedback, then \`generative-apps\` when the interaction warrants a reusable deterministic app. Load a Skill only if its instructions are not already present in the current context. Do not use Telegram-specific features from unrelated local/TUI prompts.`;
|
|
17
|
+
${TELEGRAM_CONNECTED_CONTEXT_MESSAGE} For Telegram work, consult bundled Skills in routing order: \`telegram-bridge\` for the transport and turn protocol, \`show-me\` when a user needs a truthful visual explanation of work or behavior, \`generated-control-surface\` when contextual controls materially shorten feedback, then \`generative-apps\` when the interaction warrants a reusable deterministic app. Load a Skill only if its instructions are not already present in the current context. Do not use Telegram-specific features from unrelated local/TUI prompts.`;
|
|
18
18
|
|
|
19
19
|
const TELEGRAM_TURN_SYSTEM_PROMPT_SUFFIX = `
|
|
20
20
|
|
|
@@ -28,9 +28,11 @@ export const TELEGRAM_ATTACH_PROMPT_GUIDELINES = [
|
|
|
28
28
|
"For an explicit thread target, provide chat_id plus thread_id; registered multi-instance followers default to their assigned thread target.",
|
|
29
29
|
] as const;
|
|
30
30
|
export const TELEGRAM_MESSAGE_PROMPT_SNIPPET =
|
|
31
|
-
"Send direct Telegram Markdown text when the user explicitly asks for Telegram delivery outside the normal reply flow.";
|
|
31
|
+
"Send direct Telegram Markdown text when the user explicitly asks for Telegram delivery to a concrete chat, channel, or live Pi Thread outside the normal reply flow.";
|
|
32
32
|
export const TELEGRAM_MESSAGE_PROMPT_GUIDELINES = [
|
|
33
33
|
"Use telegram_message only when the user explicitly asks to send a message to Telegram from the local/TUI side, or names a concrete Telegram delivery target.",
|
|
34
|
+
"For an explicitly requested channel post, pass its exact numeric id or public @username as chat_id; no local channel registry is required, and Telegram remains the authority on the bot's posting permission.",
|
|
35
|
+
"For an explicitly requested channel media post, pass one local .jpg/.jpeg/.png/.webp photo or .mp4 video as media; the text becomes its caption (max 1024 characters), and albums or other media types are rejected.",
|
|
34
36
|
"For a live Pi thread target, provide thread as its case-insensitive name or numeric id; the bridge sends visibly and admits one attributed turn to that live instance. Unknown, ambiguous, same, or offline targets fail before sending.",
|
|
35
37
|
"Add buttons by embedding the same top-level telegram_button HTML comments used in normal Telegram replies; Telegram does not support standalone buttons.",
|
|
36
38
|
"During an active Telegram turn, omit telegram_message for the current target and answer normally; use thread only when the user requests delivery to a different live Pi thread.",
|
|
@@ -48,6 +50,8 @@ const TELEGRAM_TOOL_METADATA_LINES = Object.fromEntries(
|
|
|
48
50
|
const TELEGRAM_MODEL_CONTEXT_TOOL_NAMES = new Set([
|
|
49
51
|
"telegram_attach",
|
|
50
52
|
"telegram_bind",
|
|
53
|
+
"telegram_channel_post",
|
|
54
|
+
"telegram_channel_posts",
|
|
51
55
|
"telegram_message",
|
|
52
56
|
]);
|
|
53
57
|
const TELEGRAM_MODEL_CONTEXT_MEMORY_KEY = Symbol.for(
|
|
@@ -110,6 +110,7 @@ export interface TelegramQueueItemBase {
|
|
|
110
110
|
transportStamp?: TelegramTransportStamp;
|
|
111
111
|
replyToMessageId: number;
|
|
112
112
|
guestQueryId?: string;
|
|
113
|
+
guestInlineMessageId?: string;
|
|
113
114
|
queueOrder: number;
|
|
114
115
|
queueLane: TelegramQueueLane;
|
|
115
116
|
laneOrder: number;
|
|
@@ -152,6 +153,7 @@ export interface TelegramQueueHandoffBase {
|
|
|
152
153
|
transportStamp?: TelegramTransportStamp;
|
|
153
154
|
replyToMessageId: number;
|
|
154
155
|
guestQueryId?: string;
|
|
156
|
+
guestInlineMessageId?: string;
|
|
155
157
|
queueOrder: number;
|
|
156
158
|
queueLane: TelegramQueueLane;
|
|
157
159
|
laneOrder: number;
|
|
@@ -612,6 +614,9 @@ export function createTelegramQueueHandoffPayload<TContext>(
|
|
|
612
614
|
...(item.transportStamp ? { transportStamp: item.transportStamp } : {}),
|
|
613
615
|
replyToMessageId: item.replyToMessageId,
|
|
614
616
|
...(item.guestQueryId ? { guestQueryId: item.guestQueryId } : {}),
|
|
617
|
+
...(item.guestInlineMessageId
|
|
618
|
+
? { guestInlineMessageId: item.guestInlineMessageId }
|
|
619
|
+
: {}),
|
|
615
620
|
queueOrder: item.queueOrder,
|
|
616
621
|
queueLane: item.queueLane,
|
|
617
622
|
laneOrder: item.laneOrder,
|
|
@@ -626,6 +631,9 @@ export function createTelegramQueueHandoffPayload<TContext>(
|
|
|
626
631
|
...(item.transportStamp ? { transportStamp: item.transportStamp } : {}),
|
|
627
632
|
replyToMessageId: item.replyToMessageId,
|
|
628
633
|
...(item.guestQueryId ? { guestQueryId: item.guestQueryId } : {}),
|
|
634
|
+
...(item.guestInlineMessageId
|
|
635
|
+
? { guestInlineMessageId: item.guestInlineMessageId }
|
|
636
|
+
: {}),
|
|
629
637
|
queueOrder: item.queueOrder,
|
|
630
638
|
queueLane: item.queueLane,
|
|
631
639
|
laneOrder: item.laneOrder,
|
|
@@ -1527,7 +1535,7 @@ export interface TelegramAgentEndRuntimeDeps<
|
|
|
1527
1535
|
chatId: number,
|
|
1528
1536
|
replyToMessageId: number,
|
|
1529
1537
|
text: string,
|
|
1530
|
-
options?: { target?: TelegramQueueTarget },
|
|
1538
|
+
options?: { target?: TelegramQueueTarget; parseMode?: "HTML" },
|
|
1531
1539
|
) => Promise<unknown>;
|
|
1532
1540
|
sendQueuedAttachments: (
|
|
1533
1541
|
turn: TTurn,
|
|
@@ -1544,6 +1552,8 @@ export interface TelegramAgentEndRuntimeDeps<
|
|
|
1544
1552
|
options?: { parseMode?: string },
|
|
1545
1553
|
) => Promise<void>;
|
|
1546
1554
|
sendGuestReply?: (guestQueryId: string, markdown: string) => Promise<void>;
|
|
1555
|
+
/** Replaces the early guest ACK with the final text. */
|
|
1556
|
+
editGuestReply?: (inlineMessageId: string, markdown: string) => Promise<void>;
|
|
1547
1557
|
sendGuestAttachment?: (
|
|
1548
1558
|
turn: TTurn,
|
|
1549
1559
|
attachment: QueuedAttachment,
|
|
@@ -1622,6 +1632,7 @@ export interface TelegramAgentEndHookRuntimeDeps<
|
|
|
1622
1632
|
>["sendRichAttachmentReply"];
|
|
1623
1633
|
answerGuestQuery?: TelegramAgentEndRuntimeDeps<TTurn>["answerGuestQuery"];
|
|
1624
1634
|
sendGuestReply?: TelegramAgentEndRuntimeDeps<TTurn>["sendGuestReply"];
|
|
1635
|
+
editGuestReply?: TelegramAgentEndRuntimeDeps<TTurn>["editGuestReply"];
|
|
1625
1636
|
sendGuestAttachment?: TelegramAgentEndRuntimeDeps<TTurn>["sendGuestAttachment"];
|
|
1626
1637
|
sendGuestVoiceReply?: TelegramAgentEndRuntimeDeps<TTurn>["sendGuestVoiceReply"];
|
|
1627
1638
|
planOutboundReply?: TelegramAgentEndRuntimeDeps<
|
|
@@ -1762,6 +1773,7 @@ export function createTelegramAgentEndHook<
|
|
|
1762
1773
|
sendRichAttachmentReply: deps.sendRichAttachmentReply,
|
|
1763
1774
|
answerGuestQuery: deps.answerGuestQuery,
|
|
1764
1775
|
sendGuestReply: deps.sendGuestReply,
|
|
1776
|
+
editGuestReply: deps.editGuestReply,
|
|
1765
1777
|
sendGuestAttachment: deps.sendGuestAttachment,
|
|
1766
1778
|
sendGuestVoiceReply: deps.sendGuestVoiceReply,
|
|
1767
1779
|
planOutboundReply: deps.planOutboundReply,
|
|
@@ -1854,11 +1866,47 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1854
1866
|
return;
|
|
1855
1867
|
}
|
|
1856
1868
|
if (turn.guestQueryId) {
|
|
1869
|
+
if (turn.guestInlineMessageId && deps.editGuestReply) {
|
|
1870
|
+
const experimentText = assistant.errorMessage
|
|
1871
|
+
? "Telegram bridge: Pi failed while processing the request."
|
|
1872
|
+
: finalText;
|
|
1873
|
+
if (experimentText) {
|
|
1874
|
+
try {
|
|
1875
|
+
await deps.editGuestReply(turn.guestInlineMessageId, experimentText);
|
|
1876
|
+
deps.recordRuntimeEvent?.(
|
|
1877
|
+
"guest",
|
|
1878
|
+
new Error("Guest ACK experiment edited the guest answer"),
|
|
1879
|
+
{ phase: "guest-ack-edited", guestQueryId: turn.guestQueryId },
|
|
1880
|
+
);
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
deps.recordRuntimeEvent?.("delivery", error, {
|
|
1883
|
+
phase: "guest-ack-edit",
|
|
1884
|
+
guestQueryId: turn.guestQueryId,
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
} else {
|
|
1888
|
+
deps.recordRuntimeEvent?.(
|
|
1889
|
+
"delivery",
|
|
1890
|
+
new Error("Guest ACK experiment turn produced no editable text"),
|
|
1891
|
+
{ phase: "guest-ack-edit-empty", guestQueryId: turn.guestQueryId },
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
if (!isDeliveryActive()) return;
|
|
1895
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1857
1898
|
if (assistant.errorMessage) {
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1899
|
+
try {
|
|
1900
|
+
await deps.answerGuestQuery?.(
|
|
1901
|
+
turn.guestQueryId,
|
|
1902
|
+
"Telegram bridge: Pi failed while processing the request.",
|
|
1903
|
+
);
|
|
1904
|
+
} catch (error) {
|
|
1905
|
+
deps.recordRuntimeEvent?.("delivery", error, {
|
|
1906
|
+
phase: "guest-error-reply",
|
|
1907
|
+
guestQueryId: turn.guestQueryId,
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1862
1910
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1863
1911
|
return;
|
|
1864
1912
|
}
|
|
@@ -1894,10 +1942,20 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1894
1942
|
});
|
|
1895
1943
|
}
|
|
1896
1944
|
} else if (finalText) {
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1945
|
+
try {
|
|
1946
|
+
if (deps.sendGuestReply) {
|
|
1947
|
+
await deps.sendGuestReply(turn.guestQueryId, finalText);
|
|
1948
|
+
} else {
|
|
1949
|
+
await deps.answerGuestQuery?.(turn.guestQueryId, finalText);
|
|
1950
|
+
}
|
|
1951
|
+
} catch (error) {
|
|
1952
|
+
// Guest queries expire after Telegram's response timeout, so a slow
|
|
1953
|
+
// turn can fail the only delivery attempt. Record and continue the
|
|
1954
|
+
// agent-end lifecycle instead of rejecting the extension hook.
|
|
1955
|
+
deps.recordRuntimeEvent?.("delivery", error, {
|
|
1956
|
+
phase: "guest-reply",
|
|
1957
|
+
guestQueryId: turn.guestQueryId,
|
|
1958
|
+
});
|
|
1901
1959
|
}
|
|
1902
1960
|
}
|
|
1903
1961
|
if (!isDeliveryActive()) return;
|
|
@@ -1908,23 +1966,37 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1908
1966
|
const deliverActiveTurn = async () => {
|
|
1909
1967
|
await deps.waitForActivityIdle?.();
|
|
1910
1968
|
if (!isDeliveryActive()) return;
|
|
1911
|
-
|
|
1969
|
+
let previewCleared = false;
|
|
1970
|
+
const clearTurnPreview = async () => {
|
|
1971
|
+
if (previewCleared) return;
|
|
1912
1972
|
await clearPreview?.();
|
|
1973
|
+
previewCleared = true;
|
|
1974
|
+
};
|
|
1975
|
+
if (endPlan.shouldClearPreview || (!finalText && hasOutboundArtifacts)) {
|
|
1976
|
+
await clearTurnPreview();
|
|
1913
1977
|
if (!isDeliveryActive()) return;
|
|
1914
1978
|
}
|
|
1915
1979
|
if (endPlan.shouldSendErrorMessage) {
|
|
1980
|
+
const errorMessage = assistant.errorMessage ||
|
|
1981
|
+
"Telegram bridge: Pi failed while processing the request.";
|
|
1982
|
+
const isOperationAborted = errorMessage.trim().replace(/\.$/, "") ===
|
|
1983
|
+
"This operation was aborted";
|
|
1916
1984
|
await deps.sendTextReply(
|
|
1917
1985
|
turn.chatId,
|
|
1918
1986
|
turn.replyToMessageId,
|
|
1919
|
-
|
|
1920
|
-
"
|
|
1921
|
-
|
|
1987
|
+
isOperationAborted
|
|
1988
|
+
? "<b>⏹️ This operation was aborted.</b>"
|
|
1989
|
+
: errorMessage,
|
|
1990
|
+
{
|
|
1991
|
+
target: turn.target,
|
|
1992
|
+
...(isOperationAborted ? { parseMode: "HTML" as const } : {}),
|
|
1993
|
+
},
|
|
1922
1994
|
);
|
|
1923
1995
|
if (!isDeliveryActive()) return;
|
|
1924
1996
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1925
1997
|
return;
|
|
1926
1998
|
}
|
|
1927
|
-
if (finalText) setPreviewPendingText(finalText);
|
|
1999
|
+
if (finalText && turn.queuedAttachments.length === 0) setPreviewPendingText(finalText);
|
|
1928
2000
|
|
|
1929
2001
|
if (!isDeliveryActive()) return;
|
|
1930
2002
|
let richAttachmentDelivered = false;
|
|
@@ -1942,7 +2014,7 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1942
2014
|
);
|
|
1943
2015
|
if (!isDeliveryActive()) return;
|
|
1944
2016
|
if (richAttachmentDelivered) {
|
|
1945
|
-
await
|
|
2017
|
+
await clearTurnPreview();
|
|
1946
2018
|
if (!isDeliveryActive()) return;
|
|
1947
2019
|
setPreviewPendingText("");
|
|
1948
2020
|
}
|
|
@@ -1957,24 +2029,42 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1957
2029
|
}
|
|
1958
2030
|
}
|
|
1959
2031
|
if (!isDeliveryActive()) return;
|
|
2032
|
+
let queuedAttachmentsDelivered = false;
|
|
2033
|
+
if (!richAttachmentDelivered && turn.queuedAttachments.length > 0) {
|
|
2034
|
+
await clearTurnPreview();
|
|
2035
|
+
if (!isDeliveryActive()) return;
|
|
2036
|
+
setPreviewPendingText("");
|
|
2037
|
+
await deps.sendQueuedAttachments(turn, { isDeliveryActive });
|
|
2038
|
+
if (!isDeliveryActive()) return;
|
|
2039
|
+
queuedAttachmentsDelivered = true;
|
|
2040
|
+
}
|
|
1960
2041
|
if (!richAttachmentDelivered && endPlan.kind === "text" && finalText) {
|
|
1961
2042
|
try {
|
|
1962
|
-
|
|
1963
|
-
turn.chatId,
|
|
1964
|
-
finalText,
|
|
1965
|
-
turn.replyToMessageId,
|
|
1966
|
-
{ replyMarkup, target: turn.target },
|
|
1967
|
-
);
|
|
1968
|
-
if (!isDeliveryActive()) return;
|
|
1969
|
-
if (!finalized) {
|
|
1970
|
-
await clearPreview?.();
|
|
1971
|
-
if (!isDeliveryActive()) return;
|
|
2043
|
+
if (queuedAttachmentsDelivered) {
|
|
1972
2044
|
await deps.sendMarkdownReply(
|
|
1973
2045
|
turn.chatId,
|
|
1974
2046
|
turn.replyToMessageId,
|
|
1975
2047
|
finalText,
|
|
1976
2048
|
{ replyMarkup, target: turn.target },
|
|
1977
2049
|
);
|
|
2050
|
+
} else {
|
|
2051
|
+
const finalized = await finalizeMarkdownPreview(
|
|
2052
|
+
turn.chatId,
|
|
2053
|
+
finalText,
|
|
2054
|
+
turn.replyToMessageId,
|
|
2055
|
+
{ replyMarkup, target: turn.target },
|
|
2056
|
+
);
|
|
2057
|
+
if (!isDeliveryActive()) return;
|
|
2058
|
+
if (!finalized) {
|
|
2059
|
+
await clearTurnPreview();
|
|
2060
|
+
if (!isDeliveryActive()) return;
|
|
2061
|
+
await deps.sendMarkdownReply(
|
|
2062
|
+
turn.chatId,
|
|
2063
|
+
turn.replyToMessageId,
|
|
2064
|
+
finalText,
|
|
2065
|
+
{ replyMarkup, target: turn.target },
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
1978
2068
|
}
|
|
1979
2069
|
if (!isDeliveryActive()) return;
|
|
1980
2070
|
setPreviewPendingText("");
|
|
@@ -2041,7 +2131,9 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2041
2131
|
);
|
|
2042
2132
|
}
|
|
2043
2133
|
if (!isDeliveryActive()) return;
|
|
2044
|
-
if (!richAttachmentDelivered
|
|
2134
|
+
if (!richAttachmentDelivered && !queuedAttachmentsDelivered) {
|
|
2135
|
+
await deps.sendQueuedAttachments(turn, { isDeliveryActive });
|
|
2136
|
+
}
|
|
2045
2137
|
if (!isDeliveryActive()) return;
|
|
2046
2138
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
2047
2139
|
};
|
|
@@ -551,9 +551,12 @@ function applyInlineMarkdownStyles(text: string): string {
|
|
|
551
551
|
result = renderDelimitedInlineStyle(result, "*", (content) => {
|
|
552
552
|
return `<i>${content}</i>`;
|
|
553
553
|
});
|
|
554
|
-
|
|
554
|
+
result = renderDelimitedInlineStyle(result, "_", (content) => {
|
|
555
555
|
return `<i>${content}</i>`;
|
|
556
556
|
});
|
|
557
|
+
return renderDelimitedInlineStyle(result, "||", (content) => {
|
|
558
|
+
return `<tg-spoiler>${content}</tg-spoiler>`;
|
|
559
|
+
});
|
|
557
560
|
}
|
|
558
561
|
|
|
559
562
|
function restoreInlineMarkdownTokens(
|
|
@@ -672,7 +672,7 @@ export async function sendTelegramNativeMarkdownReply<TReplyMarkup = unknown>(
|
|
|
672
672
|
chatId, index === 0 ? replyToMessageId : undefined, options?.target,
|
|
673
673
|
(replyParameters) => deps.sendRichMessage({
|
|
674
674
|
chat_id: chatId,
|
|
675
|
-
rich_message: { markdown: chunk
|
|
675
|
+
rich_message: { markdown: chunk },
|
|
676
676
|
reply_markup:
|
|
677
677
|
index === chunks.length - 1 ? options?.replyMarkup : undefined,
|
|
678
678
|
...(replyParameters ? { reply_parameters: replyParameters } : {}),
|
|
@@ -894,7 +894,26 @@ export function createGuestMarkdownReplySender(deps: {
|
|
|
894
894
|
return async (guestQueryId: string, markdown: string) => {
|
|
895
895
|
const [richMarkdown = markdown] = splitTelegramNativeMarkdown(markdown);
|
|
896
896
|
await deps.answerGuestQuery(guestQueryId, undefined, {
|
|
897
|
-
richMessage: { markdown: richMarkdown
|
|
897
|
+
richMessage: { markdown: richMarkdown },
|
|
898
|
+
});
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Guest reply editor: replaces an early Guest Mode answer (the temporary ACK
|
|
904
|
+
* experiment) with native Rich Markdown content addressed by
|
|
905
|
+
* `inline_message_id` instead of a chat/message pair.
|
|
906
|
+
*/
|
|
907
|
+
export function createGuestMarkdownReplyEditor(deps: {
|
|
908
|
+
editGuestInlineMessage: (
|
|
909
|
+
inlineMessageId: string,
|
|
910
|
+
content: { richMessage?: TelegramInputRichMessage; text?: string },
|
|
911
|
+
) => Promise<void>;
|
|
912
|
+
}) {
|
|
913
|
+
return async (inlineMessageId: string, markdown: string) => {
|
|
914
|
+
const [richMarkdown = markdown] = splitTelegramNativeMarkdown(markdown);
|
|
915
|
+
await deps.editGuestInlineMessage(inlineMessageId, {
|
|
916
|
+
richMessage: { markdown: richMarkdown },
|
|
898
917
|
});
|
|
899
918
|
};
|
|
900
919
|
}
|