@llblab/pi-telegram 0.52.2 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +11 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +61 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +249 -126
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +125 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +511 -300
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/lib/queue.ts
CHANGED
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
|
|
9
|
+
import { isPiStaleContextError } from "./pi.ts";
|
|
10
|
+
import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
|
|
9
11
|
import { isVoiceTurn } from "./voice.ts";
|
|
10
12
|
import { isWireRecord as isQueuePayloadRecord } from "./wire.ts";
|
|
11
|
-
import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
|
|
12
|
-
|
|
13
|
-
// --- Queue Items ---
|
|
14
13
|
|
|
15
14
|
export interface QueuedAttachment {
|
|
16
15
|
path: string;
|
|
@@ -164,8 +163,7 @@ export interface TelegramQueueHandoffBase {
|
|
|
164
163
|
admissionReceipts: TelegramQueueAdmissionReceipt[];
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
export interface TelegramPromptQueueHandoffPayload
|
|
168
|
-
extends TelegramQueueHandoffBase {
|
|
166
|
+
export interface TelegramPromptQueueHandoffPayload extends TelegramQueueHandoffBase {
|
|
169
167
|
kind: "prompt";
|
|
170
168
|
sourceMessageIds: number[];
|
|
171
169
|
queuedAttachments: QueuedAttachment[];
|
|
@@ -177,76 +175,182 @@ export interface TelegramPromptQueueHandoffPayload
|
|
|
177
175
|
voiceReplyRequired?: boolean;
|
|
178
176
|
}
|
|
179
177
|
|
|
180
|
-
export interface TelegramControlQueueHandoffPayload
|
|
181
|
-
extends TelegramQueueHandoffBase {
|
|
178
|
+
export interface TelegramControlQueueHandoffPayload extends TelegramQueueHandoffBase {
|
|
182
179
|
kind: "control";
|
|
183
180
|
controlType: PendingTelegramControlItem<unknown>["controlType"];
|
|
184
181
|
}
|
|
185
182
|
|
|
186
183
|
export type TelegramQueueHandoffPayload =
|
|
187
|
-
|
|
|
188
|
-
| TelegramControlQueueHandoffPayload;
|
|
184
|
+
TelegramPromptQueueHandoffPayload | TelegramControlQueueHandoffPayload;
|
|
189
185
|
|
|
190
186
|
export interface TelegramQueueHandoff {
|
|
191
187
|
handoffToken: string;
|
|
192
188
|
payload: TelegramQueueHandoffPayload;
|
|
193
189
|
}
|
|
194
190
|
|
|
195
|
-
function parseQueuePayloadReceipt(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
191
|
+
function parseQueuePayloadReceipt(
|
|
192
|
+
value: unknown,
|
|
193
|
+
kind: TelegramQueueItemKind,
|
|
194
|
+
): TelegramQueueAdmissionReceipt | undefined {
|
|
195
|
+
if (
|
|
196
|
+
!isQueuePayloadRecord(value) ||
|
|
197
|
+
value.queueKind !== kind ||
|
|
198
|
+
typeof value.receiptId !== "string" ||
|
|
199
|
+
!value.receiptId ||
|
|
200
|
+
(value.journalBindingKey !== undefined &&
|
|
201
|
+
(typeof value.journalBindingKey !== "string" ||
|
|
202
|
+
!value.journalBindingKey.trim())) ||
|
|
203
|
+
!Array.isArray(value.sourceUpdateIds) ||
|
|
204
|
+
value.sourceUpdateIds.length === 0 ||
|
|
205
|
+
value.sourceUpdateIds.some(
|
|
206
|
+
(id, index) =>
|
|
207
|
+
!Number.isSafeInteger(id) ||
|
|
208
|
+
id < 0 ||
|
|
209
|
+
(index > 0 && id <= (value.sourceUpdateIds as number[])[index - 1]!),
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
return undefined;
|
|
213
|
+
return {
|
|
214
|
+
queueKind: kind,
|
|
215
|
+
receiptId: value.receiptId,
|
|
216
|
+
sourceUpdateIds: value.sourceUpdateIds as number[],
|
|
217
|
+
...(typeof value.journalBindingKey === "string"
|
|
218
|
+
? { journalBindingKey: value.journalBindingKey }
|
|
219
|
+
: {}),
|
|
220
|
+
};
|
|
204
221
|
}
|
|
205
222
|
|
|
206
223
|
/** Data decoder shared by authenticated IPC and cold storage; decoding grants no admission. */
|
|
207
|
-
export function parseTelegramQueueHandoffPayload(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
export function parseTelegramQueueHandoffPayload(
|
|
225
|
+
value: unknown,
|
|
226
|
+
): TelegramQueueHandoffPayload | undefined {
|
|
227
|
+
if (
|
|
228
|
+
!isQueuePayloadRecord(value) ||
|
|
229
|
+
(value.kind !== "prompt" && value.kind !== "control")
|
|
230
|
+
)
|
|
231
|
+
return undefined;
|
|
232
|
+
const target =
|
|
233
|
+
isQueuePayloadRecord(value.target) &&
|
|
234
|
+
typeof value.target.chatId === "number"
|
|
235
|
+
? {
|
|
236
|
+
chatId: value.target.chatId,
|
|
237
|
+
...(typeof value.target.threadId === "number"
|
|
238
|
+
? { threadId: value.target.threadId }
|
|
239
|
+
: {}),
|
|
240
|
+
}
|
|
241
|
+
: undefined;
|
|
242
|
+
const transportStamp =
|
|
243
|
+
isQueuePayloadRecord(value.transportStamp) &&
|
|
244
|
+
typeof value.transportStamp.profile === "string" &&
|
|
245
|
+
typeof value.transportStamp.generation === "string"
|
|
246
|
+
? {
|
|
247
|
+
profile: value.transportStamp.profile,
|
|
248
|
+
generation: value.transportStamp.generation,
|
|
249
|
+
}
|
|
250
|
+
: undefined;
|
|
251
|
+
if (
|
|
252
|
+
!Number.isSafeInteger(value.chatId) ||
|
|
253
|
+
(value.target !== undefined && !target) ||
|
|
254
|
+
(value.transportStamp !== undefined && !transportStamp) ||
|
|
255
|
+
!Number.isSafeInteger(value.replyToMessageId) ||
|
|
256
|
+
(value.guestQueryId !== undefined &&
|
|
257
|
+
typeof value.guestQueryId !== "string") ||
|
|
258
|
+
(value.guestInlineMessageId !== undefined &&
|
|
259
|
+
typeof value.guestInlineMessageId !== "string") ||
|
|
260
|
+
!Number.isSafeInteger(value.queueOrder) ||
|
|
261
|
+
(value.queueLane !== "control" &&
|
|
262
|
+
value.queueLane !== "priority" &&
|
|
263
|
+
value.queueLane !== "default") ||
|
|
264
|
+
!Number.isSafeInteger(value.laneOrder) ||
|
|
265
|
+
typeof value.statusSummary !== "string" ||
|
|
266
|
+
!Array.isArray(value.admissionReceipts)
|
|
267
|
+
)
|
|
268
|
+
return undefined;
|
|
269
|
+
const admissionReceipts = value.admissionReceipts.map((receipt) =>
|
|
270
|
+
parseQueuePayloadReceipt(receipt, value.kind as TelegramQueueItemKind),
|
|
271
|
+
);
|
|
272
|
+
if (
|
|
273
|
+
admissionReceipts.length === 0 ||
|
|
274
|
+
admissionReceipts.length > TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS ||
|
|
275
|
+
admissionReceipts.some((receipt) => !receipt)
|
|
276
|
+
)
|
|
277
|
+
return undefined;
|
|
278
|
+
const base = {
|
|
279
|
+
chatId: value.chatId as number,
|
|
280
|
+
...(target ? { target } : {}),
|
|
281
|
+
...(transportStamp ? { transportStamp } : {}),
|
|
224
282
|
replyToMessageId: value.replyToMessageId as number,
|
|
225
|
-
...(typeof value.guestQueryId === "string"
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
283
|
+
...(typeof value.guestQueryId === "string"
|
|
284
|
+
? { guestQueryId: value.guestQueryId }
|
|
285
|
+
: {}),
|
|
286
|
+
...(typeof value.guestInlineMessageId === "string"
|
|
287
|
+
? { guestInlineMessageId: value.guestInlineMessageId }
|
|
288
|
+
: {}),
|
|
289
|
+
queueOrder: value.queueOrder as number,
|
|
290
|
+
queueLane: value.queueLane as TelegramQueueLane,
|
|
291
|
+
laneOrder: value.laneOrder as number,
|
|
292
|
+
statusSummary: value.statusSummary,
|
|
293
|
+
admissionReceipts: admissionReceipts as TelegramQueueAdmissionReceipt[],
|
|
294
|
+
};
|
|
230
295
|
if (value.kind === "control") {
|
|
231
|
-
return value.queueLane === "control" &&
|
|
232
|
-
|
|
296
|
+
return value.queueLane === "control" &&
|
|
297
|
+
(value.controlType === "status" || value.controlType === "model")
|
|
298
|
+
? { kind: "control", controlType: value.controlType, ...base }
|
|
299
|
+
: undefined;
|
|
233
300
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
301
|
+
if (
|
|
302
|
+
value.queueLane === "control" ||
|
|
303
|
+
!Array.isArray(value.sourceMessageIds) ||
|
|
304
|
+
value.sourceMessageIds.some((id) => !Number.isSafeInteger(id)) ||
|
|
305
|
+
!Array.isArray(value.queuedAttachments) ||
|
|
306
|
+
value.queuedAttachments.some(
|
|
307
|
+
(attachment) =>
|
|
308
|
+
!isQueuePayloadRecord(attachment) ||
|
|
309
|
+
typeof attachment.path !== "string" ||
|
|
310
|
+
typeof attachment.fileName !== "string",
|
|
311
|
+
) ||
|
|
312
|
+
!Array.isArray(value.content) ||
|
|
313
|
+
value.content.some(
|
|
314
|
+
(content) =>
|
|
315
|
+
!isQueuePayloadRecord(content) ||
|
|
316
|
+
(content.type === "text"
|
|
317
|
+
? typeof content.text !== "string"
|
|
318
|
+
: content.type === "image"
|
|
319
|
+
? typeof content.data !== "string" ||
|
|
320
|
+
typeof content.mimeType !== "string"
|
|
321
|
+
: true),
|
|
322
|
+
) ||
|
|
323
|
+
typeof value.historyText !== "string" ||
|
|
324
|
+
(value.priorityEmoji !== undefined &&
|
|
325
|
+
typeof value.priorityEmoji !== "string") ||
|
|
326
|
+
(value.reactionSuppressionEmoji !== undefined &&
|
|
327
|
+
typeof value.reactionSuppressionEmoji !== "string") ||
|
|
328
|
+
(value.voiceReplyPreferred !== undefined &&
|
|
329
|
+
typeof value.voiceReplyPreferred !== "boolean") ||
|
|
330
|
+
(value.voiceReplyRequired !== undefined &&
|
|
331
|
+
typeof value.voiceReplyRequired !== "boolean")
|
|
332
|
+
)
|
|
333
|
+
return undefined;
|
|
334
|
+
return {
|
|
335
|
+
kind: "prompt",
|
|
336
|
+
...base,
|
|
337
|
+
sourceMessageIds: value.sourceMessageIds as number[],
|
|
338
|
+
queuedAttachments: value.queuedAttachments as QueuedAttachment[],
|
|
339
|
+
content: value.content as TelegramPromptContent[],
|
|
340
|
+
historyText: value.historyText,
|
|
341
|
+
...(typeof value.priorityEmoji === "string"
|
|
342
|
+
? { priorityEmoji: value.priorityEmoji }
|
|
343
|
+
: {}),
|
|
344
|
+
...(typeof value.reactionSuppressionEmoji === "string"
|
|
345
|
+
? { reactionSuppressionEmoji: value.reactionSuppressionEmoji }
|
|
346
|
+
: {}),
|
|
347
|
+
...(typeof value.voiceReplyPreferred === "boolean"
|
|
348
|
+
? { voiceReplyPreferred: value.voiceReplyPreferred }
|
|
349
|
+
: {}),
|
|
350
|
+
...(typeof value.voiceReplyRequired === "boolean"
|
|
351
|
+
? { voiceReplyRequired: value.voiceReplyRequired }
|
|
352
|
+
: {}),
|
|
353
|
+
};
|
|
250
354
|
}
|
|
251
355
|
|
|
252
356
|
export interface TelegramQueueHandoffStageReceipt {
|
|
@@ -265,13 +369,14 @@ export interface TelegramQueueHandoffAcceptedOwner {
|
|
|
265
369
|
handoffId?: string;
|
|
266
370
|
}
|
|
267
371
|
|
|
268
|
-
export interface TelegramQueueHandoffStageResult
|
|
269
|
-
extends TelegramQueueHandoffStageReceipt {
|
|
372
|
+
export interface TelegramQueueHandoffStageResult extends TelegramQueueHandoffStageReceipt {
|
|
270
373
|
queueOwner: TelegramQueueHandoffAcceptedOwner;
|
|
271
374
|
}
|
|
272
375
|
|
|
273
376
|
export interface TelegramQueueHandoffStagingRuntime {
|
|
274
|
-
stage: (
|
|
377
|
+
stage: (
|
|
378
|
+
payload: TelegramQueueHandoffPayload,
|
|
379
|
+
) => TelegramQueueHandoffStageReceipt;
|
|
275
380
|
accept: (receipt: TelegramQueueAdmissionReceipt) => boolean;
|
|
276
381
|
cancel: (receipt: TelegramQueueAdmissionReceipt) => boolean;
|
|
277
382
|
hasStaged: (receipt: TelegramQueueAdmissionReceipt) => boolean;
|
|
@@ -333,7 +438,9 @@ export function createTelegramQueueAdmissionReceipt(options: {
|
|
|
333
438
|
(updateId) => !Number.isSafeInteger(updateId) || updateId < 0,
|
|
334
439
|
)
|
|
335
440
|
) {
|
|
336
|
-
throw new Error(
|
|
441
|
+
throw new Error(
|
|
442
|
+
"Telegram queue admission update ids must be safe integers.",
|
|
443
|
+
);
|
|
337
444
|
}
|
|
338
445
|
const digest = createHash("sha256")
|
|
339
446
|
.update(
|
|
@@ -352,14 +459,6 @@ export function createTelegramQueueAdmissionReceipt(options: {
|
|
|
352
459
|
};
|
|
353
460
|
}
|
|
354
461
|
|
|
355
|
-
export function isTelegramQueueItemDurablyAdmitted<TContext = unknown>(
|
|
356
|
-
item: TelegramQueueItem<TContext>,
|
|
357
|
-
isReceiptCommitted: (receipt: TelegramQueueAdmissionReceipt) => boolean,
|
|
358
|
-
): boolean {
|
|
359
|
-
assertTelegramQueueItemAdmissionValid(item);
|
|
360
|
-
return (item.admissionReceipts ?? []).every(isReceiptCommitted);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
462
|
export function getTelegramQueueLaneContract(
|
|
364
463
|
lane: TelegramQueueLane,
|
|
365
464
|
): TelegramQueueLaneContract {
|
|
@@ -370,12 +469,6 @@ export function getTelegramQueueLaneContract(
|
|
|
370
469
|
return contract;
|
|
371
470
|
}
|
|
372
471
|
|
|
373
|
-
export function getTelegramQueueItemAdmissionMode(
|
|
374
|
-
item: Pick<TelegramQueueItem, "queueLane">,
|
|
375
|
-
): TelegramQueueAdmissionMode {
|
|
376
|
-
return getTelegramQueueLaneContract(item.queueLane).admissionMode;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
472
|
export function isTelegramQueueItemAdmissionValid(
|
|
380
473
|
item: Pick<TelegramQueueItem, "kind" | "queueLane">,
|
|
381
474
|
): boolean {
|
|
@@ -385,10 +478,7 @@ export function isTelegramQueueItemAdmissionValid(
|
|
|
385
478
|
}
|
|
386
479
|
|
|
387
480
|
export function assertTelegramQueueItemAdmissionValid(
|
|
388
|
-
item: Pick<
|
|
389
|
-
TelegramQueueItem,
|
|
390
|
-
"kind" | "queueLane" | "admissionReceipts"
|
|
391
|
-
>,
|
|
481
|
+
item: Pick<TelegramQueueItem, "kind" | "queueLane" | "admissionReceipts">,
|
|
392
482
|
): void {
|
|
393
483
|
if (!isTelegramQueueItemAdmissionValid(item)) {
|
|
394
484
|
throw new Error(
|
|
@@ -413,9 +503,7 @@ export function assertTelegramQueueItemAdmissionValid(
|
|
|
413
503
|
(index > 0 && updateId <= receipt.sourceUpdateIds[index - 1]!),
|
|
414
504
|
)
|
|
415
505
|
) {
|
|
416
|
-
throw new Error(
|
|
417
|
-
`Invalid Telegram queue receipt for ${item.kind} item`,
|
|
418
|
-
);
|
|
506
|
+
throw new Error(`Invalid Telegram queue receipt for ${item.kind} item`);
|
|
419
507
|
}
|
|
420
508
|
receiptIds.add(receipt.receiptId);
|
|
421
509
|
}
|
|
@@ -542,6 +630,42 @@ export function createTelegramActiveTurnStore<
|
|
|
542
630
|
};
|
|
543
631
|
}
|
|
544
632
|
|
|
633
|
+
/** Current captured targets only; skipped/receipt-held items remain work, never historical census. */
|
|
634
|
+
export function observeTelegramTargetQueueWork(
|
|
635
|
+
target: TelegramQueueTarget & { threadId: number },
|
|
636
|
+
activeTurn: Pick<TelegramQueueItemBase, "chatId" | "target"> | undefined,
|
|
637
|
+
items: readonly Pick<TelegramQueueItemBase, "chatId" | "target">[],
|
|
638
|
+
): { protected: boolean; unknown: boolean } {
|
|
639
|
+
let protectedWork = false,
|
|
640
|
+
unknown =
|
|
641
|
+
!Number.isSafeInteger(target.chatId) ||
|
|
642
|
+
!Number.isSafeInteger(target.threadId) ||
|
|
643
|
+
target.threadId <= 0;
|
|
644
|
+
const observe = (item: Pick<TelegramQueueItemBase, "chatId" | "target">) => {
|
|
645
|
+
if (item.chatId !== target.chatId && item.target?.chatId !== target.chatId)
|
|
646
|
+
return;
|
|
647
|
+
const captured = item.target;
|
|
648
|
+
if (
|
|
649
|
+
!captured ||
|
|
650
|
+
captured.chatId !== item.chatId ||
|
|
651
|
+
!Number.isSafeInteger(captured.threadId) ||
|
|
652
|
+
!captured.threadId ||
|
|
653
|
+
captured.threadId < 0
|
|
654
|
+
) {
|
|
655
|
+
unknown = true;
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (
|
|
659
|
+
captured.chatId === target.chatId &&
|
|
660
|
+
captured.threadId === target.threadId
|
|
661
|
+
)
|
|
662
|
+
protectedWork = true;
|
|
663
|
+
};
|
|
664
|
+
if (activeTurn) observe(activeTurn);
|
|
665
|
+
for (const item of items) observe(item);
|
|
666
|
+
return { protected: protectedWork, unknown };
|
|
667
|
+
}
|
|
668
|
+
|
|
545
669
|
// --- Queue Mutations ---
|
|
546
670
|
|
|
547
671
|
export function partitionTelegramQueueItemsForHistory<TContext = unknown>(
|
|
@@ -593,10 +717,7 @@ export function areTelegramQueueAdmissionReceiptsEqual(
|
|
|
593
717
|
function getTelegramQueueAdmissionReceiptKey(
|
|
594
718
|
receipt: TelegramQueueAdmissionReceipt,
|
|
595
719
|
): string {
|
|
596
|
-
return JSON.stringify([
|
|
597
|
-
receipt.receiptId,
|
|
598
|
-
receipt.journalBindingKey ?? null,
|
|
599
|
-
]);
|
|
720
|
+
return JSON.stringify([receipt.receiptId, receipt.journalBindingKey ?? null]);
|
|
600
721
|
}
|
|
601
722
|
|
|
602
723
|
function isDuplicateTelegramQueueAdmission<TContext>(
|
|
@@ -661,7 +782,10 @@ export function createTelegramQueueHandoff<TContext>(input: {
|
|
|
661
782
|
handoffToken: input.handoffToken,
|
|
662
783
|
payload: createTelegramQueueHandoffPayload(input.item),
|
|
663
784
|
};
|
|
664
|
-
if (
|
|
785
|
+
if (
|
|
786
|
+
Buffer.byteLength(JSON.stringify(handoff)) >
|
|
787
|
+
TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES
|
|
788
|
+
) {
|
|
665
789
|
throw new Error("Telegram queue handoff payload exceeds its byte limit.");
|
|
666
790
|
}
|
|
667
791
|
return handoff;
|
|
@@ -672,32 +796,14 @@ export function createTelegramQueueHandoffPayload<TContext>(
|
|
|
672
796
|
): TelegramQueueHandoffPayload {
|
|
673
797
|
assertTelegramQueueItemAdmissionValid(item);
|
|
674
798
|
if (!item.admissionReceipts?.length) {
|
|
675
|
-
throw new Error(
|
|
799
|
+
throw new Error(
|
|
800
|
+
"Telegram queue handoff requires durable admission receipts.",
|
|
801
|
+
);
|
|
676
802
|
}
|
|
677
803
|
if (item.admissionReceipts.length > TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS) {
|
|
678
804
|
throw new Error("Telegram queue handoff has too many admission receipts.");
|
|
679
805
|
}
|
|
680
|
-
|
|
681
|
-
return structuredClone({
|
|
682
|
-
kind: item.kind,
|
|
683
|
-
controlType: item.controlType,
|
|
684
|
-
chatId: item.chatId,
|
|
685
|
-
...(item.target ? { target: item.target } : {}),
|
|
686
|
-
...(item.transportStamp ? { transportStamp: item.transportStamp } : {}),
|
|
687
|
-
replyToMessageId: item.replyToMessageId,
|
|
688
|
-
...(item.guestQueryId ? { guestQueryId: item.guestQueryId } : {}),
|
|
689
|
-
...(item.guestInlineMessageId
|
|
690
|
-
? { guestInlineMessageId: item.guestInlineMessageId }
|
|
691
|
-
: {}),
|
|
692
|
-
queueOrder: item.queueOrder,
|
|
693
|
-
queueLane: item.queueLane,
|
|
694
|
-
laneOrder: item.laneOrder,
|
|
695
|
-
statusSummary: item.statusSummary,
|
|
696
|
-
admissionReceipts: item.admissionReceipts,
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
return structuredClone({
|
|
700
|
-
kind: item.kind,
|
|
806
|
+
const base: TelegramQueueHandoffBase = {
|
|
701
807
|
chatId: item.chatId,
|
|
702
808
|
...(item.target ? { target: item.target } : {}),
|
|
703
809
|
...(item.transportStamp ? { transportStamp: item.transportStamp } : {}),
|
|
@@ -711,6 +817,17 @@ export function createTelegramQueueHandoffPayload<TContext>(
|
|
|
711
817
|
laneOrder: item.laneOrder,
|
|
712
818
|
statusSummary: item.statusSummary,
|
|
713
819
|
admissionReceipts: item.admissionReceipts,
|
|
820
|
+
};
|
|
821
|
+
if (item.kind === "control") {
|
|
822
|
+
return structuredClone({
|
|
823
|
+
kind: item.kind,
|
|
824
|
+
controlType: item.controlType,
|
|
825
|
+
...base,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
return structuredClone({
|
|
829
|
+
kind: item.kind,
|
|
830
|
+
...base,
|
|
714
831
|
sourceMessageIds: item.sourceMessageIds,
|
|
715
832
|
queuedAttachments: item.queuedAttachments,
|
|
716
833
|
content: item.content,
|
|
@@ -1003,9 +1120,11 @@ function removeTelegramQueuedGuestPromptByOrder<TContext = unknown>(
|
|
|
1003
1120
|
removedCount: number;
|
|
1004
1121
|
} {
|
|
1005
1122
|
const index = items.findIndex((item) => {
|
|
1006
|
-
return
|
|
1123
|
+
return (
|
|
1124
|
+
isPendingTelegramTurn(item) &&
|
|
1007
1125
|
item.guestQueryId !== undefined &&
|
|
1008
|
-
item.queueOrder === queueOrder
|
|
1126
|
+
item.queueOrder === queueOrder
|
|
1127
|
+
);
|
|
1009
1128
|
});
|
|
1010
1129
|
if (index < 0) return { items, removedItems: [], removedCount: 0 };
|
|
1011
1130
|
const removedItem = items[index] as PendingTelegramTurn;
|
|
@@ -1016,9 +1135,7 @@ function removeTelegramQueuedGuestPromptByOrder<TContext = unknown>(
|
|
|
1016
1135
|
};
|
|
1017
1136
|
}
|
|
1018
1137
|
|
|
1019
|
-
export function applyTelegramQueuePromptReactionDisposition<
|
|
1020
|
-
TContext = unknown,
|
|
1021
|
-
>(
|
|
1138
|
+
export function applyTelegramQueuePromptReactionDisposition<TContext = unknown>(
|
|
1022
1139
|
items: TelegramQueueItem<TContext>[],
|
|
1023
1140
|
messageId: number,
|
|
1024
1141
|
disposition: TelegramQueueReactionDisposition,
|
|
@@ -1028,39 +1145,41 @@ export function applyTelegramQueuePromptReactionDisposition<
|
|
|
1028
1145
|
let nextItems = items;
|
|
1029
1146
|
for (const [index, item] of items.entries()) {
|
|
1030
1147
|
if (
|
|
1031
|
-
!isPendingTelegramTurn(item) ||
|
|
1148
|
+
!isPendingTelegramTurn(item) ||
|
|
1149
|
+
item.queueLane === "control" ||
|
|
1032
1150
|
!isTelegramQueueItemInMessageScope(item, scope) ||
|
|
1033
1151
|
!item.sourceMessageIds.includes(messageId)
|
|
1034
1152
|
) {
|
|
1035
1153
|
continue;
|
|
1036
1154
|
}
|
|
1037
|
-
const isPriority =
|
|
1038
|
-
|
|
1039
|
-
?
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
disposition.kind === "priority
|
|
1155
|
+
const isPriority =
|
|
1156
|
+
disposition.kind === "reaction-transition"
|
|
1157
|
+
? disposition.priorityEmoji === undefined
|
|
1158
|
+
? item.queueLane === "priority"
|
|
1159
|
+
: disposition.priorityEmoji !== null
|
|
1160
|
+
: disposition.kind === "priority" ||
|
|
1161
|
+
disposition.kind === "priority-suppressed";
|
|
1043
1162
|
const queueLane: TelegramQueueLane = isPriority ? "priority" : "default";
|
|
1044
|
-
const laneOrder =
|
|
1045
|
-
? item.laneOrder
|
|
1046
|
-
: destinationLaneOrder;
|
|
1163
|
+
const laneOrder =
|
|
1164
|
+
item.queueLane === queueLane ? item.laneOrder : destinationLaneOrder;
|
|
1047
1165
|
if (laneOrder === undefined) {
|
|
1048
1166
|
throw new Error("Telegram destination lane order is unavailable.");
|
|
1049
1167
|
}
|
|
1050
|
-
const priorityEmoji =
|
|
1051
|
-
|
|
1052
|
-
?
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1168
|
+
const priorityEmoji =
|
|
1169
|
+
disposition.kind === "reaction-transition"
|
|
1170
|
+
? disposition.priorityEmoji === undefined
|
|
1171
|
+
? item.priorityEmoji
|
|
1172
|
+
: (disposition.priorityEmoji ?? undefined)
|
|
1173
|
+
: disposition.kind === "priority"
|
|
1174
|
+
? disposition.emoji
|
|
1175
|
+
: disposition.kind === "priority-suppressed"
|
|
1176
|
+
? disposition.priorityEmoji
|
|
1177
|
+
: undefined;
|
|
1059
1178
|
const reactionSuppressionEmoji =
|
|
1060
1179
|
disposition.kind === "reaction-transition"
|
|
1061
1180
|
? disposition.suppressionEmoji === undefined
|
|
1062
1181
|
? item.reactionSuppressionEmoji
|
|
1063
|
-
: disposition.suppressionEmoji ?? undefined
|
|
1182
|
+
: (disposition.suppressionEmoji ?? undefined)
|
|
1064
1183
|
: disposition.kind === "suppressed"
|
|
1065
1184
|
? disposition.emoji
|
|
1066
1185
|
: disposition.kind === "priority-suppressed"
|
|
@@ -1595,13 +1714,20 @@ export interface TelegramAgentEndRuntimeDeps<
|
|
|
1595
1714
|
updateStatus: () => void;
|
|
1596
1715
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
1597
1716
|
scheduleActiveTurnDelivery?: (task: () => Promise<void>) => void;
|
|
1598
|
-
preparePreviewDelivery?: (
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1717
|
+
preparePreviewDelivery?: (
|
|
1718
|
+
isDeliveryActive: () => boolean,
|
|
1719
|
+
) =>
|
|
1720
|
+
| Pick<
|
|
1721
|
+
TelegramAgentEndRuntimeDeps<TTurn, TReplyMarkup>,
|
|
1722
|
+
"clearPreview" | "setPreviewPendingText" | "finalizeMarkdownPreview"
|
|
1723
|
+
>
|
|
1724
|
+
| undefined;
|
|
1602
1725
|
preparePreviewClear?: (
|
|
1603
1726
|
chatId: number,
|
|
1604
|
-
options?: {
|
|
1727
|
+
options?: {
|
|
1728
|
+
target?: TelegramQueueTarget;
|
|
1729
|
+
isDeliveryActive?: () => boolean;
|
|
1730
|
+
},
|
|
1605
1731
|
) => () => Promise<void>;
|
|
1606
1732
|
clearPreview: (
|
|
1607
1733
|
chatId: number,
|
|
@@ -1681,7 +1807,9 @@ export interface TelegramAgentEndHookRuntimeDeps<
|
|
|
1681
1807
|
extractAssistant: (
|
|
1682
1808
|
messages: readonly TMessage[],
|
|
1683
1809
|
) => TelegramAgentEndAssistantResult;
|
|
1684
|
-
isAssistantAlreadyPublished?: (
|
|
1810
|
+
isAssistantAlreadyPublished?: (
|
|
1811
|
+
assistant: TelegramAgentEndAssistantResult,
|
|
1812
|
+
) => boolean;
|
|
1685
1813
|
getFoldQueuedPromptsIntoHistory: () => boolean;
|
|
1686
1814
|
resetRuntimeState: () => void;
|
|
1687
1815
|
isSessionActive?: (ctx: TContext) => boolean;
|
|
@@ -1701,8 +1829,14 @@ export interface TelegramAgentEndHookRuntimeDeps<
|
|
|
1701
1829
|
schedule: (task: () => Promise<void>) => void;
|
|
1702
1830
|
cancel: () => void;
|
|
1703
1831
|
};
|
|
1704
|
-
preparePreviewDelivery?: TelegramAgentEndRuntimeDeps<
|
|
1705
|
-
|
|
1832
|
+
preparePreviewDelivery?: TelegramAgentEndRuntimeDeps<
|
|
1833
|
+
TTurn,
|
|
1834
|
+
TReplyMarkup
|
|
1835
|
+
>["preparePreviewDelivery"];
|
|
1836
|
+
preparePreviewClear?: TelegramAgentEndRuntimeDeps<
|
|
1837
|
+
TTurn,
|
|
1838
|
+
TReplyMarkup
|
|
1839
|
+
>["preparePreviewClear"];
|
|
1706
1840
|
clearPreview: TelegramAgentEndRuntimeDeps<
|
|
1707
1841
|
TTurn,
|
|
1708
1842
|
TReplyMarkup
|
|
@@ -1833,16 +1967,28 @@ export function createTelegramAgentEndHook<
|
|
|
1833
1967
|
): Promise<void> => {
|
|
1834
1968
|
if (deps.isSessionActive?.(ctx) === false) return;
|
|
1835
1969
|
const turn = deps.getActiveTurn();
|
|
1836
|
-
const extractedAssistant =
|
|
1970
|
+
const extractedAssistant =
|
|
1971
|
+
assistantOverride ?? (turn ? deps.extractAssistant(event.messages) : {});
|
|
1837
1972
|
const assistant = deps.isAssistantAlreadyPublished?.(extractedAssistant)
|
|
1838
1973
|
? { stopReason: extractedAssistant.stopReason }
|
|
1839
1974
|
: extractedAssistant;
|
|
1840
|
-
const hasPublication =
|
|
1841
|
-
|
|
1842
|
-
|
|
1975
|
+
const hasPublication =
|
|
1976
|
+
!!assistant.text ||
|
|
1977
|
+
assistant.stopReason === "error" ||
|
|
1978
|
+
!!turn?.queuedAttachments.length;
|
|
1979
|
+
const reservation =
|
|
1980
|
+
turn && !turn.guestQueryId && hasPublication
|
|
1981
|
+
? deps.reserveActiveTurnDelivery?.()
|
|
1982
|
+
: undefined;
|
|
1983
|
+
const scheduleDelivery =
|
|
1984
|
+
reservation?.schedule ?? deps.scheduleActiveTurnDelivery;
|
|
1843
1985
|
try {
|
|
1844
1986
|
await deps.loadConfig?.();
|
|
1845
|
-
if (
|
|
1987
|
+
if (
|
|
1988
|
+
deps.isSessionActive?.(ctx) === false ||
|
|
1989
|
+
deps.getActiveTurn() !== turn
|
|
1990
|
+
)
|
|
1991
|
+
return;
|
|
1846
1992
|
await handleTelegramAgentEndRuntime({
|
|
1847
1993
|
turn,
|
|
1848
1994
|
assistant,
|
|
@@ -1929,19 +2075,27 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1929
2075
|
const isDeliveryActive = (): boolean =>
|
|
1930
2076
|
deps.isSessionActive?.() !== false &&
|
|
1931
2077
|
(!turn || deps.isTurnTransportActive?.(turn) !== false);
|
|
1932
|
-
const preview =
|
|
1933
|
-
|
|
1934
|
-
|
|
2078
|
+
const preview =
|
|
2079
|
+
turn && !turn.guestQueryId
|
|
2080
|
+
? deps.preparePreviewDelivery?.(isDeliveryActive)
|
|
2081
|
+
: undefined;
|
|
2082
|
+
const setPreviewPendingText =
|
|
2083
|
+
preview?.setPreviewPendingText ?? deps.setPreviewPendingText;
|
|
2084
|
+
const finalizeMarkdownPreview =
|
|
2085
|
+
preview?.finalizeMarkdownPreview ?? deps.finalizeMarkdownPreview;
|
|
1935
2086
|
const clearPreview = turn
|
|
1936
|
-
? preview
|
|
1937
|
-
|
|
1938
|
-
|
|
2087
|
+
? preview
|
|
2088
|
+
? () => preview.clearPreview(turn.chatId, { target: turn.target })
|
|
2089
|
+
: (deps.preparePreviewClear?.(turn.chatId, {
|
|
2090
|
+
target: turn.target,
|
|
2091
|
+
isDeliveryActive,
|
|
2092
|
+
}) ?? (() => deps.clearPreview(turn.chatId, { target: turn.target })))
|
|
1939
2093
|
: undefined;
|
|
1940
2094
|
const updateStatusIgnoringStaleContext = (): void => {
|
|
1941
2095
|
try {
|
|
1942
2096
|
deps.updateStatus();
|
|
1943
2097
|
} catch (error) {
|
|
1944
|
-
if (!
|
|
2098
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
1945
2099
|
}
|
|
1946
2100
|
};
|
|
1947
2101
|
if (!isDeliveryActive()) {
|
|
@@ -2092,9 +2246,11 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2092
2246
|
if (!isDeliveryActive()) return;
|
|
2093
2247
|
}
|
|
2094
2248
|
if (endPlan.shouldSendAbortMessage || endPlan.shouldSendErrorMessage) {
|
|
2095
|
-
const errorMessage =
|
|
2249
|
+
const errorMessage =
|
|
2250
|
+
assistant.errorMessage ||
|
|
2096
2251
|
"Telegram bridge: Pi failed while processing the request.";
|
|
2097
|
-
const isOperationAborted =
|
|
2252
|
+
const isOperationAborted =
|
|
2253
|
+
endPlan.shouldSendAbortMessage ||
|
|
2098
2254
|
errorMessage.trim().replace(/\.$/, "") === "This operation was aborted";
|
|
2099
2255
|
try {
|
|
2100
2256
|
await deps.sendTextReply(
|
|
@@ -2118,7 +2274,8 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2118
2274
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
2119
2275
|
return;
|
|
2120
2276
|
}
|
|
2121
|
-
if (finalText && turn.queuedAttachments.length === 0)
|
|
2277
|
+
if (finalText && turn.queuedAttachments.length === 0)
|
|
2278
|
+
setPreviewPendingText(finalText);
|
|
2122
2279
|
|
|
2123
2280
|
if (!isDeliveryActive()) return;
|
|
2124
2281
|
let richAttachmentDelivered = false;
|
|
@@ -2261,7 +2418,11 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2261
2418
|
};
|
|
2262
2419
|
if (
|
|
2263
2420
|
deps.scheduleActiveTurnDelivery &&
|
|
2264
|
-
(endPlan.kind === "text" ||
|
|
2421
|
+
(endPlan.kind === "text" ||
|
|
2422
|
+
endPlan.kind === "attachments-only" ||
|
|
2423
|
+
endPlan.shouldSendAbortMessage ||
|
|
2424
|
+
endPlan.shouldSendErrorMessage ||
|
|
2425
|
+
endPlan.shouldClearPreview)
|
|
2265
2426
|
) {
|
|
2266
2427
|
deps.scheduleActiveTurnDelivery(deliverActiveTurn);
|
|
2267
2428
|
return;
|
|
@@ -2352,7 +2513,13 @@ export interface TelegramSessionLifecycleHookRuntimeDeps<
|
|
|
2352
2513
|
TContext,
|
|
2353
2514
|
TQueueItem,
|
|
2354
2515
|
TModel = unknown,
|
|
2355
|
-
>
|
|
2516
|
+
>
|
|
2517
|
+
extends
|
|
2518
|
+
TelegramRuntimeEventRecorderPort,
|
|
2519
|
+
Omit<
|
|
2520
|
+
TelegramSessionShutdownRuntimeDeps<TQueueItem>,
|
|
2521
|
+
"isSessionActive" | "discardQueuedItems" | "applyState"
|
|
2522
|
+
> {
|
|
2356
2523
|
getCurrentModel: (ctx: TContext) => TModel | undefined;
|
|
2357
2524
|
loadConfig: () => Promise<void>;
|
|
2358
2525
|
applySessionStartState: (state: TelegramSessionStartState<TModel>) => void;
|
|
@@ -2360,23 +2527,10 @@ export interface TelegramSessionLifecycleHookRuntimeDeps<
|
|
|
2360
2527
|
prepareTempDir: () => Promise<unknown>;
|
|
2361
2528
|
updateStatus: (ctx: TContext) => void;
|
|
2362
2529
|
isSessionActive?: (ctx: TContext) => boolean;
|
|
2363
|
-
unbindDeferredDispatchContext?: () => void;
|
|
2364
2530
|
discardQueuedItems?: (ctx: TContext) => void;
|
|
2365
2531
|
applySessionShutdownState: (
|
|
2366
2532
|
state: TelegramSessionShutdownState<TQueueItem>,
|
|
2367
2533
|
) => void;
|
|
2368
|
-
clearPendingMediaGroups: () => void;
|
|
2369
|
-
clearModelMenuState: () => void;
|
|
2370
|
-
getActiveTurnChatId: () => number | undefined;
|
|
2371
|
-
getActiveTurnTarget?: () => TelegramQueueTarget | undefined;
|
|
2372
|
-
clearPreview: (
|
|
2373
|
-
chatId: number,
|
|
2374
|
-
options?: { target?: TelegramQueueTarget },
|
|
2375
|
-
) => Promise<void>;
|
|
2376
|
-
previewShutdownTimeoutMs?: number;
|
|
2377
|
-
clearActiveTurn: () => void;
|
|
2378
|
-
clearAbort: () => void;
|
|
2379
|
-
stopPolling: () => Promise<void>;
|
|
2380
2534
|
}
|
|
2381
2535
|
|
|
2382
2536
|
export type TelegramSessionLifecycleHookEvent = unknown;
|
|
@@ -2401,9 +2555,8 @@ export function createTelegramSessionStateApplier<TQueueItem, TModel>(
|
|
|
2401
2555
|
};
|
|
2402
2556
|
}
|
|
2403
2557
|
|
|
2404
|
-
export interface TelegramQueueMutationRuntimeDeps<
|
|
2405
|
-
TContext
|
|
2406
|
-
> extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2558
|
+
export interface TelegramQueueMutationRuntimeDeps<TContext>
|
|
2559
|
+
extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2407
2560
|
ctx: TContext;
|
|
2408
2561
|
hasPendingDispatch?: () => boolean;
|
|
2409
2562
|
allocateLaneOrder?: () => number;
|
|
@@ -2414,9 +2567,8 @@ export interface TelegramQueueMutationRuntimeDeps<
|
|
|
2414
2567
|
updateStatus: (ctx: TContext) => void;
|
|
2415
2568
|
}
|
|
2416
2569
|
|
|
2417
|
-
export interface TelegramQueueMutationControllerDeps<
|
|
2418
|
-
TContext
|
|
2419
|
-
> extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2570
|
+
export interface TelegramQueueMutationControllerDeps<TContext>
|
|
2571
|
+
extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2420
2572
|
hasPendingDispatch?: () => boolean;
|
|
2421
2573
|
allocateLaneOrder?: () => number;
|
|
2422
2574
|
onItemsDiscarded?: (
|
|
@@ -2478,6 +2630,8 @@ export interface TelegramPromptEnqueueRuntimeDeps<
|
|
|
2478
2630
|
updateStatus: () => void;
|
|
2479
2631
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
2480
2632
|
assertExecutionCurrent?: () => void;
|
|
2633
|
+
/** Input-specific relocation must not fold or replace unrelated queued work. */
|
|
2634
|
+
preserveQueued?: boolean;
|
|
2481
2635
|
onQueued?: (turn: PendingTelegramTurn) => void;
|
|
2482
2636
|
}
|
|
2483
2637
|
|
|
@@ -2502,17 +2656,10 @@ export interface TelegramPromptEnqueueController<TMessage, TContext = unknown> {
|
|
|
2502
2656
|
messages: TMessage[],
|
|
2503
2657
|
ctx: TContext,
|
|
2504
2658
|
onQueued?: (turn: PendingTelegramTurn) => void,
|
|
2659
|
+
options?: { assertCurrent?: () => void; preserveQueued?: boolean },
|
|
2505
2660
|
) => Promise<PendingTelegramTurn>;
|
|
2506
2661
|
}
|
|
2507
2662
|
|
|
2508
|
-
function isTelegramStaleContextError(error: unknown): boolean {
|
|
2509
|
-
return (
|
|
2510
|
-
error instanceof Error &&
|
|
2511
|
-
(error.message.includes("stale after session") ||
|
|
2512
|
-
error.message.includes("stale ctx"))
|
|
2513
|
-
);
|
|
2514
|
-
}
|
|
2515
|
-
|
|
2516
2663
|
export function buildTelegramSessionStartState<TModel = unknown>(
|
|
2517
2664
|
currentModel: TModel | undefined,
|
|
2518
2665
|
): TelegramSessionStartState<TModel> {
|
|
@@ -2554,7 +2701,7 @@ export async function startTelegramSessionRuntime<TContext, TModel = unknown>(
|
|
|
2554
2701
|
try {
|
|
2555
2702
|
deps.bindDeferredDispatchContext?.(deps.ctx);
|
|
2556
2703
|
} catch (error) {
|
|
2557
|
-
if (!
|
|
2704
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
2558
2705
|
}
|
|
2559
2706
|
deps.updateStatus();
|
|
2560
2707
|
}
|
|
@@ -2733,7 +2880,7 @@ function updateTelegramQueueStatusRuntime<TContext>(
|
|
|
2733
2880
|
try {
|
|
2734
2881
|
deps.updateStatus(deps.ctx);
|
|
2735
2882
|
} catch (error) {
|
|
2736
|
-
if (!
|
|
2883
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
2737
2884
|
}
|
|
2738
2885
|
}
|
|
2739
2886
|
|
|
@@ -2741,11 +2888,18 @@ function commitReorderedTelegramQueueItemsRuntime<TContext>(
|
|
|
2741
2888
|
items: TelegramQueueItem<TContext>[],
|
|
2742
2889
|
deps: TelegramQueueMutationRuntimeDeps<TContext>,
|
|
2743
2890
|
): void {
|
|
2744
|
-
const pendingHead = deps.hasPendingDispatch?.()
|
|
2891
|
+
const pendingHead = deps.hasPendingDispatch?.()
|
|
2892
|
+
? deps.getQueuedItems()[0]
|
|
2893
|
+
: undefined;
|
|
2745
2894
|
const ordered = [...items].sort(compareTelegramQueueItems);
|
|
2746
2895
|
if (pendingHead) {
|
|
2747
|
-
const index = ordered.findIndex(
|
|
2748
|
-
item
|
|
2896
|
+
const index = ordered.findIndex(
|
|
2897
|
+
(item) =>
|
|
2898
|
+
item.queueOrder === pendingHead.queueOrder &&
|
|
2899
|
+
item.kind === pendingHead.kind &&
|
|
2900
|
+
item.chatId === pendingHead.chatId &&
|
|
2901
|
+
item.replyToMessageId === pendingHead.replyToMessageId,
|
|
2902
|
+
);
|
|
2749
2903
|
if (index > 0) ordered.unshift(ordered.splice(index, 1)[0]!);
|
|
2750
2904
|
}
|
|
2751
2905
|
deps.setQueuedItems(ordered);
|
|
@@ -2826,23 +2980,44 @@ export function applyTelegramQueuePromptReactionDispositionRuntime<TContext>(
|
|
|
2826
2980
|
scope?: TelegramQueueMessageScope,
|
|
2827
2981
|
): boolean {
|
|
2828
2982
|
const queuedItems = deps.getQueuedItems();
|
|
2829
|
-
const suppression =
|
|
2830
|
-
disposition.kind === "
|
|
2983
|
+
const suppression =
|
|
2984
|
+
disposition.kind === "reaction-transition"
|
|
2985
|
+
? disposition.suppressionEmoji
|
|
2986
|
+
: disposition.kind === "suppressed"
|
|
2987
|
+
? disposition.emoji
|
|
2988
|
+
: disposition.kind === "priority-suppressed"
|
|
2989
|
+
? disposition.suppressionEmoji
|
|
2990
|
+
: undefined;
|
|
2831
2991
|
// Source-addressed control-lane prompts are explicit continuations. Synthetic model-switch turns have no source IDs.
|
|
2832
2992
|
// A Pi-owned dispatched head cannot be removed: agent_start must consume that exact item, not its successor.
|
|
2833
|
-
const cancelled =
|
|
2834
|
-
|
|
2835
|
-
|
|
2993
|
+
const cancelled =
|
|
2994
|
+
suppression && deps.hasPendingDispatch
|
|
2995
|
+
? queuedItems.filter(
|
|
2996
|
+
(item, index) =>
|
|
2997
|
+
isPendingTelegramTurn(item) &&
|
|
2998
|
+
item.queueLane === "control" &&
|
|
2999
|
+
isTelegramQueueItemInMessageScope(item, scope) &&
|
|
3000
|
+
item.sourceMessageIds.includes(messageId) &&
|
|
3001
|
+
!(index === 0 && deps.hasPendingDispatch!()),
|
|
3002
|
+
)
|
|
3003
|
+
: [];
|
|
2836
3004
|
if (cancelled.length) {
|
|
2837
|
-
if (
|
|
3005
|
+
if (
|
|
3006
|
+
cancelled.some((item) => (item.admissionReceipts?.length ?? 0) > 0) &&
|
|
3007
|
+
!deps.onItemsDiscarded
|
|
3008
|
+
)
|
|
3009
|
+
return false;
|
|
2838
3010
|
deps.onItemsDiscarded?.(cancelled, deps.ctx);
|
|
2839
|
-
deps.setQueuedItems(
|
|
3011
|
+
deps.setQueuedItems(
|
|
3012
|
+
queuedItems.filter((item) => !cancelled.includes(item)),
|
|
3013
|
+
);
|
|
2840
3014
|
updateTelegramQueueStatusRuntime(deps);
|
|
2841
3015
|
return true;
|
|
2842
3016
|
}
|
|
2843
3017
|
const changesLane = queuedItems.some((item) => {
|
|
2844
3018
|
if (
|
|
2845
|
-
!isPendingTelegramTurn(item) ||
|
|
3019
|
+
!isPendingTelegramTurn(item) ||
|
|
3020
|
+
item.queueLane === "control" ||
|
|
2846
3021
|
!isTelegramQueueItemInMessageScope(item, scope) ||
|
|
2847
3022
|
!item.sourceMessageIds.includes(messageId)
|
|
2848
3023
|
) {
|
|
@@ -2885,23 +3060,32 @@ export async function enqueueTelegramPromptTurnRuntime<
|
|
|
2885
3060
|
deps: TelegramPromptEnqueueRuntimeDeps<TMessage, TContext>,
|
|
2886
3061
|
): Promise<PendingTelegramTurn> {
|
|
2887
3062
|
deps.assertExecutionCurrent?.();
|
|
2888
|
-
const historyOrders = new Set(
|
|
2889
|
-
deps.
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
3063
|
+
const historyOrders = new Set(
|
|
3064
|
+
deps.preserveQueued
|
|
3065
|
+
? []
|
|
3066
|
+
: planTelegramPromptEnqueue(
|
|
3067
|
+
deps.getQueuedItems(),
|
|
3068
|
+
deps.getFoldQueuedPromptsIntoHistory(),
|
|
3069
|
+
).historyTurns.map((turn) => turn.queueOrder),
|
|
3070
|
+
);
|
|
3071
|
+
if (!deps.preserveQueued) deps.setFoldQueuedPromptsIntoHistory(false);
|
|
2893
3072
|
const buildTurn = await deps.prepareTurn(messages);
|
|
2894
3073
|
deps.assertExecutionCurrent?.();
|
|
2895
3074
|
// Preserve the Pi-owned head until agent_start, plus later arrivals and current edits/reactions.
|
|
2896
3075
|
const pendingDispatch = deps.hasPendingDispatch();
|
|
2897
3076
|
const historyTurns: PendingTelegramTurn[] = [];
|
|
2898
3077
|
const remainingItems = deps.getQueuedItems().filter((item, index) => {
|
|
2899
|
-
if (
|
|
2900
|
-
|
|
3078
|
+
if (
|
|
3079
|
+
(pendingDispatch && index === 0) ||
|
|
3080
|
+
!isPendingTelegramTurn(item) ||
|
|
3081
|
+
!historyOrders.has(item.queueOrder)
|
|
3082
|
+
)
|
|
3083
|
+
return true;
|
|
2901
3084
|
historyTurns.push(item);
|
|
2902
3085
|
return false;
|
|
2903
3086
|
});
|
|
2904
3087
|
const turn = buildTurn(historyTurns);
|
|
3088
|
+
deps.assertExecutionCurrent?.();
|
|
2905
3089
|
deps.setQueuedItems(appendTelegramQueueItem(remainingItems, turn));
|
|
2906
3090
|
deps.onQueued?.(turn);
|
|
2907
3091
|
deps.updateStatus();
|
|
@@ -2916,15 +3100,18 @@ export function createTelegramPromptEnqueueController<
|
|
|
2916
3100
|
deps: TelegramPromptEnqueueControllerDeps<TMessage, TContext>,
|
|
2917
3101
|
): TelegramPromptEnqueueController<TMessage, TContext> {
|
|
2918
3102
|
return {
|
|
2919
|
-
enqueue: (messages, ctx, onQueued) =>
|
|
3103
|
+
enqueue: (messages, ctx, onQueued, options) =>
|
|
2920
3104
|
enqueueTelegramPromptTurnRuntime(messages, {
|
|
2921
3105
|
...deps,
|
|
2922
3106
|
prepareTurn: (nextMessages) => deps.prepareTurn(nextMessages, ctx),
|
|
2923
3107
|
updateStatus: () => deps.updateStatus(ctx),
|
|
2924
3108
|
dispatchNextQueuedTelegramTurn: () =>
|
|
2925
3109
|
deps.dispatchNextQueuedTelegramTurn(ctx),
|
|
2926
|
-
assertExecutionCurrent: () =>
|
|
2927
|
-
deps.assertExecutionCurrent?.(messages)
|
|
3110
|
+
assertExecutionCurrent: () => {
|
|
3111
|
+
deps.assertExecutionCurrent?.(messages);
|
|
3112
|
+
options?.assertCurrent?.();
|
|
3113
|
+
},
|
|
3114
|
+
preserveQueued: options?.preserveQueued,
|
|
2928
3115
|
onQueued,
|
|
2929
3116
|
}),
|
|
2930
3117
|
};
|
|
@@ -3165,7 +3352,10 @@ export interface TelegramDispatchRuntimeDeps<TContext = unknown> {
|
|
|
3165
3352
|
) => void;
|
|
3166
3353
|
onPromptDispatchStart: (chatId: number) => void;
|
|
3167
3354
|
commitPromptDispatch?: (
|
|
3168
|
-
item: Extract<
|
|
3355
|
+
item: Extract<
|
|
3356
|
+
TelegramQueueDispatchAction<TContext>,
|
|
3357
|
+
{ kind: "prompt" }
|
|
3358
|
+
>["item"],
|
|
3169
3359
|
) => boolean;
|
|
3170
3360
|
sendUserMessage: (
|
|
3171
3361
|
content: Extract<
|
|
@@ -3191,7 +3381,10 @@ export interface TelegramQueueDispatchControllerDeps<
|
|
|
3191
3381
|
sendTextReply: TelegramControlRuntimeDeps<TContext>["sendTextReply"];
|
|
3192
3382
|
onPromptDispatchStart: (ctx: TContext, chatId: number) => void;
|
|
3193
3383
|
commitPromptDispatch?: (
|
|
3194
|
-
item: Extract<
|
|
3384
|
+
item: Extract<
|
|
3385
|
+
TelegramQueueDispatchAction<TContext>,
|
|
3386
|
+
{ kind: "prompt" }
|
|
3387
|
+
>["item"],
|
|
3195
3388
|
ctx: TContext,
|
|
3196
3389
|
) => boolean;
|
|
3197
3390
|
sendUserMessage: TelegramDispatchRuntimeDeps<TContext>["sendUserMessage"];
|
|
@@ -3232,7 +3425,9 @@ export function executeTelegramQueueDispatchPlan<TContext = unknown>(
|
|
|
3232
3425
|
deps.onPromptDispatchStart(plan.item.chatId);
|
|
3233
3426
|
try {
|
|
3234
3427
|
if (deps.commitPromptDispatch && !deps.commitPromptDispatch(plan.item)) {
|
|
3235
|
-
throw new Error(
|
|
3428
|
+
throw new Error(
|
|
3429
|
+
"Telegram prompt dispatch could not be committed durably.",
|
|
3430
|
+
);
|
|
3236
3431
|
}
|
|
3237
3432
|
deps.sendUserMessage(plan.item.content);
|
|
3238
3433
|
} catch (error) {
|
|
@@ -3390,9 +3585,13 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3390
3585
|
const nextItem = dispatchableItems[0];
|
|
3391
3586
|
if (nextDispatchAnnouncementRequested) {
|
|
3392
3587
|
if (nextDispatchAnnouncementAnchor) {
|
|
3393
|
-
const anchorRetained = retainedItems.includes(
|
|
3588
|
+
const anchorRetained = retainedItems.includes(
|
|
3589
|
+
nextDispatchAnnouncementAnchor,
|
|
3590
|
+
);
|
|
3394
3591
|
const anchorTransportActive =
|
|
3395
|
-
deps.isQueueItemTransportActive?.(
|
|
3592
|
+
deps.isQueueItemTransportActive?.(
|
|
3593
|
+
nextDispatchAnnouncementAnchor,
|
|
3594
|
+
) !== false;
|
|
3396
3595
|
if (!anchorRetained || !anchorTransportActive) {
|
|
3397
3596
|
nextDispatchAnnouncementRequested = false;
|
|
3398
3597
|
nextDispatchAnnouncementAnchor = undefined;
|
|
@@ -3403,10 +3602,7 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3403
3602
|
nextDispatchAnnouncementRequested = false;
|
|
3404
3603
|
}
|
|
3405
3604
|
}
|
|
3406
|
-
if (
|
|
3407
|
-
nextItem &&
|
|
3408
|
-
deps.hasPendingInboundQueueMutationForItem?.(nextItem)
|
|
3409
|
-
) {
|
|
3605
|
+
if (nextItem && deps.hasPendingInboundQueueMutationForItem?.(nextItem)) {
|
|
3410
3606
|
deps.updateStatus(ctx);
|
|
3411
3607
|
return;
|
|
3412
3608
|
}
|
|
@@ -3429,14 +3625,19 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3429
3625
|
if (dispatchPlan.kind === "prompt") {
|
|
3430
3626
|
const queueDrifted =
|
|
3431
3627
|
currentItems.length !== dispatchBasisItems.length ||
|
|
3432
|
-
currentItems.some(
|
|
3628
|
+
currentItems.some(
|
|
3629
|
+
(item, index) => item !== dispatchBasisItems[index],
|
|
3630
|
+
);
|
|
3433
3631
|
const dispatchEligibilityDrifted =
|
|
3434
3632
|
!deps.canDispatch(ctx) ||
|
|
3435
|
-
deps.hasPendingInboundQueueMutationForItem?.(dispatchPlan.item) ===
|
|
3436
|
-
|
|
3437
|
-
|
|
3633
|
+
deps.hasPendingInboundQueueMutationForItem?.(dispatchPlan.item) ===
|
|
3634
|
+
true ||
|
|
3635
|
+
deps.isQueueItemAdmissionReady?.(dispatchPlan.item) === false ||
|
|
3636
|
+
deps.isQueueItemTransportActive?.(dispatchPlan.item) === false;
|
|
3438
3637
|
if (queueDrifted || dispatchEligibilityDrifted) {
|
|
3439
|
-
const selectedItemRetained = currentItems.includes(
|
|
3638
|
+
const selectedItemRetained = currentItems.includes(
|
|
3639
|
+
dispatchPlan.item,
|
|
3640
|
+
);
|
|
3440
3641
|
const selectedTransportActive =
|
|
3441
3642
|
deps.isQueueItemTransportActive?.(dispatchPlan.item) !== false;
|
|
3442
3643
|
nextDispatchAnnouncementRequested =
|
|
@@ -3445,7 +3646,11 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3445
3646
|
? dispatchPlan.item
|
|
3446
3647
|
: undefined;
|
|
3447
3648
|
deps.updateStatus(ctx);
|
|
3448
|
-
if (
|
|
3649
|
+
if (
|
|
3650
|
+
nextDispatchAnnouncementRequested &&
|
|
3651
|
+
queueDrifted &&
|
|
3652
|
+
!dispatchEligibilityDrifted
|
|
3653
|
+
) {
|
|
3449
3654
|
controller.dispatchNext(ctx);
|
|
3450
3655
|
}
|
|
3451
3656
|
return false;
|
|
@@ -3461,51 +3666,52 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3461
3666
|
const executePlan = (): void => {
|
|
3462
3667
|
if (!commitDispatchPlan()) return;
|
|
3463
3668
|
executeTelegramQueueDispatchPlan(dispatchPlan, {
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3669
|
+
executeControlItem: (item) => {
|
|
3670
|
+
controlDispatchPending = true;
|
|
3671
|
+
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
3672
|
+
deps.updateStatus(ctx);
|
|
3673
|
+
void executeTelegramControlItemRuntime(item, {
|
|
3674
|
+
ctx,
|
|
3675
|
+
sendTextReply: deps.sendTextReply,
|
|
3676
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
3677
|
+
onSettled: (settledItem) => {
|
|
3678
|
+
try {
|
|
3679
|
+
deps.onControlSettled?.(settledItem, ctx);
|
|
3680
|
+
} catch (error) {
|
|
3681
|
+
deps.recordRuntimeEvent?.("control", error, {
|
|
3682
|
+
phase: "receipt-settlement",
|
|
3683
|
+
controlType: settledItem.controlType,
|
|
3684
|
+
});
|
|
3685
|
+
}
|
|
3686
|
+
controlDispatchPending = false;
|
|
3687
|
+
if (deps.hasDispatchContext && !deps.hasDispatchContext())
|
|
3688
|
+
return;
|
|
3689
|
+
if (
|
|
3690
|
+
dispatchGeneration !== undefined &&
|
|
3691
|
+
deps.isDispatchGenerationActive &&
|
|
3692
|
+
!deps.isDispatchGenerationActive(dispatchGeneration)
|
|
3693
|
+
) {
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
deps.updateStatus(ctx);
|
|
3697
|
+
controller.dispatchNext(ctx);
|
|
3698
|
+
},
|
|
3699
|
+
});
|
|
3700
|
+
},
|
|
3701
|
+
onPromptDispatchStart: (chatId) => {
|
|
3702
|
+
deps.onPromptDispatchStart(ctx, chatId);
|
|
3703
|
+
},
|
|
3704
|
+
commitPromptDispatch: deps.commitPromptDispatch
|
|
3705
|
+
? (item) => deps.commitPromptDispatch!(item, ctx)
|
|
3706
|
+
: undefined,
|
|
3707
|
+
sendUserMessage: deps.sendUserMessage,
|
|
3708
|
+
onPromptDispatchFailure: (message) => {
|
|
3709
|
+
deps.onPromptDispatchFailure(ctx, message);
|
|
3710
|
+
},
|
|
3711
|
+
onIdle: () => {
|
|
3712
|
+
deps.updateStatus(ctx);
|
|
3713
|
+
},
|
|
3714
|
+
});
|
|
3509
3715
|
};
|
|
3510
3716
|
if (dispatchPlan.kind === "prompt" && nextDispatchAnnouncementRequested) {
|
|
3511
3717
|
nextDispatchAnnouncementRequested = false;
|
|
@@ -3513,38 +3719,43 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3513
3719
|
const announcementGeneration = nextDispatchAnnouncementGeneration;
|
|
3514
3720
|
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
3515
3721
|
deps.updateStatus(ctx);
|
|
3516
|
-
void deps
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
});
|
|
3525
|
-
}).finally(() => {
|
|
3526
|
-
try {
|
|
3527
|
-
deps.reconcileNextDispatchAnnouncementReplyOwnership?.(
|
|
3528
|
-
dispatchPlan.item,
|
|
3529
|
-
);
|
|
3530
|
-
} catch (error) {
|
|
3722
|
+
void deps
|
|
3723
|
+
.sendTextReply(
|
|
3724
|
+
dispatchPlan.item.chatId,
|
|
3725
|
+
dispatchPlan.item.replyToMessageId,
|
|
3726
|
+
"<b>⏩ Dispatching next queued turn.</b>",
|
|
3727
|
+
{ target: dispatchPlan.item.target, parseMode: "HTML" },
|
|
3728
|
+
)
|
|
3729
|
+
.catch((error) => {
|
|
3531
3730
|
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3532
|
-
phase: "next-announcement
|
|
3731
|
+
phase: "next-announcement",
|
|
3533
3732
|
});
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3733
|
+
})
|
|
3734
|
+
.finally(() => {
|
|
3735
|
+
try {
|
|
3736
|
+
deps.reconcileNextDispatchAnnouncementReplyOwnership?.(
|
|
3737
|
+
dispatchPlan.item,
|
|
3738
|
+
);
|
|
3739
|
+
} catch (error) {
|
|
3740
|
+
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3741
|
+
phase: "next-announcement-reply-ownership",
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
controlDispatchPending = false;
|
|
3745
|
+
if (deps.hasDispatchContext && !deps.hasDispatchContext()) return;
|
|
3746
|
+
if (
|
|
3747
|
+
dispatchGeneration !== undefined &&
|
|
3748
|
+
deps.isDispatchGenerationActive &&
|
|
3749
|
+
!deps.isDispatchGenerationActive(dispatchGeneration)
|
|
3750
|
+
)
|
|
3751
|
+
return;
|
|
3752
|
+
if (announcementGeneration !== nextDispatchAnnouncementGeneration) {
|
|
3753
|
+
if (nextDispatchAnnouncementRequested)
|
|
3754
|
+
controller.dispatchNext(ctx);
|
|
3755
|
+
return;
|
|
3756
|
+
}
|
|
3757
|
+
executePlan();
|
|
3758
|
+
});
|
|
3548
3759
|
return;
|
|
3549
3760
|
}
|
|
3550
3761
|
executePlan();
|