@llblab/pi-kit 0.29.1 → 0.30.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/BACKLOG.md +3 -1
- package/CHANGELOG.md +10 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +12 -0
- package/node_modules/@llblab/pi-state-flow/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +17 -8
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +7 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +2 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +3 -2
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -3
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +3 -2
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +17 -8
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +6 -3
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +4 -2
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +13 -10
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -1
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +23 -0
- package/node_modules/@llblab/pi-telegram/README.md +10 -11
- package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
- package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
- package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
- package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +164 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +181 -77
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
- package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +642 -170
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +40 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2102 -760
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +34 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +318 -119
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
- package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +61 -51
- package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +23 -37
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +71 -52
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
- package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +16 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +261 -116
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +121 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4750 -1161
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +6 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +112 -45
- package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +165 -49
- package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +88 -24
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2276 -842
- package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +178 -82
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2438 -645
- package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
- package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +43 -35
- package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
- package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +390 -82
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +262 -99
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
- package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
- package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
- package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1209 -431
- package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
- package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +5329 -1712
- package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +703 -212
- package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
- package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
- package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
- package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
- package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
- package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
- package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
- package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +125 -125
- package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +165 -149
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
- package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +523 -289
- package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
- package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +8527 -2149
- package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
- package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
- package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
- package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
- package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +488 -359
- package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +322 -122
- package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
- package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +5329 -1817
- package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +5194 -1600
- package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
- package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
- package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
- package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
- package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
|
@@ -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,
|
|
@@ -1029,38 +1146,40 @@ export function applyTelegramQueuePromptReactionDisposition<
|
|
|
1029
1146
|
for (const [index, item] of items.entries()) {
|
|
1030
1147
|
if (
|
|
1031
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,10 +2555,10 @@ 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;
|
|
2561
|
+
hasPendingDispatch?: () => boolean;
|
|
2408
2562
|
allocateLaneOrder?: () => number;
|
|
2409
2563
|
onItemsDiscarded?: (
|
|
2410
2564
|
items: readonly TelegramQueueItem<TContext>[],
|
|
@@ -2413,9 +2567,9 @@ export interface TelegramQueueMutationRuntimeDeps<
|
|
|
2413
2567
|
updateStatus: (ctx: TContext) => void;
|
|
2414
2568
|
}
|
|
2415
2569
|
|
|
2416
|
-
export interface TelegramQueueMutationControllerDeps<
|
|
2417
|
-
TContext
|
|
2418
|
-
|
|
2570
|
+
export interface TelegramQueueMutationControllerDeps<TContext>
|
|
2571
|
+
extends TelegramQueueStore<TContext>, TelegramRuntimeEventRecorderPort {
|
|
2572
|
+
hasPendingDispatch?: () => boolean;
|
|
2419
2573
|
allocateLaneOrder?: () => number;
|
|
2420
2574
|
onItemsDiscarded?: (
|
|
2421
2575
|
items: readonly TelegramQueueItem<TContext>[],
|
|
@@ -2476,6 +2630,8 @@ export interface TelegramPromptEnqueueRuntimeDeps<
|
|
|
2476
2630
|
updateStatus: () => void;
|
|
2477
2631
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
2478
2632
|
assertExecutionCurrent?: () => void;
|
|
2633
|
+
/** Input-specific relocation must not fold or replace unrelated queued work. */
|
|
2634
|
+
preserveQueued?: boolean;
|
|
2479
2635
|
onQueued?: (turn: PendingTelegramTurn) => void;
|
|
2480
2636
|
}
|
|
2481
2637
|
|
|
@@ -2500,17 +2656,10 @@ export interface TelegramPromptEnqueueController<TMessage, TContext = unknown> {
|
|
|
2500
2656
|
messages: TMessage[],
|
|
2501
2657
|
ctx: TContext,
|
|
2502
2658
|
onQueued?: (turn: PendingTelegramTurn) => void,
|
|
2659
|
+
options?: { assertCurrent?: () => void; preserveQueued?: boolean },
|
|
2503
2660
|
) => Promise<PendingTelegramTurn>;
|
|
2504
2661
|
}
|
|
2505
2662
|
|
|
2506
|
-
function isTelegramStaleContextError(error: unknown): boolean {
|
|
2507
|
-
return (
|
|
2508
|
-
error instanceof Error &&
|
|
2509
|
-
(error.message.includes("stale after session") ||
|
|
2510
|
-
error.message.includes("stale ctx"))
|
|
2511
|
-
);
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
2663
|
export function buildTelegramSessionStartState<TModel = unknown>(
|
|
2515
2664
|
currentModel: TModel | undefined,
|
|
2516
2665
|
): TelegramSessionStartState<TModel> {
|
|
@@ -2552,7 +2701,7 @@ export async function startTelegramSessionRuntime<TContext, TModel = unknown>(
|
|
|
2552
2701
|
try {
|
|
2553
2702
|
deps.bindDeferredDispatchContext?.(deps.ctx);
|
|
2554
2703
|
} catch (error) {
|
|
2555
|
-
if (!
|
|
2704
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
2556
2705
|
}
|
|
2557
2706
|
deps.updateStatus();
|
|
2558
2707
|
}
|
|
@@ -2731,7 +2880,7 @@ function updateTelegramQueueStatusRuntime<TContext>(
|
|
|
2731
2880
|
try {
|
|
2732
2881
|
deps.updateStatus(deps.ctx);
|
|
2733
2882
|
} catch (error) {
|
|
2734
|
-
if (!
|
|
2883
|
+
if (!isPiStaleContextError(error)) throw error;
|
|
2735
2884
|
}
|
|
2736
2885
|
}
|
|
2737
2886
|
|
|
@@ -2739,7 +2888,21 @@ function commitReorderedTelegramQueueItemsRuntime<TContext>(
|
|
|
2739
2888
|
items: TelegramQueueItem<TContext>[],
|
|
2740
2889
|
deps: TelegramQueueMutationRuntimeDeps<TContext>,
|
|
2741
2890
|
): void {
|
|
2742
|
-
deps.
|
|
2891
|
+
const pendingHead = deps.hasPendingDispatch?.()
|
|
2892
|
+
? deps.getQueuedItems()[0]
|
|
2893
|
+
: undefined;
|
|
2894
|
+
const ordered = [...items].sort(compareTelegramQueueItems);
|
|
2895
|
+
if (pendingHead) {
|
|
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
|
+
);
|
|
2903
|
+
if (index > 0) ordered.unshift(ordered.splice(index, 1)[0]!);
|
|
2904
|
+
}
|
|
2905
|
+
deps.setQueuedItems(ordered);
|
|
2743
2906
|
updateTelegramQueueStatusRuntime(deps);
|
|
2744
2907
|
}
|
|
2745
2908
|
|
|
@@ -2817,9 +2980,44 @@ export function applyTelegramQueuePromptReactionDispositionRuntime<TContext>(
|
|
|
2817
2980
|
scope?: TelegramQueueMessageScope,
|
|
2818
2981
|
): boolean {
|
|
2819
2982
|
const queuedItems = deps.getQueuedItems();
|
|
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;
|
|
2991
|
+
// Source-addressed control-lane prompts are explicit continuations. Synthetic model-switch turns have no source IDs.
|
|
2992
|
+
// A Pi-owned dispatched head cannot be removed: agent_start must consume that exact item, not its successor.
|
|
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
|
+
: [];
|
|
3004
|
+
if (cancelled.length) {
|
|
3005
|
+
if (
|
|
3006
|
+
cancelled.some((item) => (item.admissionReceipts?.length ?? 0) > 0) &&
|
|
3007
|
+
!deps.onItemsDiscarded
|
|
3008
|
+
)
|
|
3009
|
+
return false;
|
|
3010
|
+
deps.onItemsDiscarded?.(cancelled, deps.ctx);
|
|
3011
|
+
deps.setQueuedItems(
|
|
3012
|
+
queuedItems.filter((item) => !cancelled.includes(item)),
|
|
3013
|
+
);
|
|
3014
|
+
updateTelegramQueueStatusRuntime(deps);
|
|
3015
|
+
return true;
|
|
3016
|
+
}
|
|
2820
3017
|
const changesLane = queuedItems.some((item) => {
|
|
2821
3018
|
if (
|
|
2822
3019
|
!isPendingTelegramTurn(item) ||
|
|
3020
|
+
item.queueLane === "control" ||
|
|
2823
3021
|
!isTelegramQueueItemInMessageScope(item, scope) ||
|
|
2824
3022
|
!item.sourceMessageIds.includes(messageId)
|
|
2825
3023
|
) {
|
|
@@ -2862,23 +3060,32 @@ export async function enqueueTelegramPromptTurnRuntime<
|
|
|
2862
3060
|
deps: TelegramPromptEnqueueRuntimeDeps<TMessage, TContext>,
|
|
2863
3061
|
): Promise<PendingTelegramTurn> {
|
|
2864
3062
|
deps.assertExecutionCurrent?.();
|
|
2865
|
-
const historyOrders = new Set(
|
|
2866
|
-
deps.
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
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);
|
|
2870
3072
|
const buildTurn = await deps.prepareTurn(messages);
|
|
2871
3073
|
deps.assertExecutionCurrent?.();
|
|
2872
3074
|
// Preserve the Pi-owned head until agent_start, plus later arrivals and current edits/reactions.
|
|
2873
3075
|
const pendingDispatch = deps.hasPendingDispatch();
|
|
2874
3076
|
const historyTurns: PendingTelegramTurn[] = [];
|
|
2875
3077
|
const remainingItems = deps.getQueuedItems().filter((item, index) => {
|
|
2876
|
-
if (
|
|
2877
|
-
|
|
3078
|
+
if (
|
|
3079
|
+
(pendingDispatch && index === 0) ||
|
|
3080
|
+
!isPendingTelegramTurn(item) ||
|
|
3081
|
+
!historyOrders.has(item.queueOrder)
|
|
3082
|
+
)
|
|
3083
|
+
return true;
|
|
2878
3084
|
historyTurns.push(item);
|
|
2879
3085
|
return false;
|
|
2880
3086
|
});
|
|
2881
3087
|
const turn = buildTurn(historyTurns);
|
|
3088
|
+
deps.assertExecutionCurrent?.();
|
|
2882
3089
|
deps.setQueuedItems(appendTelegramQueueItem(remainingItems, turn));
|
|
2883
3090
|
deps.onQueued?.(turn);
|
|
2884
3091
|
deps.updateStatus();
|
|
@@ -2893,15 +3100,18 @@ export function createTelegramPromptEnqueueController<
|
|
|
2893
3100
|
deps: TelegramPromptEnqueueControllerDeps<TMessage, TContext>,
|
|
2894
3101
|
): TelegramPromptEnqueueController<TMessage, TContext> {
|
|
2895
3102
|
return {
|
|
2896
|
-
enqueue: (messages, ctx, onQueued) =>
|
|
3103
|
+
enqueue: (messages, ctx, onQueued, options) =>
|
|
2897
3104
|
enqueueTelegramPromptTurnRuntime(messages, {
|
|
2898
3105
|
...deps,
|
|
2899
3106
|
prepareTurn: (nextMessages) => deps.prepareTurn(nextMessages, ctx),
|
|
2900
3107
|
updateStatus: () => deps.updateStatus(ctx),
|
|
2901
3108
|
dispatchNextQueuedTelegramTurn: () =>
|
|
2902
3109
|
deps.dispatchNextQueuedTelegramTurn(ctx),
|
|
2903
|
-
assertExecutionCurrent: () =>
|
|
2904
|
-
deps.assertExecutionCurrent?.(messages)
|
|
3110
|
+
assertExecutionCurrent: () => {
|
|
3111
|
+
deps.assertExecutionCurrent?.(messages);
|
|
3112
|
+
options?.assertCurrent?.();
|
|
3113
|
+
},
|
|
3114
|
+
preserveQueued: options?.preserveQueued,
|
|
2905
3115
|
onQueued,
|
|
2906
3116
|
}),
|
|
2907
3117
|
};
|
|
@@ -3142,7 +3352,10 @@ export interface TelegramDispatchRuntimeDeps<TContext = unknown> {
|
|
|
3142
3352
|
) => void;
|
|
3143
3353
|
onPromptDispatchStart: (chatId: number) => void;
|
|
3144
3354
|
commitPromptDispatch?: (
|
|
3145
|
-
item: Extract<
|
|
3355
|
+
item: Extract<
|
|
3356
|
+
TelegramQueueDispatchAction<TContext>,
|
|
3357
|
+
{ kind: "prompt" }
|
|
3358
|
+
>["item"],
|
|
3146
3359
|
) => boolean;
|
|
3147
3360
|
sendUserMessage: (
|
|
3148
3361
|
content: Extract<
|
|
@@ -3168,7 +3381,10 @@ export interface TelegramQueueDispatchControllerDeps<
|
|
|
3168
3381
|
sendTextReply: TelegramControlRuntimeDeps<TContext>["sendTextReply"];
|
|
3169
3382
|
onPromptDispatchStart: (ctx: TContext, chatId: number) => void;
|
|
3170
3383
|
commitPromptDispatch?: (
|
|
3171
|
-
item: Extract<
|
|
3384
|
+
item: Extract<
|
|
3385
|
+
TelegramQueueDispatchAction<TContext>,
|
|
3386
|
+
{ kind: "prompt" }
|
|
3387
|
+
>["item"],
|
|
3172
3388
|
ctx: TContext,
|
|
3173
3389
|
) => boolean;
|
|
3174
3390
|
sendUserMessage: TelegramDispatchRuntimeDeps<TContext>["sendUserMessage"];
|
|
@@ -3209,7 +3425,9 @@ export function executeTelegramQueueDispatchPlan<TContext = unknown>(
|
|
|
3209
3425
|
deps.onPromptDispatchStart(plan.item.chatId);
|
|
3210
3426
|
try {
|
|
3211
3427
|
if (deps.commitPromptDispatch && !deps.commitPromptDispatch(plan.item)) {
|
|
3212
|
-
throw new Error(
|
|
3428
|
+
throw new Error(
|
|
3429
|
+
"Telegram prompt dispatch could not be committed durably.",
|
|
3430
|
+
);
|
|
3213
3431
|
}
|
|
3214
3432
|
deps.sendUserMessage(plan.item.content);
|
|
3215
3433
|
} catch (error) {
|
|
@@ -3367,9 +3585,13 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3367
3585
|
const nextItem = dispatchableItems[0];
|
|
3368
3586
|
if (nextDispatchAnnouncementRequested) {
|
|
3369
3587
|
if (nextDispatchAnnouncementAnchor) {
|
|
3370
|
-
const anchorRetained = retainedItems.includes(
|
|
3588
|
+
const anchorRetained = retainedItems.includes(
|
|
3589
|
+
nextDispatchAnnouncementAnchor,
|
|
3590
|
+
);
|
|
3371
3591
|
const anchorTransportActive =
|
|
3372
|
-
deps.isQueueItemTransportActive?.(
|
|
3592
|
+
deps.isQueueItemTransportActive?.(
|
|
3593
|
+
nextDispatchAnnouncementAnchor,
|
|
3594
|
+
) !== false;
|
|
3373
3595
|
if (!anchorRetained || !anchorTransportActive) {
|
|
3374
3596
|
nextDispatchAnnouncementRequested = false;
|
|
3375
3597
|
nextDispatchAnnouncementAnchor = undefined;
|
|
@@ -3380,10 +3602,7 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3380
3602
|
nextDispatchAnnouncementRequested = false;
|
|
3381
3603
|
}
|
|
3382
3604
|
}
|
|
3383
|
-
if (
|
|
3384
|
-
nextItem &&
|
|
3385
|
-
deps.hasPendingInboundQueueMutationForItem?.(nextItem)
|
|
3386
|
-
) {
|
|
3605
|
+
if (nextItem && deps.hasPendingInboundQueueMutationForItem?.(nextItem)) {
|
|
3387
3606
|
deps.updateStatus(ctx);
|
|
3388
3607
|
return;
|
|
3389
3608
|
}
|
|
@@ -3406,14 +3625,19 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3406
3625
|
if (dispatchPlan.kind === "prompt") {
|
|
3407
3626
|
const queueDrifted =
|
|
3408
3627
|
currentItems.length !== dispatchBasisItems.length ||
|
|
3409
|
-
currentItems.some(
|
|
3628
|
+
currentItems.some(
|
|
3629
|
+
(item, index) => item !== dispatchBasisItems[index],
|
|
3630
|
+
);
|
|
3410
3631
|
const dispatchEligibilityDrifted =
|
|
3411
3632
|
!deps.canDispatch(ctx) ||
|
|
3412
|
-
deps.hasPendingInboundQueueMutationForItem?.(dispatchPlan.item) ===
|
|
3413
|
-
|
|
3414
|
-
|
|
3633
|
+
deps.hasPendingInboundQueueMutationForItem?.(dispatchPlan.item) ===
|
|
3634
|
+
true ||
|
|
3635
|
+
deps.isQueueItemAdmissionReady?.(dispatchPlan.item) === false ||
|
|
3636
|
+
deps.isQueueItemTransportActive?.(dispatchPlan.item) === false;
|
|
3415
3637
|
if (queueDrifted || dispatchEligibilityDrifted) {
|
|
3416
|
-
const selectedItemRetained = currentItems.includes(
|
|
3638
|
+
const selectedItemRetained = currentItems.includes(
|
|
3639
|
+
dispatchPlan.item,
|
|
3640
|
+
);
|
|
3417
3641
|
const selectedTransportActive =
|
|
3418
3642
|
deps.isQueueItemTransportActive?.(dispatchPlan.item) !== false;
|
|
3419
3643
|
nextDispatchAnnouncementRequested =
|
|
@@ -3422,7 +3646,11 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3422
3646
|
? dispatchPlan.item
|
|
3423
3647
|
: undefined;
|
|
3424
3648
|
deps.updateStatus(ctx);
|
|
3425
|
-
if (
|
|
3649
|
+
if (
|
|
3650
|
+
nextDispatchAnnouncementRequested &&
|
|
3651
|
+
queueDrifted &&
|
|
3652
|
+
!dispatchEligibilityDrifted
|
|
3653
|
+
) {
|
|
3426
3654
|
controller.dispatchNext(ctx);
|
|
3427
3655
|
}
|
|
3428
3656
|
return false;
|
|
@@ -3438,51 +3666,52 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3438
3666
|
const executePlan = (): void => {
|
|
3439
3667
|
if (!commitDispatchPlan()) return;
|
|
3440
3668
|
executeTelegramQueueDispatchPlan(dispatchPlan, {
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
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
|
+
});
|
|
3486
3715
|
};
|
|
3487
3716
|
if (dispatchPlan.kind === "prompt" && nextDispatchAnnouncementRequested) {
|
|
3488
3717
|
nextDispatchAnnouncementRequested = false;
|
|
@@ -3490,38 +3719,43 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
|
|
|
3490
3719
|
const announcementGeneration = nextDispatchAnnouncementGeneration;
|
|
3491
3720
|
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
3492
3721
|
deps.updateStatus(ctx);
|
|
3493
|
-
void deps
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
});
|
|
3502
|
-
}).finally(() => {
|
|
3503
|
-
try {
|
|
3504
|
-
deps.reconcileNextDispatchAnnouncementReplyOwnership?.(
|
|
3505
|
-
dispatchPlan.item,
|
|
3506
|
-
);
|
|
3507
|
-
} 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) => {
|
|
3508
3730
|
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
3509
|
-
phase: "next-announcement
|
|
3731
|
+
phase: "next-announcement",
|
|
3510
3732
|
});
|
|
3511
|
-
}
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
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
|
+
});
|
|
3525
3759
|
return;
|
|
3526
3760
|
}
|
|
3527
3761
|
executePlan();
|