@llblab/pi-kit 0.21.0 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +2 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-state-flow/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +17 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +4 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +29 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +33 -7
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +10 -2
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +4 -4
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +15 -6
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +24 -1
- package/node_modules/@llblab/pi-state-flow/lib/logging.ts +31 -6
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +2 -2
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +5 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +3 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +6 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +5 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +9 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +5 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +8 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +63 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +4 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +14 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +5 -0
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +7 -0
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +13 -2
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +19 -0
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +9 -0
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +75 -16
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +18 -0
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +7 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
|
@@ -52,6 +52,7 @@ export interface TelegramQueueBindingRuntime<TContext> {
|
|
|
52
52
|
mutation: Queue.TelegramQueueMutationController<TContext>;
|
|
53
53
|
dispatchNext: (ctx: TContext) => void;
|
|
54
54
|
requestNextDispatchAnnouncement: () => void;
|
|
55
|
+
cancelNextDispatchAnnouncement: () => void;
|
|
55
56
|
watchdog: Queue.TelegramQueueDispatchWatchdogRuntime<TContext>;
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -96,6 +97,9 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
|
|
|
96
97
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
97
98
|
sendTextReply: Queue.TelegramQueueDispatchRuntimeDeps<TContext>["sendTextReply"];
|
|
98
99
|
sendUserMessage: Queue.TelegramQueueDispatchRuntimeDeps<TContext>["sendUserMessage"];
|
|
100
|
+
reconcileNextDispatchAnnouncementReplyOwnership?: (
|
|
101
|
+
item: Queue.PendingTelegramTurn,
|
|
102
|
+
) => void;
|
|
99
103
|
recordRuntimeEvent?: TelegramRuntimeEventRecorder;
|
|
100
104
|
}): TelegramQueueBindingRuntime<TContext> {
|
|
101
105
|
const settleDiscardedItems = (
|
|
@@ -159,6 +163,8 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
|
|
|
159
163
|
},
|
|
160
164
|
updateStatus: deps.updateStatus,
|
|
161
165
|
sendTextReply: deps.sendTextReply,
|
|
166
|
+
reconcileNextDispatchAnnouncementReplyOwnership:
|
|
167
|
+
deps.reconcileNextDispatchAnnouncementReplyOwnership,
|
|
162
168
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
163
169
|
...deps.promptDispatch,
|
|
164
170
|
sendUserMessage: deps.sendUserMessage,
|
|
@@ -167,6 +173,7 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
|
|
|
167
173
|
mutation,
|
|
168
174
|
dispatchNext: dispatch.dispatchNext,
|
|
169
175
|
requestNextDispatchAnnouncement: dispatch.requestNextDispatchAnnouncement,
|
|
176
|
+
cancelNextDispatchAnnouncement: dispatch.cancelNextDispatchAnnouncement,
|
|
170
177
|
watchdog: Queue.createTelegramQueueDispatchWatchdogRuntime({
|
|
171
178
|
hasQueuedItems: deps.store.hasQueuedItems,
|
|
172
179
|
dispatchNextQueuedTelegramTurn: dispatch.dispatchNext,
|
|
@@ -11,7 +11,11 @@ import { basename } from "node:path";
|
|
|
11
11
|
import * as Sync from "./sync.ts";
|
|
12
12
|
import * as Threads from "./threads.ts";
|
|
13
13
|
import { parseTelegramUpdateJournalQueueOwner, type TelegramUpdateJournalStoreOptions } from "./journal.ts";
|
|
14
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
|
|
16
|
+
type TelegramLockEntry,
|
|
17
|
+
type TelegramLockState,
|
|
18
|
+
} from "./locks.ts";
|
|
15
19
|
import type {
|
|
16
20
|
TelegramQueueHandoffPayload,
|
|
17
21
|
TelegramQueueHandoffStageResult,
|
|
@@ -60,6 +64,8 @@ export const TELEGRAM_BUS_FOLLOWER_PROMOTION_GRACE_MS = 2_500;
|
|
|
60
64
|
export const TELEGRAM_FOLLOWER_SESSION_HANDOFF_TTL_MS = 30_000;
|
|
61
65
|
export const TELEGRAM_BUS_FOLLOWER_CLIENT_TIMEOUT_MS = 30_000;
|
|
62
66
|
export const TELEGRAM_BUS_FOLLOWER_REGISTRATION_WAIT_MS = 30_000;
|
|
67
|
+
export const TELEGRAM_BUS_FOLLOWER_HEARTBEAT_TIMEOUT_MS =
|
|
68
|
+
TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS;
|
|
63
69
|
export const TELEGRAM_BUS_FOLLOWER_REGISTRATION_RETRY_ATTEMPTS =
|
|
64
70
|
TELEGRAM_BUS_REGISTRATION_RETRY.attempts;
|
|
65
71
|
export const TELEGRAM_BUS_FOLLOWER_REGISTRATION_RETRY_DELAY_MS =
|
|
@@ -301,6 +307,7 @@ export interface TelegramBusFollowerRegistrationRuntimeDeps<
|
|
|
301
307
|
registrationRetryAttempts?: number;
|
|
302
308
|
registrationRetryDelayMs?: number;
|
|
303
309
|
heartbeatMs?: number;
|
|
310
|
+
heartbeatTimeoutMs?: number;
|
|
304
311
|
recordRuntimeEvent?: (
|
|
305
312
|
category: string,
|
|
306
313
|
error: unknown,
|
|
@@ -1619,6 +1626,10 @@ export function createTelegramBusFollowerRegistrationRuntime<
|
|
|
1619
1626
|
const getNowMs = deps.getNowMs ?? Date.now;
|
|
1620
1627
|
const getPid = deps.getPid ?? (() => process.pid);
|
|
1621
1628
|
const heartbeatMs = deps.heartbeatMs ?? 1000;
|
|
1629
|
+
const heartbeatTimeoutMs =
|
|
1630
|
+
deps.heartbeatTimeoutMs ??
|
|
1631
|
+
deps.timeoutMs ??
|
|
1632
|
+
TELEGRAM_BUS_FOLLOWER_HEARTBEAT_TIMEOUT_MS;
|
|
1622
1633
|
const registrationTimeoutMs =
|
|
1623
1634
|
deps.registrationTimeoutMs ?? deps.timeoutMs ?? 30000;
|
|
1624
1635
|
const registrationRetryAttempts =
|
|
@@ -1679,7 +1690,7 @@ export function createTelegramBusFollowerRegistrationRuntime<
|
|
|
1679
1690
|
try {
|
|
1680
1691
|
const response = await sendTelegramBusLocalEnvelope({
|
|
1681
1692
|
socketPath: leaderSocketPath,
|
|
1682
|
-
timeoutMs:
|
|
1693
|
+
timeoutMs: heartbeatTimeoutMs,
|
|
1683
1694
|
retry: getTelegramBusTransportRetryPolicy({
|
|
1684
1695
|
endpoint: leaderSocketPath,
|
|
1685
1696
|
operation: "operation",
|
|
@@ -711,6 +711,7 @@ export interface TelegramCommandActionDeps<TMessage, TContext> {
|
|
|
711
711
|
export interface TelegramStopCommandDeps {
|
|
712
712
|
hasAbortHandler: () => boolean;
|
|
713
713
|
clearPendingModelSwitch: () => void;
|
|
714
|
+
cancelNextTransitionAnnouncements?: () => void;
|
|
714
715
|
clearQueuedTelegramItems: () => number;
|
|
715
716
|
setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
|
|
716
717
|
abortCurrentTurn: () => void;
|
|
@@ -1146,6 +1147,8 @@ export interface TelegramCommandRuntimeDeps<
|
|
|
1146
1147
|
isContextActive?: (ctx: TContext) => boolean;
|
|
1147
1148
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
1148
1149
|
requestNextDispatchAnnouncement?: () => void;
|
|
1150
|
+
markActiveTurnNextAbortAnnouncement?: () => boolean;
|
|
1151
|
+
cancelNextTransitionAnnouncements?: () => void;
|
|
1149
1152
|
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
1150
1153
|
dispatch: (ctx: TContext) => void,
|
|
1151
1154
|
) => void;
|
|
@@ -1352,6 +1355,7 @@ export async function handleTelegramStopCommand(
|
|
|
1352
1355
|
deps: TelegramStopCommandDeps,
|
|
1353
1356
|
): Promise<void> {
|
|
1354
1357
|
deps.clearPendingModelSwitch();
|
|
1358
|
+
deps.cancelNextTransitionAnnouncements?.();
|
|
1355
1359
|
const clearedCount = deps.clearQueuedTelegramItems();
|
|
1356
1360
|
deps.setFoldQueuedPromptsIntoHistory(false);
|
|
1357
1361
|
if (!deps.hasAbortHandler()) {
|
|
@@ -1385,6 +1389,7 @@ export async function handleTelegramAbortCommand(deps: {
|
|
|
1385
1389
|
hasAbortHandler: () => boolean;
|
|
1386
1390
|
hasActiveTelegramTurn: () => boolean;
|
|
1387
1391
|
clearPendingModelSwitch: () => void;
|
|
1392
|
+
cancelNextTransitionAnnouncements?: () => void;
|
|
1388
1393
|
abortCurrentTurn: () => void;
|
|
1389
1394
|
setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
|
|
1390
1395
|
updateStatus: () => void;
|
|
@@ -1394,6 +1399,7 @@ export async function handleTelegramAbortCommand(deps: {
|
|
|
1394
1399
|
) => Promise<void>;
|
|
1395
1400
|
}): Promise<void> {
|
|
1396
1401
|
deps.clearPendingModelSwitch();
|
|
1402
|
+
deps.cancelNextTransitionAnnouncements?.();
|
|
1397
1403
|
if (!deps.hasAbortHandler()) {
|
|
1398
1404
|
await deps.sendTextReply(
|
|
1399
1405
|
formatTelegramInformationHeading("💤", "No active turn."),
|
|
@@ -1418,6 +1424,7 @@ export async function handleTelegramNextCommand(deps: {
|
|
|
1418
1424
|
abortCurrentTurn: () => void;
|
|
1419
1425
|
dispatchNextQueuedTurn: () => void;
|
|
1420
1426
|
requestNextDispatchAnnouncement?: () => void;
|
|
1427
|
+
markActiveTurnNextAbortAnnouncement?: () => boolean;
|
|
1421
1428
|
clearFoldForDispatch: () => void;
|
|
1422
1429
|
updateStatus: () => void;
|
|
1423
1430
|
sendTextReply: (
|
|
@@ -1439,6 +1446,7 @@ export async function handleTelegramNextCommand(deps: {
|
|
|
1439
1446
|
if (!deps.isIdle() && deps.hasAbortHandler()) {
|
|
1440
1447
|
deps.clearFoldForDispatch();
|
|
1441
1448
|
deps.requestNextDispatchAnnouncement?.();
|
|
1449
|
+
deps.markActiveTurnNextAbortAnnouncement?.();
|
|
1442
1450
|
deps.abortCurrentTurn();
|
|
1443
1451
|
deps.updateStatus();
|
|
1444
1452
|
return;
|
|
@@ -1880,6 +1888,11 @@ export function createTelegramCommandHandlerTargetRuntime<
|
|
|
1880
1888
|
updateStatus: deps.updateStatus,
|
|
1881
1889
|
isContextActive: deps.isContextActive,
|
|
1882
1890
|
dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
|
|
1891
|
+
requestNextDispatchAnnouncement: deps.requestNextDispatchAnnouncement,
|
|
1892
|
+
markActiveTurnNextAbortAnnouncement:
|
|
1893
|
+
deps.markActiveTurnNextAbortAnnouncement,
|
|
1894
|
+
cancelNextTransitionAnnouncements:
|
|
1895
|
+
deps.cancelNextTransitionAnnouncements,
|
|
1883
1896
|
startTypingLoop: deps.startTypingLoop,
|
|
1884
1897
|
stopTypingLoop: deps.stopTypingLoop,
|
|
1885
1898
|
enqueueContinueTurn: deps.enqueueContinueTurn,
|
|
@@ -2037,6 +2050,8 @@ async function handleTelegramCommandRuntime<
|
|
|
2037
2050
|
await handleTelegramStopCommand({
|
|
2038
2051
|
hasAbortHandler: deps.hasAbortHandler,
|
|
2039
2052
|
clearPendingModelSwitch: deps.clearPendingModelSwitch,
|
|
2053
|
+
cancelNextTransitionAnnouncements:
|
|
2054
|
+
deps.cancelNextTransitionAnnouncements,
|
|
2040
2055
|
clearQueuedTelegramItems: () =>
|
|
2041
2056
|
deps.clearQueuedTelegramItems(commandCtx),
|
|
2042
2057
|
setFoldQueuedPromptsIntoHistory: deps.setFoldQueuedPromptsIntoHistory,
|
|
@@ -2113,6 +2128,8 @@ async function handleTelegramCommandRuntime<
|
|
|
2113
2128
|
hasAbortHandler: deps.hasAbortHandler,
|
|
2114
2129
|
hasActiveTelegramTurn: deps.hasActiveTelegramTurn,
|
|
2115
2130
|
clearPendingModelSwitch: deps.clearPendingModelSwitch,
|
|
2131
|
+
cancelNextTransitionAnnouncements:
|
|
2132
|
+
deps.cancelNextTransitionAnnouncements,
|
|
2116
2133
|
abortCurrentTurn: deps.abortCurrentTurn,
|
|
2117
2134
|
setFoldQueuedPromptsIntoHistory: deps.setFoldQueuedPromptsIntoHistory,
|
|
2118
2135
|
updateStatus: updateStatusFor(commandCtx),
|
|
@@ -2129,6 +2146,8 @@ async function handleTelegramCommandRuntime<
|
|
|
2129
2146
|
dispatchNextQueuedTurn: () =>
|
|
2130
2147
|
deps.dispatchNextQueuedTelegramTurn(commandCtx),
|
|
2131
2148
|
requestNextDispatchAnnouncement: deps.requestNextDispatchAnnouncement,
|
|
2149
|
+
markActiveTurnNextAbortAnnouncement:
|
|
2150
|
+
deps.markActiveTurnNextAbortAnnouncement,
|
|
2132
2151
|
clearFoldForDispatch: () =>
|
|
2133
2152
|
deps.setFoldQueuedPromptsIntoHistory(false),
|
|
2134
2153
|
updateStatus: updateStatusFor(commandCtx),
|
|
@@ -820,6 +820,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
820
820
|
mutation: queueMutationRuntime,
|
|
821
821
|
dispatchNext: dispatchNextQueuedTelegramTurn,
|
|
822
822
|
requestNextDispatchAnnouncement,
|
|
823
|
+
cancelNextDispatchAnnouncement,
|
|
823
824
|
watchdog: queueDispatchWatchdogRuntime,
|
|
824
825
|
} = Bindings.createTelegramQueueBindingRuntime({
|
|
825
826
|
store: telegramQueueStore,
|
|
@@ -835,6 +836,13 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
835
836
|
updateStatus,
|
|
836
837
|
sendTextReply,
|
|
837
838
|
sendUserMessage,
|
|
839
|
+
reconcileNextDispatchAnnouncementReplyOwnership(item) {
|
|
840
|
+
Replies.preserveTransportReplyDedupOnNextReset(
|
|
841
|
+
item.chatId,
|
|
842
|
+
item.replyToMessageId,
|
|
843
|
+
item.target,
|
|
844
|
+
);
|
|
845
|
+
},
|
|
838
846
|
recordRuntimeEvent,
|
|
839
847
|
});
|
|
840
848
|
const { finalizeMarkdownPreview, preparePreviewDelivery } =
|
|
@@ -1093,6 +1101,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1093
1101
|
isContextActive: telegramSessionContextStore.isCurrent,
|
|
1094
1102
|
dispatchNextQueuedTelegramTurn,
|
|
1095
1103
|
requestNextDispatchAnnouncement,
|
|
1104
|
+
cancelNextDispatchAnnouncement,
|
|
1096
1105
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
1097
1106
|
deferredQueueDispatchRuntime.request,
|
|
1098
1107
|
hasDeferredDispatchContext: deferredQueueDispatchRuntime.isBound,
|
|
@@ -126,6 +126,8 @@ export interface PendingTelegramTurn extends TelegramQueueItemBase {
|
|
|
126
126
|
historyText: string;
|
|
127
127
|
priorityEmoji?: string;
|
|
128
128
|
reactionSuppressionEmoji?: string;
|
|
129
|
+
/** Emit the explicit aborted-turn notice when /next settles this active turn. */
|
|
130
|
+
announceNextAbortOnEnd?: boolean;
|
|
129
131
|
|
|
130
132
|
/** Turn should preferably be delivered as voice (mirror mode + user sent voice) */
|
|
131
133
|
voiceReplyPreferred?: boolean;
|
|
@@ -235,6 +237,8 @@ export interface TelegramActiveTurnStore<
|
|
|
235
237
|
has: () => boolean;
|
|
236
238
|
set: (turn: TTurn) => void;
|
|
237
239
|
clear: () => void;
|
|
240
|
+
markNextAbortAnnouncement: () => boolean;
|
|
241
|
+
clearNextAbortAnnouncement: () => boolean;
|
|
238
242
|
getChatId: () => number | undefined;
|
|
239
243
|
getTarget: () => TelegramQueueTarget | undefined;
|
|
240
244
|
getReplyToMessageId: () => number | undefined;
|
|
@@ -461,6 +465,16 @@ export function createTelegramActiveTurnStore<
|
|
|
461
465
|
clear: () => {
|
|
462
466
|
activeTurn = undefined;
|
|
463
467
|
},
|
|
468
|
+
markNextAbortAnnouncement: () => {
|
|
469
|
+
if (!activeTurn) return false;
|
|
470
|
+
activeTurn.announceNextAbortOnEnd = true;
|
|
471
|
+
return true;
|
|
472
|
+
},
|
|
473
|
+
clearNextAbortAnnouncement: () => {
|
|
474
|
+
if (!activeTurn?.announceNextAbortOnEnd) return false;
|
|
475
|
+
delete activeTurn.announceNextAbortOnEnd;
|
|
476
|
+
return true;
|
|
477
|
+
},
|
|
464
478
|
getChatId: () => activeTurn?.chatId,
|
|
465
479
|
getTarget: () =>
|
|
466
480
|
activeTurn?.target ? { ...activeTurn.target } : undefined,
|
|
@@ -1488,6 +1502,7 @@ export interface TelegramAgentEndPlan {
|
|
|
1488
1502
|
kind: "no-turn" | "aborted" | "error" | "text" | "attachments-only" | "empty";
|
|
1489
1503
|
shouldClearPreview: boolean;
|
|
1490
1504
|
shouldDispatchNext: boolean;
|
|
1505
|
+
shouldSendAbortMessage: boolean;
|
|
1491
1506
|
shouldSendErrorMessage: boolean;
|
|
1492
1507
|
shouldSendAttachmentNotice: boolean;
|
|
1493
1508
|
}
|
|
@@ -1680,6 +1695,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1680
1695
|
hasFinalText: boolean;
|
|
1681
1696
|
hasQueuedAttachments: boolean;
|
|
1682
1697
|
foldQueuedPromptsIntoHistory: boolean;
|
|
1698
|
+
announceNextAbortOnEnd?: boolean;
|
|
1683
1699
|
}): TelegramAgentEndPlan {
|
|
1684
1700
|
const shouldDispatchNext = shouldDispatchAfterTelegramAgentEnd({
|
|
1685
1701
|
hasTurn: options.hasTurn,
|
|
@@ -1691,6 +1707,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1691
1707
|
kind: "no-turn",
|
|
1692
1708
|
shouldClearPreview: false,
|
|
1693
1709
|
shouldDispatchNext,
|
|
1710
|
+
shouldSendAbortMessage: false,
|
|
1694
1711
|
shouldSendErrorMessage: false,
|
|
1695
1712
|
shouldSendAttachmentNotice: false,
|
|
1696
1713
|
};
|
|
@@ -1700,6 +1717,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1700
1717
|
kind: "aborted",
|
|
1701
1718
|
shouldClearPreview: true,
|
|
1702
1719
|
shouldDispatchNext,
|
|
1720
|
+
shouldSendAbortMessage: options.announceNextAbortOnEnd === true,
|
|
1703
1721
|
shouldSendErrorMessage: false,
|
|
1704
1722
|
shouldSendAttachmentNotice: false,
|
|
1705
1723
|
};
|
|
@@ -1709,6 +1727,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1709
1727
|
kind: "error",
|
|
1710
1728
|
shouldClearPreview: true,
|
|
1711
1729
|
shouldDispatchNext,
|
|
1730
|
+
shouldSendAbortMessage: false,
|
|
1712
1731
|
shouldSendErrorMessage: true,
|
|
1713
1732
|
shouldSendAttachmentNotice: false,
|
|
1714
1733
|
};
|
|
@@ -1718,6 +1737,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1718
1737
|
kind: "text",
|
|
1719
1738
|
shouldClearPreview: false,
|
|
1720
1739
|
shouldDispatchNext,
|
|
1740
|
+
shouldSendAbortMessage: false,
|
|
1721
1741
|
shouldSendErrorMessage: false,
|
|
1722
1742
|
shouldSendAttachmentNotice: false,
|
|
1723
1743
|
};
|
|
@@ -1727,6 +1747,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1727
1747
|
kind: "attachments-only",
|
|
1728
1748
|
shouldClearPreview: true,
|
|
1729
1749
|
shouldDispatchNext,
|
|
1750
|
+
shouldSendAbortMessage: false,
|
|
1730
1751
|
shouldSendErrorMessage: false,
|
|
1731
1752
|
shouldSendAttachmentNotice: true,
|
|
1732
1753
|
};
|
|
@@ -1735,6 +1756,7 @@ export function buildTelegramAgentEndPlan(options: {
|
|
|
1735
1756
|
kind: "empty",
|
|
1736
1757
|
shouldClearPreview: true,
|
|
1737
1758
|
shouldDispatchNext,
|
|
1759
|
+
shouldSendAbortMessage: false,
|
|
1738
1760
|
shouldSendErrorMessage: false,
|
|
1739
1761
|
shouldSendAttachmentNotice: false,
|
|
1740
1762
|
};
|
|
@@ -1891,6 +1913,7 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1891
1913
|
hasFinalText: !!finalText || hasOutboundArtifacts,
|
|
1892
1914
|
hasQueuedAttachments: (turn?.queuedAttachments.length ?? 0) > 0,
|
|
1893
1915
|
foldQueuedPromptsIntoHistory: deps.foldQueuedPromptsIntoHistory,
|
|
1916
|
+
announceNextAbortOnEnd: turn?.announceNextAbortOnEnd === true,
|
|
1894
1917
|
});
|
|
1895
1918
|
if (!turn) {
|
|
1896
1919
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
@@ -2017,22 +2040,29 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2017
2040
|
await clearTurnPreview();
|
|
2018
2041
|
if (!isDeliveryActive()) return;
|
|
2019
2042
|
}
|
|
2020
|
-
if (endPlan.shouldSendErrorMessage) {
|
|
2043
|
+
if (endPlan.shouldSendAbortMessage || endPlan.shouldSendErrorMessage) {
|
|
2021
2044
|
const errorMessage = assistant.errorMessage ||
|
|
2022
2045
|
"Telegram bridge: Pi failed while processing the request.";
|
|
2023
|
-
const isOperationAborted =
|
|
2024
|
-
"This operation was aborted";
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2046
|
+
const isOperationAborted = endPlan.shouldSendAbortMessage ||
|
|
2047
|
+
errorMessage.trim().replace(/\.$/, "") === "This operation was aborted";
|
|
2048
|
+
try {
|
|
2049
|
+
await deps.sendTextReply(
|
|
2050
|
+
turn.chatId,
|
|
2051
|
+
turn.replyToMessageId,
|
|
2052
|
+
isOperationAborted
|
|
2053
|
+
? "<b>⏹️ This operation was aborted.</b>"
|
|
2054
|
+
: errorMessage,
|
|
2055
|
+
{
|
|
2056
|
+
target: turn.target,
|
|
2057
|
+
...(isOperationAborted ? { parseMode: "HTML" as const } : {}),
|
|
2058
|
+
},
|
|
2059
|
+
);
|
|
2060
|
+
} catch (error) {
|
|
2061
|
+
if (!isOperationAborted) throw error;
|
|
2062
|
+
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
2063
|
+
phase: "next-abort-announcement",
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2036
2066
|
if (!isDeliveryActive()) return;
|
|
2037
2067
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
2038
2068
|
return;
|
|
@@ -2180,7 +2210,7 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2180
2210
|
};
|
|
2181
2211
|
if (
|
|
2182
2212
|
deps.scheduleActiveTurnDelivery &&
|
|
2183
|
-
(endPlan.kind === "text" || endPlan.kind === "attachments-only" || endPlan.shouldSendErrorMessage || endPlan.shouldClearPreview)
|
|
2213
|
+
(endPlan.kind === "text" || endPlan.kind === "attachments-only" || endPlan.shouldSendAbortMessage || endPlan.shouldSendErrorMessage || endPlan.shouldClearPreview)
|
|
2184
2214
|
) {
|
|
2185
2215
|
deps.scheduleActiveTurnDelivery(deliverActiveTurn);
|
|
2186
2216
|
return;
|
|
@@ -3092,6 +3122,9 @@ export interface TelegramQueueDispatchControllerDeps<
|
|
|
3092
3122
|
) => boolean;
|
|
3093
3123
|
sendUserMessage: TelegramDispatchRuntimeDeps<TContext>["sendUserMessage"];
|
|
3094
3124
|
onPromptDispatchFailure: (ctx: TContext, message: string) => void;
|
|
3125
|
+
reconcileNextDispatchAnnouncementReplyOwnership?: (
|
|
3126
|
+
item: PendingTelegramTurn,
|
|
3127
|
+
) => void;
|
|
3095
3128
|
isQueueItemTransportActive?: (item: TelegramQueueItem<TContext>) => boolean;
|
|
3096
3129
|
hasPendingInboundQueueMutationForItem?: (
|
|
3097
3130
|
item: TelegramQueueItem<TContext>,
|
|
@@ -3107,6 +3140,7 @@ export interface TelegramQueueDispatchControllerDeps<
|
|
|
3107
3140
|
export interface TelegramQueueDispatchController<TContext = unknown> {
|
|
3108
3141
|
dispatchNext: (ctx: TContext) => void;
|
|
3109
3142
|
requestNextDispatchAnnouncement: () => void;
|
|
3143
|
+
cancelNextDispatchAnnouncement: () => void;
|
|
3110
3144
|
}
|
|
3111
3145
|
|
|
3112
3146
|
export function executeTelegramQueueDispatchPlan<TContext = unknown>(
|
|
@@ -3161,6 +3195,8 @@ export function createTelegramQueueDispatchRuntime<TContext = unknown>(
|
|
|
3161
3195
|
commitPromptDispatch: deps.commitPromptDispatch,
|
|
3162
3196
|
sendUserMessage: deps.sendUserMessage,
|
|
3163
3197
|
onPromptDispatchFailure: deps.onPromptDispatchFailure,
|
|
3198
|
+
reconcileNextDispatchAnnouncementReplyOwnership:
|
|
3199
|
+
deps.reconcileNextDispatchAnnouncementReplyOwnership,
|
|
3164
3200
|
isQueueItemTransportActive: deps.isQueueItemTransportActive,
|
|
3165
3201
|
hasPendingInboundQueueMutationForItem:
|
|
3166
3202
|
deps.hasPendingInboundQueueMutationForItem,
|
|
@@ -3176,12 +3212,19 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3176
3212
|
): TelegramQueueDispatchController<TContext> {
|
|
3177
3213
|
let controlDispatchPending = false;
|
|
3178
3214
|
let nextDispatchAnnouncementRequested = false;
|
|
3215
|
+
let nextDispatchAnnouncementGeneration = 0;
|
|
3179
3216
|
let nextDispatchAnnouncementAnchor: TelegramQueueItem<TContext> | undefined;
|
|
3180
3217
|
const controller: TelegramQueueDispatchController<TContext> = {
|
|
3181
3218
|
requestNextDispatchAnnouncement: () => {
|
|
3219
|
+
nextDispatchAnnouncementGeneration += 1;
|
|
3182
3220
|
nextDispatchAnnouncementRequested = true;
|
|
3183
3221
|
nextDispatchAnnouncementAnchor = undefined;
|
|
3184
3222
|
},
|
|
3223
|
+
cancelNextDispatchAnnouncement: () => {
|
|
3224
|
+
nextDispatchAnnouncementGeneration += 1;
|
|
3225
|
+
nextDispatchAnnouncementRequested = false;
|
|
3226
|
+
nextDispatchAnnouncementAnchor = undefined;
|
|
3227
|
+
},
|
|
3185
3228
|
dispatchNext: (ctx) => {
|
|
3186
3229
|
if (deps.hasDispatchContext && !deps.hasDispatchContext()) return;
|
|
3187
3230
|
if (controlDispatchPending) {
|
|
@@ -3393,6 +3436,7 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3393
3436
|
if (dispatchPlan.kind === "prompt" && nextDispatchAnnouncementRequested) {
|
|
3394
3437
|
nextDispatchAnnouncementRequested = false;
|
|
3395
3438
|
controlDispatchPending = true;
|
|
3439
|
+
const announcementGeneration = nextDispatchAnnouncementGeneration;
|
|
3396
3440
|
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
3397
3441
|
deps.updateStatus(ctx);
|
|
3398
3442
|
void deps.sendTextReply(
|
|
@@ -3401,8 +3445,19 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3401
3445
|
"<b>⏩ Dispatching next queued turn.</b>",
|
|
3402
3446
|
{ target: dispatchPlan.item.target },
|
|
3403
3447
|
).catch((error) => {
|
|
3404
|
-
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3448
|
+
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3449
|
+
phase: "next-announcement",
|
|
3450
|
+
});
|
|
3405
3451
|
}).finally(() => {
|
|
3452
|
+
try {
|
|
3453
|
+
deps.reconcileNextDispatchAnnouncementReplyOwnership?.(
|
|
3454
|
+
dispatchPlan.item,
|
|
3455
|
+
);
|
|
3456
|
+
} catch (error) {
|
|
3457
|
+
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3458
|
+
phase: "next-announcement-reply-ownership",
|
|
3459
|
+
});
|
|
3460
|
+
}
|
|
3406
3461
|
controlDispatchPending = false;
|
|
3407
3462
|
if (deps.hasDispatchContext && !deps.hasDispatchContext()) return;
|
|
3408
3463
|
if (
|
|
@@ -3410,6 +3465,10 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3410
3465
|
deps.isDispatchGenerationActive &&
|
|
3411
3466
|
!deps.isDispatchGenerationActive(dispatchGeneration)
|
|
3412
3467
|
) return;
|
|
3468
|
+
if (announcementGeneration !== nextDispatchAnnouncementGeneration) {
|
|
3469
|
+
if (nextDispatchAnnouncementRequested) controller.dispatchNext(ctx);
|
|
3470
|
+
return;
|
|
3471
|
+
}
|
|
3413
3472
|
executePlan();
|
|
3414
3473
|
});
|
|
3415
3474
|
return;
|
|
@@ -68,6 +68,7 @@ export function createReplyDedupRuntime(): ReplyDedupRuntime {
|
|
|
68
68
|
// --- Transport-level dedup ---
|
|
69
69
|
|
|
70
70
|
const lastRepliedToMessageIdByTarget = new Map<string, number>();
|
|
71
|
+
const replyDedupPreservedOnNextReset = new Map<string, number>();
|
|
71
72
|
let replyDedupGeneration = 0;
|
|
72
73
|
|
|
73
74
|
function getReplyDedupTargetKey(
|
|
@@ -83,6 +84,23 @@ function getReplyDedupTargetKey(
|
|
|
83
84
|
export function resetTransportReplyDedup(): void {
|
|
84
85
|
replyDedupGeneration += 1;
|
|
85
86
|
lastRepliedToMessageIdByTarget.clear();
|
|
87
|
+
for (const [key, messageId] of replyDedupPreservedOnNextReset) {
|
|
88
|
+
lastRepliedToMessageIdByTarget.set(key, messageId);
|
|
89
|
+
}
|
|
90
|
+
replyDedupPreservedOnNextReset.clear();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Keeps a successfully published transition notice as the first reply of the
|
|
94
|
+
* next agent turn. The following agent-start reset consumes this one-shot
|
|
95
|
+
* preservation, so later messages in that turn do not repeat the reply header. */
|
|
96
|
+
export function preserveTransportReplyDedupOnNextReset(
|
|
97
|
+
chatId: number,
|
|
98
|
+
messageId: number,
|
|
99
|
+
target?: TelegramTarget,
|
|
100
|
+
): void {
|
|
101
|
+
const key = getReplyDedupTargetKey(chatId, target);
|
|
102
|
+
if (lastRepliedToMessageIdByTarget.get(key) !== messageId) return;
|
|
103
|
+
replyDedupPreservedOnNextReset.set(key, messageId);
|
|
86
104
|
}
|
|
87
105
|
|
|
88
106
|
export function buildTelegramReplyParameters(
|
|
@@ -627,6 +627,7 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
627
627
|
isContextActive?: (ctx: TContext) => boolean;
|
|
628
628
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
629
629
|
requestNextDispatchAnnouncement?: () => void;
|
|
630
|
+
cancelNextDispatchAnnouncement?: () => void;
|
|
630
631
|
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
631
632
|
dispatch: (ctx: TContext) => void,
|
|
632
633
|
) => void;
|
|
@@ -2222,6 +2223,10 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2222
2223
|
isContextActive: deps.isContextActive,
|
|
2223
2224
|
dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
|
|
2224
2225
|
requestNextDispatchAnnouncement: deps.requestNextDispatchAnnouncement,
|
|
2226
|
+
cancelNextTransitionAnnouncements: () => {
|
|
2227
|
+
deps.activeTurnRuntime.clearNextAbortAnnouncement();
|
|
2228
|
+
deps.cancelNextDispatchAnnouncement?.();
|
|
2229
|
+
},
|
|
2225
2230
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
2226
2231
|
deps.requestDeferredDispatchNextQueuedTelegramTurn,
|
|
2227
2232
|
startTypingLoop: deps.startTypingLoop,
|
|
@@ -2297,6 +2302,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2297
2302
|
},
|
|
2298
2303
|
getPromptTemplateCommands,
|
|
2299
2304
|
sendTextReply: deps.sendTextReply,
|
|
2305
|
+
markActiveTurnNextAbortAnnouncement:
|
|
2306
|
+
deps.activeTurnRuntime.markNextAbortAnnouncement,
|
|
2300
2307
|
getActiveTurnReply: () => {
|
|
2301
2308
|
const activeTurn = deps.activeTurnRuntime.get();
|
|
2302
2309
|
if (!activeTurn) return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@llblab/pi-clean-room": "0.1.1",
|
|
45
45
|
"@llblab/pi-codex-usage": "0.10.0",
|
|
46
46
|
"@llblab/pi-grow-loop": "0.8.1",
|
|
47
|
-
"@llblab/pi-state-flow": "0.18.
|
|
48
|
-
"@llblab/pi-telegram": "0.51.
|
|
47
|
+
"@llblab/pi-state-flow": "0.18.1",
|
|
48
|
+
"@llblab/pi-telegram": "0.51.1",
|
|
49
49
|
"@llblab/skills": "1.15.0"
|
|
50
50
|
},
|
|
51
51
|
"bundledDependencies": [
|