@llblab/pi-kit 0.6.0 → 0.7.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/CHANGELOG.md +4 -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 +13 -8
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +20 -3
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-telegram/README.md +12 -8
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +220 -18
- 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 +12 -5
- 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 +28 -1
- 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 +350 -26
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +544 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +234 -11
- package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -24
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1792 -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 +38 -1
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +152 -13
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +51 -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/prompts.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +52 -18
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +305 -112
- package/node_modules/@llblab/pi-telegram/lib/status.ts +10 -0
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +308 -39
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +279 -4
- 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/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
|
@@ -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({
|
|
@@ -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,10 @@ 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.",
|
|
34
35
|
"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
36
|
"Add buttons by embedding the same top-level telegram_button HTML comments used in normal Telegram replies; Telegram does not support standalone buttons.",
|
|
36
37
|
"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 +49,8 @@ const TELEGRAM_TOOL_METADATA_LINES = Object.fromEntries(
|
|
|
48
49
|
const TELEGRAM_MODEL_CONTEXT_TOOL_NAMES = new Set([
|
|
49
50
|
"telegram_attach",
|
|
50
51
|
"telegram_bind",
|
|
52
|
+
"telegram_channel_post",
|
|
53
|
+
"telegram_channel_posts",
|
|
51
54
|
"telegram_message",
|
|
52
55
|
]);
|
|
53
56
|
const TELEGRAM_MODEL_CONTEXT_MEMORY_KEY = Symbol.for(
|
|
@@ -1527,7 +1527,7 @@ export interface TelegramAgentEndRuntimeDeps<
|
|
|
1527
1527
|
chatId: number,
|
|
1528
1528
|
replyToMessageId: number,
|
|
1529
1529
|
text: string,
|
|
1530
|
-
options?: { target?: TelegramQueueTarget },
|
|
1530
|
+
options?: { target?: TelegramQueueTarget; parseMode?: "HTML" },
|
|
1531
1531
|
) => Promise<unknown>;
|
|
1532
1532
|
sendQueuedAttachments: (
|
|
1533
1533
|
turn: TTurn,
|
|
@@ -1908,23 +1908,37 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1908
1908
|
const deliverActiveTurn = async () => {
|
|
1909
1909
|
await deps.waitForActivityIdle?.();
|
|
1910
1910
|
if (!isDeliveryActive()) return;
|
|
1911
|
-
|
|
1911
|
+
let previewCleared = false;
|
|
1912
|
+
const clearTurnPreview = async () => {
|
|
1913
|
+
if (previewCleared) return;
|
|
1912
1914
|
await clearPreview?.();
|
|
1915
|
+
previewCleared = true;
|
|
1916
|
+
};
|
|
1917
|
+
if (endPlan.shouldClearPreview || (!finalText && hasOutboundArtifacts)) {
|
|
1918
|
+
await clearTurnPreview();
|
|
1913
1919
|
if (!isDeliveryActive()) return;
|
|
1914
1920
|
}
|
|
1915
1921
|
if (endPlan.shouldSendErrorMessage) {
|
|
1922
|
+
const errorMessage = assistant.errorMessage ||
|
|
1923
|
+
"Telegram bridge: Pi failed while processing the request.";
|
|
1924
|
+
const isOperationAborted = errorMessage.trim().replace(/\.$/, "") ===
|
|
1925
|
+
"This operation was aborted";
|
|
1916
1926
|
await deps.sendTextReply(
|
|
1917
1927
|
turn.chatId,
|
|
1918
1928
|
turn.replyToMessageId,
|
|
1919
|
-
|
|
1920
|
-
"
|
|
1921
|
-
|
|
1929
|
+
isOperationAborted
|
|
1930
|
+
? "<b>⏹️ This operation was aborted.</b>"
|
|
1931
|
+
: errorMessage,
|
|
1932
|
+
{
|
|
1933
|
+
target: turn.target,
|
|
1934
|
+
...(isOperationAborted ? { parseMode: "HTML" as const } : {}),
|
|
1935
|
+
},
|
|
1922
1936
|
);
|
|
1923
1937
|
if (!isDeliveryActive()) return;
|
|
1924
1938
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1925
1939
|
return;
|
|
1926
1940
|
}
|
|
1927
|
-
if (finalText) setPreviewPendingText(finalText);
|
|
1941
|
+
if (finalText && turn.queuedAttachments.length === 0) setPreviewPendingText(finalText);
|
|
1928
1942
|
|
|
1929
1943
|
if (!isDeliveryActive()) return;
|
|
1930
1944
|
let richAttachmentDelivered = false;
|
|
@@ -1942,7 +1956,7 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1942
1956
|
);
|
|
1943
1957
|
if (!isDeliveryActive()) return;
|
|
1944
1958
|
if (richAttachmentDelivered) {
|
|
1945
|
-
await
|
|
1959
|
+
await clearTurnPreview();
|
|
1946
1960
|
if (!isDeliveryActive()) return;
|
|
1947
1961
|
setPreviewPendingText("");
|
|
1948
1962
|
}
|
|
@@ -1957,24 +1971,42 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1957
1971
|
}
|
|
1958
1972
|
}
|
|
1959
1973
|
if (!isDeliveryActive()) return;
|
|
1974
|
+
let queuedAttachmentsDelivered = false;
|
|
1975
|
+
if (!richAttachmentDelivered && turn.queuedAttachments.length > 0) {
|
|
1976
|
+
await clearTurnPreview();
|
|
1977
|
+
if (!isDeliveryActive()) return;
|
|
1978
|
+
setPreviewPendingText("");
|
|
1979
|
+
await deps.sendQueuedAttachments(turn, { isDeliveryActive });
|
|
1980
|
+
if (!isDeliveryActive()) return;
|
|
1981
|
+
queuedAttachmentsDelivered = true;
|
|
1982
|
+
}
|
|
1960
1983
|
if (!richAttachmentDelivered && endPlan.kind === "text" && finalText) {
|
|
1961
1984
|
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;
|
|
1985
|
+
if (queuedAttachmentsDelivered) {
|
|
1972
1986
|
await deps.sendMarkdownReply(
|
|
1973
1987
|
turn.chatId,
|
|
1974
1988
|
turn.replyToMessageId,
|
|
1975
1989
|
finalText,
|
|
1976
1990
|
{ replyMarkup, target: turn.target },
|
|
1977
1991
|
);
|
|
1992
|
+
} else {
|
|
1993
|
+
const finalized = await finalizeMarkdownPreview(
|
|
1994
|
+
turn.chatId,
|
|
1995
|
+
finalText,
|
|
1996
|
+
turn.replyToMessageId,
|
|
1997
|
+
{ replyMarkup, target: turn.target },
|
|
1998
|
+
);
|
|
1999
|
+
if (!isDeliveryActive()) return;
|
|
2000
|
+
if (!finalized) {
|
|
2001
|
+
await clearTurnPreview();
|
|
2002
|
+
if (!isDeliveryActive()) return;
|
|
2003
|
+
await deps.sendMarkdownReply(
|
|
2004
|
+
turn.chatId,
|
|
2005
|
+
turn.replyToMessageId,
|
|
2006
|
+
finalText,
|
|
2007
|
+
{ replyMarkup, target: turn.target },
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
1978
2010
|
}
|
|
1979
2011
|
if (!isDeliveryActive()) return;
|
|
1980
2012
|
setPreviewPendingText("");
|
|
@@ -2041,7 +2073,9 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2041
2073
|
);
|
|
2042
2074
|
}
|
|
2043
2075
|
if (!isDeliveryActive()) return;
|
|
2044
|
-
if (!richAttachmentDelivered
|
|
2076
|
+
if (!richAttachmentDelivered && !queuedAttachmentsDelivered) {
|
|
2077
|
+
await deps.sendQueuedAttachments(turn, { isDeliveryActive });
|
|
2078
|
+
}
|
|
2045
2079
|
if (!isDeliveryActive()) return;
|
|
2046
2080
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
2047
2081
|
};
|
|
@@ -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,7 @@ 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
898
|
});
|
|
899
899
|
};
|
|
900
900
|
}
|