@llblab/pi-telegram 0.52.2 → 0.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +15 -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 +65 -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 +252 -127
- 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 +128 -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 +516 -301
- 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;
|
|
@@ -2199,7 +2356,11 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2199
2356
|
}
|
|
2200
2357
|
}
|
|
2201
2358
|
if (!isDeliveryActive()) return;
|
|
2202
|
-
if (
|
|
2359
|
+
if (
|
|
2360
|
+
outboundReply &&
|
|
2361
|
+
hasOutboundArtifacts &&
|
|
2362
|
+
deps.sendOutboundReplyArtifacts
|
|
2363
|
+
) {
|
|
2203
2364
|
try {
|
|
2204
2365
|
await deps.sendOutboundReplyArtifacts(turn, outboundReply, {
|
|
2205
2366
|
replyToPrompt: !finalText,
|
|
@@ -2261,7 +2422,11 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
2261
2422
|
};
|
|
2262
2423
|
if (
|
|
2263
2424
|
deps.scheduleActiveTurnDelivery &&
|
|
2264
|
-
(endPlan.kind === "text" ||
|
|
2425
|
+
(endPlan.kind === "text" ||
|
|
2426
|
+
endPlan.kind === "attachments-only" ||
|
|
2427
|
+
endPlan.shouldSendAbortMessage ||
|
|
2428
|
+
endPlan.shouldSendErrorMessage ||
|
|
2429
|
+
endPlan.shouldClearPreview)
|
|
2265
2430
|
) {
|
|
2266
2431
|
deps.scheduleActiveTurnDelivery(deliverActiveTurn);
|
|
2267
2432
|
return;
|
|
@@ -2352,7 +2517,13 @@ export interface TelegramSessionLifecycleHookRuntimeDeps<
|
|
|
2352
2517
|
TContext,
|
|
2353
2518
|
TQueueItem,
|
|
2354
2519
|
TModel = unknown,
|
|
2355
|
-
>
|
|
2520
|
+
>
|
|
2521
|
+
extends
|
|
2522
|
+
TelegramRuntimeEventRecorderPort,
|
|
2523
|
+
Omit<
|
|
2524
|
+
TelegramSessionShutdownRuntimeDeps<TQueueItem>,
|
|
2525
|
+
"isSessionActive" | "discardQueuedItems" | "applyState"
|
|
2526
|
+
> {
|
|
2356
2527
|
getCurrentModel: (ctx: TContext) => TModel | undefined;
|
|
2357
2528
|
loadConfig: () => Promise<void>;
|
|
2358
2529
|
applySessionStartState: (state: TelegramSessionStartState<TModel>) => void;
|
|
@@ -2360,23 +2531,10 @@ export interface TelegramSessionLifecycleHookRuntimeDeps<
|
|
|
2360
2531
|
prepareTempDir: () => Promise<unknown>;
|
|
2361
2532
|
updateStatus: (ctx: TContext) => void;
|
|
2362
2533
|
isSessionActive?: (ctx: TContext) => boolean;
|
|
2363
|
-
unbindDeferredDispatchContext?: () => void;
|
|
2364
2534
|
discardQueuedItems?: (ctx: TContext) => void;
|
|
2365
2535
|
applySessionShutdownState: (
|
|
2366
2536
|
state: TelegramSessionShutdownState<TQueueItem>,
|
|
2367
2537
|
) => 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
2538
|
}
|
|
2381
2539
|
|
|
2382
2540
|
export type TelegramSessionLifecycleHookEvent = unknown;
|
|
@@ -2401,9 +2559,8 @@ export function createTelegramSessionStateApplier<TQueueItem, TModel>(
|
|
|
2401
2559
|
};
|
|
2402
2560
|
}
|
|
2403
2561
|
|
|
2404
|
-
export interface TelegramQueueMutationRuntimeDeps<
|
|
2405
|
-
TContext
|
|
2406
|
-
> extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2562
|
+
export interface TelegramQueueMutationRuntimeDeps<TContext>
|
|
2563
|
+
extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2407
2564
|
ctx: TContext;
|
|
2408
2565
|
hasPendingDispatch?: () => boolean;
|
|
2409
2566
|
allocateLaneOrder?: () => number;
|
|
@@ -2414,9 +2571,8 @@ export interface TelegramQueueMutationRuntimeDeps<
|
|
|
2414
2571
|
updateStatus: (ctx: TContext) => void;
|
|
2415
2572
|
}
|
|
2416
2573
|
|
|
2417
|
-
export interface TelegramQueueMutationControllerDeps<
|
|
2418
|
-
TContext
|
|
2419
|
-
> extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2574
|
+
export interface TelegramQueueMutationControllerDeps<TContext>
|
|
2575
|
+
extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2420
2576
|
hasPendingDispatch?: () => boolean;
|
|
2421
2577
|
allocateLaneOrder?: () => number;
|
|
2422
2578
|
onItemsDiscarded?: (
|
|
@@ -2478,6 +2634,8 @@ export interface TelegramPromptEnqueueRuntimeDeps<
|
|
|
2478
2634
|
updateStatus: () => void;
|
|
2479
2635
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
2480
2636
|
assertExecutionCurrent?: () => void;
|
|
2637
|
+
/** Input-specific relocation must not fold or replace unrelated queued work. */
|
|
2638
|
+
preserveQueued?: boolean;
|
|
2481
2639
|
onQueued?: (turn: PendingTelegramTurn) => void;
|
|
2482
2640
|
}
|
|
2483
2641
|
|
|
@@ -2502,17 +2660,10 @@ export interface TelegramPromptEnqueueController<TMessage, TContext = unknown> {
|
|
|
2502
2660
|
messages: TMessage[],
|
|
2503
2661
|
ctx: TContext,
|
|
2504
2662
|
onQueued?: (turn: PendingTelegramTurn) => void,
|
|
2663
|
+
options?: { assertCurrent?: () => void; preserveQueued?: boolean },
|
|
2505
2664
|
) => Promise<PendingTelegramTurn>;
|
|
2506
2665
|
}
|
|
2507
2666
|
|
|
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
2667
|
export function buildTelegramSessionStartState<TModel = unknown>(
|
|
2517
2668
|
currentModel: TModel | undefined,
|
|
2518
2669
|
): TelegramSessionStartState<TModel> {
|
|
@@ -2554,7 +2705,7 @@ export async function startTelegramSessionRuntime<TContext, TModel = unknown>(
|
|
|
2554
2705
|
try {
|
|
2555
2706
|
deps.bindDeferredDispatchContext?.(deps.ctx);
|
|
2556
2707
|
} catch (error) {
|
|
2557
|
-
if (!
|
|
2708
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
2558
2709
|
}
|
|
2559
2710
|
deps.updateStatus();
|
|
2560
2711
|
}
|
|
@@ -2733,7 +2884,7 @@ function updateTelegramQueueStatusRuntime<TContext>(
|
|
|
2733
2884
|
try {
|
|
2734
2885
|
deps.updateStatus(deps.ctx);
|
|
2735
2886
|
} catch (error) {
|
|
2736
|
-
if (!
|
|
2887
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
2737
2888
|
}
|
|
2738
2889
|
}
|
|
2739
2890
|
|
|
@@ -2741,11 +2892,18 @@ function commitReorderedTelegramQueueItemsRuntime<TContext>(
|
|
|
2741
2892
|
items: TelegramQueueItem<TContext>[],
|
|
2742
2893
|
deps: TelegramQueueMutationRuntimeDeps<TContext>,
|
|
2743
2894
|
): void {
|
|
2744
|
-
const pendingHead = deps.hasPendingDispatch?.()
|
|
2895
|
+
const pendingHead = deps.hasPendingDispatch?.()
|
|
2896
|
+
? deps.getQueuedItems()[0]
|
|
2897
|
+
: undefined;
|
|
2745
2898
|
const ordered = [...items].sort(compareTelegramQueueItems);
|
|
2746
2899
|
if (pendingHead) {
|
|
2747
|
-
const index = ordered.findIndex(
|
|
2748
|
-
item
|
|
2900
|
+
const index = ordered.findIndex(
|
|
2901
|
+
(item) =>
|
|
2902
|
+
item.queueOrder === pendingHead.queueOrder &&
|
|
2903
|
+
item.kind === pendingHead.kind &&
|
|
2904
|
+
item.chatId === pendingHead.chatId &&
|
|
2905
|
+
item.replyToMessageId === pendingHead.replyToMessageId,
|
|
2906
|
+
);
|
|
2749
2907
|
if (index > 0) ordered.unshift(ordered.splice(index, 1)[0]!);
|
|
2750
2908
|
}
|
|
2751
2909
|
deps.setQueuedItems(ordered);
|
|
@@ -2826,23 +2984,44 @@ export function applyTelegramQueuePromptReactionDispositionRuntime<TContext>(
|
|
|
2826
2984
|
scope?: TelegramQueueMessageScope,
|
|
2827
2985
|
): boolean {
|
|
2828
2986
|
const queuedItems = deps.getQueuedItems();
|
|
2829
|
-
const suppression =
|
|
2830
|
-
disposition.kind === "
|
|
2987
|
+
const suppression =
|
|
2988
|
+
disposition.kind === "reaction-transition"
|
|
2989
|
+
? disposition.suppressionEmoji
|
|
2990
|
+
: disposition.kind === "suppressed"
|
|
2991
|
+
? disposition.emoji
|
|
2992
|
+
: disposition.kind === "priority-suppressed"
|
|
2993
|
+
? disposition.suppressionEmoji
|
|
2994
|
+
: undefined;
|
|
2831
2995
|
// Source-addressed control-lane prompts are explicit continuations. Synthetic model-switch turns have no source IDs.
|
|
2832
2996
|
// A Pi-owned dispatched head cannot be removed: agent_start must consume that exact item, not its successor.
|
|
2833
|
-
const cancelled =
|
|
2834
|
-
|
|
2835
|
-
|
|
2997
|
+
const cancelled =
|
|
2998
|
+
suppression && deps.hasPendingDispatch
|
|
2999
|
+
? queuedItems.filter(
|
|
3000
|
+
(item, index) =>
|
|
3001
|
+
isPendingTelegramTurn(item) &&
|
|
3002
|
+
item.queueLane === "control" &&
|
|
3003
|
+
isTelegramQueueItemInMessageScope(item, scope) &&
|
|
3004
|
+
item.sourceMessageIds.includes(messageId) &&
|
|
3005
|
+
!(index === 0 && deps.hasPendingDispatch!()),
|
|
3006
|
+
)
|
|
3007
|
+
: [];
|
|
2836
3008
|
if (cancelled.length) {
|
|
2837
|
-
if (
|
|
3009
|
+
if (
|
|
3010
|
+
cancelled.some((item) => (item.admissionReceipts?.length ?? 0) > 0) &&
|
|
3011
|
+
!deps.onItemsDiscarded
|
|
3012
|
+
)
|
|
3013
|
+
return false;
|
|
2838
3014
|
deps.onItemsDiscarded?.(cancelled, deps.ctx);
|
|
2839
|
-
deps.setQueuedItems(
|
|
3015
|
+
deps.setQueuedItems(
|
|
3016
|
+
queuedItems.filter((item) => !cancelled.includes(item)),
|
|
3017
|
+
);
|
|
2840
3018
|
updateTelegramQueueStatusRuntime(deps);
|
|
2841
3019
|
return true;
|
|
2842
3020
|
}
|
|
2843
3021
|
const changesLane = queuedItems.some((item) => {
|
|
2844
3022
|
if (
|
|
2845
|
-
!isPendingTelegramTurn(item) ||
|
|
3023
|
+
!isPendingTelegramTurn(item) ||
|
|
3024
|
+
item.queueLane === "control" ||
|
|
2846
3025
|
!isTelegramQueueItemInMessageScope(item, scope) ||
|
|
2847
3026
|
!item.sourceMessageIds.includes(messageId)
|
|
2848
3027
|
) {
|
|
@@ -2885,23 +3064,32 @@ export async function enqueueTelegramPromptTurnRuntime<
|
|
|
2885
3064
|
deps: TelegramPromptEnqueueRuntimeDeps<TMessage, TContext>,
|
|
2886
3065
|
): Promise<PendingTelegramTurn> {
|
|
2887
3066
|
deps.assertExecutionCurrent?.();
|
|
2888
|
-
const historyOrders = new Set(
|
|
2889
|
-
deps.
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
3067
|
+
const historyOrders = new Set(
|
|
3068
|
+
deps.preserveQueued
|
|
3069
|
+
? []
|
|
3070
|
+
: planTelegramPromptEnqueue(
|
|
3071
|
+
deps.getQueuedItems(),
|
|
3072
|
+
deps.getFoldQueuedPromptsIntoHistory(),
|
|
3073
|
+
).historyTurns.map((turn) => turn.queueOrder),
|
|
3074
|
+
);
|
|
3075
|
+
if (!deps.preserveQueued) deps.setFoldQueuedPromptsIntoHistory(false);
|
|
2893
3076
|
const buildTurn = await deps.prepareTurn(messages);
|
|
2894
3077
|
deps.assertExecutionCurrent?.();
|
|
2895
3078
|
// Preserve the Pi-owned head until agent_start, plus later arrivals and current edits/reactions.
|
|
2896
3079
|
const pendingDispatch = deps.hasPendingDispatch();
|
|
2897
3080
|
const historyTurns: PendingTelegramTurn[] = [];
|
|
2898
3081
|
const remainingItems = deps.getQueuedItems().filter((item, index) => {
|
|
2899
|
-
if (
|
|
2900
|
-
|
|
3082
|
+
if (
|
|
3083
|
+
(pendingDispatch && index === 0) ||
|
|
3084
|
+
!isPendingTelegramTurn(item) ||
|
|
3085
|
+
!historyOrders.has(item.queueOrder)
|
|
3086
|
+
)
|
|
3087
|
+
return true;
|
|
2901
3088
|
historyTurns.push(item);
|
|
2902
3089
|
return false;
|
|
2903
3090
|
});
|
|
2904
3091
|
const turn = buildTurn(historyTurns);
|
|
3092
|
+
deps.assertExecutionCurrent?.();
|
|
2905
3093
|
deps.setQueuedItems(appendTelegramQueueItem(remainingItems, turn));
|
|
2906
3094
|
deps.onQueued?.(turn);
|
|
2907
3095
|
deps.updateStatus();
|
|
@@ -2916,15 +3104,18 @@ export function createTelegramPromptEnqueueController<
|
|
|
2916
3104
|
deps: TelegramPromptEnqueueControllerDeps<TMessage, TContext>,
|
|
2917
3105
|
): TelegramPromptEnqueueController<TMessage, TContext> {
|
|
2918
3106
|
return {
|
|
2919
|
-
enqueue: (messages, ctx, onQueued) =>
|
|
3107
|
+
enqueue: (messages, ctx, onQueued, options) =>
|
|
2920
3108
|
enqueueTelegramPromptTurnRuntime(messages, {
|
|
2921
3109
|
...deps,
|
|
2922
3110
|
prepareTurn: (nextMessages) => deps.prepareTurn(nextMessages, ctx),
|
|
2923
3111
|
updateStatus: () => deps.updateStatus(ctx),
|
|
2924
3112
|
dispatchNextQueuedTelegramTurn: () =>
|
|
2925
3113
|
deps.dispatchNextQueuedTelegramTurn(ctx),
|
|
2926
|
-
assertExecutionCurrent: () =>
|
|
2927
|
-
deps.assertExecutionCurrent?.(messages)
|
|
3114
|
+
assertExecutionCurrent: () => {
|
|
3115
|
+
deps.assertExecutionCurrent?.(messages);
|
|
3116
|
+
options?.assertCurrent?.();
|
|
3117
|
+
},
|
|
3118
|
+
preserveQueued: options?.preserveQueued,
|
|
2928
3119
|
onQueued,
|
|
2929
3120
|
}),
|
|
2930
3121
|
};
|
|
@@ -3165,7 +3356,10 @@ export interface TelegramDispatchRuntimeDeps<TContext = unknown> {
|
|
|
3165
3356
|
) => void;
|
|
3166
3357
|
onPromptDispatchStart: (chatId: number) => void;
|
|
3167
3358
|
commitPromptDispatch?: (
|
|
3168
|
-
item: Extract<
|
|
3359
|
+
item: Extract<
|
|
3360
|
+
TelegramQueueDispatchAction<TContext>,
|
|
3361
|
+
{ kind: "prompt" }
|
|
3362
|
+
>["item"],
|
|
3169
3363
|
) => boolean;
|
|
3170
3364
|
sendUserMessage: (
|
|
3171
3365
|
content: Extract<
|
|
@@ -3191,7 +3385,10 @@ export interface TelegramQueueDispatchControllerDeps<
|
|
|
3191
3385
|
sendTextReply: TelegramControlRuntimeDeps<TContext>["sendTextReply"];
|
|
3192
3386
|
onPromptDispatchStart: (ctx: TContext, chatId: number) => void;
|
|
3193
3387
|
commitPromptDispatch?: (
|
|
3194
|
-
item: Extract<
|
|
3388
|
+
item: Extract<
|
|
3389
|
+
TelegramQueueDispatchAction<TContext>,
|
|
3390
|
+
{ kind: "prompt" }
|
|
3391
|
+
>["item"],
|
|
3195
3392
|
ctx: TContext,
|
|
3196
3393
|
) => boolean;
|
|
3197
3394
|
sendUserMessage: TelegramDispatchRuntimeDeps<TContext>["sendUserMessage"];
|
|
@@ -3232,7 +3429,9 @@ export function executeTelegramQueueDispatchPlan<TContext = unknown>(
|
|
|
3232
3429
|
deps.onPromptDispatchStart(plan.item.chatId);
|
|
3233
3430
|
try {
|
|
3234
3431
|
if (deps.commitPromptDispatch && !deps.commitPromptDispatch(plan.item)) {
|
|
3235
|
-
throw new Error(
|
|
3432
|
+
throw new Error(
|
|
3433
|
+
"Telegram prompt dispatch could not be committed durably.",
|
|
3434
|
+
);
|
|
3236
3435
|
}
|
|
3237
3436
|
deps.sendUserMessage(plan.item.content);
|
|
3238
3437
|
} catch (error) {
|
|
@@ -3390,9 +3589,13 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3390
3589
|
const nextItem = dispatchableItems[0];
|
|
3391
3590
|
if (nextDispatchAnnouncementRequested) {
|
|
3392
3591
|
if (nextDispatchAnnouncementAnchor) {
|
|
3393
|
-
const anchorRetained = retainedItems.includes(
|
|
3592
|
+
const anchorRetained = retainedItems.includes(
|
|
3593
|
+
nextDispatchAnnouncementAnchor,
|
|
3594
|
+
);
|
|
3394
3595
|
const anchorTransportActive =
|
|
3395
|
-
deps.isQueueItemTransportActive?.(
|
|
3596
|
+
deps.isQueueItemTransportActive?.(
|
|
3597
|
+
nextDispatchAnnouncementAnchor,
|
|
3598
|
+
) !== false;
|
|
3396
3599
|
if (!anchorRetained || !anchorTransportActive) {
|
|
3397
3600
|
nextDispatchAnnouncementRequested = false;
|
|
3398
3601
|
nextDispatchAnnouncementAnchor = undefined;
|
|
@@ -3403,10 +3606,7 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3403
3606
|
nextDispatchAnnouncementRequested = false;
|
|
3404
3607
|
}
|
|
3405
3608
|
}
|
|
3406
|
-
if (
|
|
3407
|
-
nextItem &&
|
|
3408
|
-
deps.hasPendingInboundQueueMutationForItem?.(nextItem)
|
|
3409
|
-
) {
|
|
3609
|
+
if (nextItem && deps.hasPendingInboundQueueMutationForItem?.(nextItem)) {
|
|
3410
3610
|
deps.updateStatus(ctx);
|
|
3411
3611
|
return;
|
|
3412
3612
|
}
|
|
@@ -3429,14 +3629,19 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3429
3629
|
if (dispatchPlan.kind === "prompt") {
|
|
3430
3630
|
const queueDrifted =
|
|
3431
3631
|
currentItems.length !== dispatchBasisItems.length ||
|
|
3432
|
-
currentItems.some(
|
|
3632
|
+
currentItems.some(
|
|
3633
|
+
(item, index) => item !== dispatchBasisItems[index],
|
|
3634
|
+
);
|
|
3433
3635
|
const dispatchEligibilityDrifted =
|
|
3434
3636
|
!deps.canDispatch(ctx) ||
|
|
3435
|
-
deps.hasPendingInboundQueueMutationForItem?.(dispatchPlan.item) ===
|
|
3436
|
-
|
|
3437
|
-
|
|
3637
|
+
deps.hasPendingInboundQueueMutationForItem?.(dispatchPlan.item) ===
|
|
3638
|
+
true ||
|
|
3639
|
+
deps.isQueueItemAdmissionReady?.(dispatchPlan.item) === false ||
|
|
3640
|
+
deps.isQueueItemTransportActive?.(dispatchPlan.item) === false;
|
|
3438
3641
|
if (queueDrifted || dispatchEligibilityDrifted) {
|
|
3439
|
-
const selectedItemRetained = currentItems.includes(
|
|
3642
|
+
const selectedItemRetained = currentItems.includes(
|
|
3643
|
+
dispatchPlan.item,
|
|
3644
|
+
);
|
|
3440
3645
|
const selectedTransportActive =
|
|
3441
3646
|
deps.isQueueItemTransportActive?.(dispatchPlan.item) !== false;
|
|
3442
3647
|
nextDispatchAnnouncementRequested =
|
|
@@ -3445,7 +3650,11 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3445
3650
|
? dispatchPlan.item
|
|
3446
3651
|
: undefined;
|
|
3447
3652
|
deps.updateStatus(ctx);
|
|
3448
|
-
if (
|
|
3653
|
+
if (
|
|
3654
|
+
nextDispatchAnnouncementRequested &&
|
|
3655
|
+
queueDrifted &&
|
|
3656
|
+
!dispatchEligibilityDrifted
|
|
3657
|
+
) {
|
|
3449
3658
|
controller.dispatchNext(ctx);
|
|
3450
3659
|
}
|
|
3451
3660
|
return false;
|
|
@@ -3461,51 +3670,52 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3461
3670
|
const executePlan = (): void => {
|
|
3462
3671
|
if (!commitDispatchPlan()) return;
|
|
3463
3672
|
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
|
-
|
|
3673
|
+
executeControlItem: (item) => {
|
|
3674
|
+
controlDispatchPending = true;
|
|
3675
|
+
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
3676
|
+
deps.updateStatus(ctx);
|
|
3677
|
+
void executeTelegramControlItemRuntime(item, {
|
|
3678
|
+
ctx,
|
|
3679
|
+
sendTextReply: deps.sendTextReply,
|
|
3680
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
3681
|
+
onSettled: (settledItem) => {
|
|
3682
|
+
try {
|
|
3683
|
+
deps.onControlSettled?.(settledItem, ctx);
|
|
3684
|
+
} catch (error) {
|
|
3685
|
+
deps.recordRuntimeEvent?.("control", error, {
|
|
3686
|
+
phase: "receipt-settlement",
|
|
3687
|
+
controlType: settledItem.controlType,
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3690
|
+
controlDispatchPending = false;
|
|
3691
|
+
if (deps.hasDispatchContext && !deps.hasDispatchContext())
|
|
3692
|
+
return;
|
|
3693
|
+
if (
|
|
3694
|
+
dispatchGeneration !== undefined &&
|
|
3695
|
+
deps.isDispatchGenerationActive &&
|
|
3696
|
+
!deps.isDispatchGenerationActive(dispatchGeneration)
|
|
3697
|
+
) {
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
deps.updateStatus(ctx);
|
|
3701
|
+
controller.dispatchNext(ctx);
|
|
3702
|
+
},
|
|
3703
|
+
});
|
|
3704
|
+
},
|
|
3705
|
+
onPromptDispatchStart: (chatId) => {
|
|
3706
|
+
deps.onPromptDispatchStart(ctx, chatId);
|
|
3707
|
+
},
|
|
3708
|
+
commitPromptDispatch: deps.commitPromptDispatch
|
|
3709
|
+
? (item) => deps.commitPromptDispatch!(item, ctx)
|
|
3710
|
+
: undefined,
|
|
3711
|
+
sendUserMessage: deps.sendUserMessage,
|
|
3712
|
+
onPromptDispatchFailure: (message) => {
|
|
3713
|
+
deps.onPromptDispatchFailure(ctx, message);
|
|
3714
|
+
},
|
|
3715
|
+
onIdle: () => {
|
|
3716
|
+
deps.updateStatus(ctx);
|
|
3717
|
+
},
|
|
3718
|
+
});
|
|
3509
3719
|
};
|
|
3510
3720
|
if (dispatchPlan.kind === "prompt" && nextDispatchAnnouncementRequested) {
|
|
3511
3721
|
nextDispatchAnnouncementRequested = false;
|
|
@@ -3513,38 +3723,43 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3513
3723
|
const announcementGeneration = nextDispatchAnnouncementGeneration;
|
|
3514
3724
|
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
3515
3725
|
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) {
|
|
3726
|
+
void deps
|
|
3727
|
+
.sendTextReply(
|
|
3728
|
+
dispatchPlan.item.chatId,
|
|
3729
|
+
dispatchPlan.item.replyToMessageId,
|
|
3730
|
+
"<b>⏩ Dispatching next queued turn.</b>",
|
|
3731
|
+
{ target: dispatchPlan.item.target, parseMode: "HTML" },
|
|
3732
|
+
)
|
|
3733
|
+
.catch((error) => {
|
|
3531
3734
|
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3532
|
-
phase: "next-announcement
|
|
3735
|
+
phase: "next-announcement",
|
|
3533
3736
|
});
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3737
|
+
})
|
|
3738
|
+
.finally(() => {
|
|
3739
|
+
try {
|
|
3740
|
+
deps.reconcileNextDispatchAnnouncementReplyOwnership?.(
|
|
3741
|
+
dispatchPlan.item,
|
|
3742
|
+
);
|
|
3743
|
+
} catch (error) {
|
|
3744
|
+
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3745
|
+
phase: "next-announcement-reply-ownership",
|
|
3746
|
+
});
|
|
3747
|
+
}
|
|
3748
|
+
controlDispatchPending = false;
|
|
3749
|
+
if (deps.hasDispatchContext && !deps.hasDispatchContext()) return;
|
|
3750
|
+
if (
|
|
3751
|
+
dispatchGeneration !== undefined &&
|
|
3752
|
+
deps.isDispatchGenerationActive &&
|
|
3753
|
+
!deps.isDispatchGenerationActive(dispatchGeneration)
|
|
3754
|
+
)
|
|
3755
|
+
return;
|
|
3756
|
+
if (announcementGeneration !== nextDispatchAnnouncementGeneration) {
|
|
3757
|
+
if (nextDispatchAnnouncementRequested)
|
|
3758
|
+
controller.dispatchNext(ctx);
|
|
3759
|
+
return;
|
|
3760
|
+
}
|
|
3761
|
+
executePlan();
|
|
3762
|
+
});
|
|
3548
3763
|
return;
|
|
3549
3764
|
}
|
|
3550
3765
|
executePlan();
|