@llblab/pi-kit 0.33.1 → 0.34.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/BACKLOG.md +2 -1
- package/CHANGELOG.md +9 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-telegram/AGENTS.md +6 -5
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +6 -1
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +20 -0
- package/node_modules/@llblab/pi-telegram/README.md +28 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +8 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +41 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +10 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +34 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +8 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +21 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +33 -28
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +18 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +28 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +6 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +100 -99
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +3 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +3 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +6 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +57 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +313 -25
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +4 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +62 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +1 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +6 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +11 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +11 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +57 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +13 -32
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +44 -15
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +25 -14
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +4 -0
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +8 -6
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +17 -17
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +52 -3
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +13 -5
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +37 -23
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +23 -7
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +52 -47
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +18 -2
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +34 -28
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +114 -162
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +8 -0
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +7 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +328 -33
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +51 -1
- package/node_modules/@llblab/pi-telegram/lib/status.ts +7 -7
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +11 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +57 -5
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +44 -21
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +2 -2
|
@@ -37,7 +37,12 @@ interface TelegramQueueMenuItem {
|
|
|
37
37
|
queueOrder: number;
|
|
38
38
|
isGuest: boolean;
|
|
39
39
|
queuePosition: number;
|
|
40
|
-
|
|
40
|
+
queueLane: Queue.TelegramQueueLane;
|
|
41
|
+
canSelectLane: boolean;
|
|
42
|
+
canSelectDisposition: boolean;
|
|
43
|
+
dispatchProtected: boolean;
|
|
44
|
+
attemptFrozen: boolean;
|
|
45
|
+
exceedsIdleCapacity: boolean;
|
|
41
46
|
priorityEmoji?: string;
|
|
42
47
|
reactionSuppressionEmoji?: string;
|
|
43
48
|
hasAttachments: boolean;
|
|
@@ -60,15 +65,26 @@ function getTelegramQueueItemPromptText<Context>(
|
|
|
60
65
|
|
|
61
66
|
function toTelegramQueueMenuItems<Context>(
|
|
62
67
|
items: readonly Queue.TelegramQueueItem<Context>[],
|
|
68
|
+
pendingDispatch = false,
|
|
63
69
|
): TelegramQueueMenuItem[] {
|
|
70
|
+
const head = items[0];
|
|
64
71
|
return items.map((item, index) => {
|
|
72
|
+
const dispatchProtected = pendingDispatch && (item === head ||
|
|
73
|
+
(head?.kind === "prompt" && head.steerPacketTail?.includes(item as Queue.PendingTelegramTurn) === true));
|
|
74
|
+
const attemptFrozen = item.kind === "prompt" && item.steerAttempted === true && !dispatchProtected;
|
|
75
|
+
const editable = item.kind === "prompt" && item.guestQueryId === undefined && item.guestInlineMessageId === undefined && !dispatchProtected && !attemptFrozen;
|
|
65
76
|
return {
|
|
66
77
|
chatId: item.chatId,
|
|
67
78
|
replyToMessageId: item.replyToMessageId,
|
|
68
79
|
queueOrder: item.queueOrder,
|
|
69
80
|
isGuest: item.kind === "prompt" && item.guestQueryId !== undefined,
|
|
70
81
|
queuePosition: index + 1,
|
|
71
|
-
|
|
82
|
+
queueLane: item.queueLane,
|
|
83
|
+
canSelectLane: editable && item.queueLane !== "control",
|
|
84
|
+
canSelectDisposition: editable && (item.queueLane !== "control" || item.sourceMessageIds.includes(item.replyToMessageId)),
|
|
85
|
+
dispatchProtected, attemptFrozen,
|
|
86
|
+
exceedsIdleCapacity: item.kind === "prompt" && item.queueLane === "steer" && !item.steerAttempted &&
|
|
87
|
+
!Queue.isTelegramSteerItemWithinIdleCapacity(item),
|
|
72
88
|
priorityEmoji: item.kind === "prompt" ? item.priorityEmoji : undefined,
|
|
73
89
|
reactionSuppressionEmoji:
|
|
74
90
|
item.kind === "prompt" ? item.reactionSuppressionEmoji : undefined,
|
|
@@ -98,18 +114,16 @@ function buildTelegramQueueMenuReplyMarkup(
|
|
|
98
114
|
const refreshRow = [{ text: "🔄 Refresh", callback_data: refreshData }];
|
|
99
115
|
if (items.length === 0) return { inline_keyboard: [backRow, refreshRow] };
|
|
100
116
|
const rows = items.map((item) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
: item.
|
|
104
|
-
? `${item.priorityEmoji ?? "⚡"} `
|
|
105
|
-
: item.hasAttachments
|
|
106
|
-
? "📎 "
|
|
107
|
-
: "";
|
|
117
|
+
const badge = getTelegramQueueMenuLaneBadge(item);
|
|
118
|
+
const prefix = item.reactionSuppressionEmoji ? "👎 " :
|
|
119
|
+
badge ? `${badge} ` : item.hasAttachments ? "📎 " : "";
|
|
108
120
|
const position = item.reactionSuppressionEmoji
|
|
109
121
|
? formatSkippedTelegramQueuePosition(item.queuePosition)
|
|
110
122
|
: String(item.queuePosition);
|
|
111
123
|
const ordinalSeparator = item.reactionSuppressionEmoji ? "\u200A" : "";
|
|
112
|
-
const
|
|
124
|
+
const state = item.attemptFrozen ? " · frozen" : item.dispatchProtected ? " · dispatch protected" :
|
|
125
|
+
item.exceedsIdleCapacity ? " · idle capacity" : "";
|
|
126
|
+
const label = `${position}${ordinalSeparator}. ${prefix}${item.statusSummary}${state}`;
|
|
113
127
|
return [
|
|
114
128
|
{
|
|
115
129
|
text: label,
|
|
@@ -165,18 +179,26 @@ function escapeTelegramQueueMenuHtmlPreview(text: string): string {
|
|
|
165
179
|
return truncated ? escaped + suffix : escaped;
|
|
166
180
|
}
|
|
167
181
|
|
|
182
|
+
function getTelegramQueueMenuLaneBadge(item: Pick<TelegramQueueMenuItem, "queueLane">): string {
|
|
183
|
+
if (item.queueLane === "control") return "⚡";
|
|
184
|
+
if (item.queueLane === "steer") return "🔥";
|
|
185
|
+
if (item.queueLane !== "priority") return "";
|
|
186
|
+
return "👍";
|
|
187
|
+
}
|
|
188
|
+
|
|
168
189
|
function getTelegramQueueMenuItemText(item: TelegramQueueMenuItem): string {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
: item.isPriority
|
|
172
|
-
? ` ${item.priorityEmoji ?? "⚡"}`
|
|
173
|
-
: "";
|
|
190
|
+
const marker = item.reactionSuppressionEmoji ? "👎" : getTelegramQueueMenuLaneBadge(item);
|
|
191
|
+
const badge = marker ? ` ${marker}` : "";
|
|
174
192
|
const position = item.reactionSuppressionEmoji
|
|
175
193
|
? `<s>${item.queuePosition}</s>.`
|
|
176
194
|
: `<b>${item.queuePosition}.</b>`;
|
|
177
195
|
const heading = `${position}${badge}`;
|
|
178
196
|
const preview = `<pre>${escapeTelegramQueueMenuHtmlPreview(item.promptText)}</pre>`;
|
|
179
|
-
|
|
197
|
+
const note = item.attemptFrozen ? "\n<b>⚠️ Attempt frozen.</b>\nSDK/model use is unconfirmed; not editable or automatically retried." :
|
|
198
|
+
item.dispatchProtected ? "\n<b>⏳ Dispatch protected.</b>\nWaiting for agent_start; SDK/model use is unconfirmed." :
|
|
199
|
+
item.exceedsIdleCapacity ? "\n<b>⚠️ Idle Steer capacity exceeded.</b>\nEdit the source or choose Priority/Normal; source groups are never split." :
|
|
200
|
+
item.queueLane === "control" ? "\nControl lane is producer-owned." : "";
|
|
201
|
+
return `${heading}\n${preview}${note}`;
|
|
180
202
|
}
|
|
181
203
|
|
|
182
204
|
function buildTelegramGuestQueueItemSubmenuReplyMarkup(
|
|
@@ -195,37 +217,18 @@ function buildTelegramGuestQueueItemSubmenuReplyMarkup(
|
|
|
195
217
|
};
|
|
196
218
|
}
|
|
197
219
|
|
|
198
|
-
function buildTelegramQueueItemSubmenuReplyMarkup(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
callback_data: `queue:prio-set:${chatId}:${replyToMessageId}:priority`,
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
text: isPriority ? "⚫️ Normal" : "🔵 Normal",
|
|
214
|
-
callback_data: `queue:prio-set:${chatId}:${replyToMessageId}:normal`,
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
-
[
|
|
218
|
-
{
|
|
219
|
-
text: isSkipped ? "⚫️ Keep" : "🟢 Keep",
|
|
220
|
-
callback_data: `queue:skip-set:${chatId}:${replyToMessageId}:keep`,
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
text: isSkipped ? "🔴 Skip" : "⚫️ Skip",
|
|
224
|
-
callback_data: `queue:skip-set:${chatId}:${replyToMessageId}:skip`,
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
],
|
|
228
|
-
};
|
|
220
|
+
function buildTelegramQueueItemSubmenuReplyMarkup(item: TelegramQueueMenuItem): TelegramQueueMenuReplyMarkup {
|
|
221
|
+
const rows: TelegramQueueMenuReplyMarkup["inline_keyboard"] = [[{ text: "⬆️ Back", callback_data: "queue:list" }]];
|
|
222
|
+
if (item.canSelectLane) rows.push([
|
|
223
|
+
{ text: item.queueLane === "default" ? "🔵 Normal" : "⚫️ Normal", callback_data: `queue:lane-set:${item.chatId}:${item.replyToMessageId}:normal` },
|
|
224
|
+
{ text: item.queueLane === "priority" ? "🟣 Priority" : "⚫️ Priority", callback_data: `queue:lane-set:${item.chatId}:${item.replyToMessageId}:priority` },
|
|
225
|
+
{ text: item.queueLane === "steer" ? "🟡 Steer" : "⚫️ Steer", callback_data: `queue:lane-set:${item.chatId}:${item.replyToMessageId}:steer` },
|
|
226
|
+
]);
|
|
227
|
+
if (item.canSelectDisposition) rows.push([
|
|
228
|
+
{ text: item.reactionSuppressionEmoji ? "⚫️ Keep" : "🟢 Keep", callback_data: `queue:skip-set:${item.chatId}:${item.replyToMessageId}:keep` },
|
|
229
|
+
{ text: item.reactionSuppressionEmoji ? "🔴 Skip" : "⚫️ Skip", callback_data: `queue:skip-set:${item.chatId}:${item.replyToMessageId}:skip` },
|
|
230
|
+
]);
|
|
231
|
+
return { inline_keyboard: rows };
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
interface TelegramQueueMenuCallbackDeps<Context = unknown> {
|
|
@@ -236,12 +239,7 @@ interface TelegramQueueMenuCallbackDeps<Context = unknown> {
|
|
|
236
239
|
) => TelegramQueueMenuItem | undefined;
|
|
237
240
|
findGuestItem: (queueOrder: number) => TelegramQueueMenuItem | undefined;
|
|
238
241
|
skipGuest: (queueOrder: number, ctx: Context) => Promise<boolean>;
|
|
239
|
-
|
|
240
|
-
setPriority: (
|
|
241
|
-
chatId: number,
|
|
242
|
-
replyToMessageId: number,
|
|
243
|
-
enabled: boolean,
|
|
244
|
-
) => boolean;
|
|
242
|
+
setLane: (chatId: number, replyToMessageId: number, lane: Exclude<Queue.TelegramQueueLane, "control">) => boolean;
|
|
245
243
|
setSkipped: (
|
|
246
244
|
chatId: number,
|
|
247
245
|
replyToMessageId: number,
|
|
@@ -307,7 +305,13 @@ async function handleTelegramQueueMenuCallback<Context>(
|
|
|
307
305
|
}
|
|
308
306
|
const guestSkipMatch = data.match(/^queue:guest-skip:(\d+)$/);
|
|
309
307
|
if (guestSkipMatch) {
|
|
310
|
-
const
|
|
308
|
+
const queueOrder = Number(guestSkipMatch[1]);
|
|
309
|
+
const item = deps.findGuestItem(queueOrder);
|
|
310
|
+
if (item?.dispatchProtected || item?.attemptFrozen) {
|
|
311
|
+
await deps.answerCallbackQuery(callbackQueryId, "Queue item is protected");
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
const skipped = await deps.skipGuest(queueOrder, ctx);
|
|
311
315
|
if (!skipped) {
|
|
312
316
|
await refreshStaleTelegramQueueMenuItem(
|
|
313
317
|
callbackQueryId,
|
|
@@ -339,16 +343,16 @@ async function handleTelegramQueueMenuCallback<Context>(
|
|
|
339
343
|
return true;
|
|
340
344
|
}
|
|
341
345
|
const prioSetMatch = data.match(
|
|
342
|
-
/^queue:prio-set:(\d+):(\d+):(priority|normal)$/,
|
|
346
|
+
/^queue:(?:lane-set|prio-set):(\d+):(\d+):(steer|priority|normal)$/,
|
|
343
347
|
);
|
|
344
348
|
if (prioSetMatch) {
|
|
345
|
-
await
|
|
349
|
+
await handleTelegramQueueMenuLaneSet(
|
|
346
350
|
callbackQueryId,
|
|
347
351
|
replyChatId,
|
|
348
352
|
replyMessageId,
|
|
349
353
|
Number(prioSetMatch[1]),
|
|
350
354
|
Number(prioSetMatch[2]),
|
|
351
|
-
prioSetMatch[3] === "priority",
|
|
355
|
+
prioSetMatch[3] === "normal" ? "default" : prioSetMatch[3] as "steer" | "priority",
|
|
352
356
|
ctx,
|
|
353
357
|
deps,
|
|
354
358
|
);
|
|
@@ -448,7 +452,9 @@ async function handleTelegramGuestQueueMenuPick<Context>(
|
|
|
448
452
|
replyChatId,
|
|
449
453
|
replyMessageId,
|
|
450
454
|
getTelegramQueueMenuItemText(item),
|
|
451
|
-
|
|
455
|
+
item.dispatchProtected || item.attemptFrozen
|
|
456
|
+
? buildTelegramQueueItemSubmenuReplyMarkup(item)
|
|
457
|
+
: buildTelegramGuestQueueItemSubmenuReplyMarkup(queueOrder),
|
|
452
458
|
);
|
|
453
459
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
454
460
|
}
|
|
@@ -474,12 +480,7 @@ async function handleTelegramQueueMenuPick<Context>(
|
|
|
474
480
|
replyChatId,
|
|
475
481
|
replyMessageId,
|
|
476
482
|
getTelegramQueueMenuItemText(item),
|
|
477
|
-
buildTelegramQueueItemSubmenuReplyMarkup(
|
|
478
|
-
chatId,
|
|
479
|
-
msgId,
|
|
480
|
-
item.isPriority,
|
|
481
|
-
item.reactionSuppressionEmoji !== undefined,
|
|
482
|
-
),
|
|
483
|
+
buildTelegramQueueItemSubmenuReplyMarkup(item),
|
|
483
484
|
);
|
|
484
485
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
485
486
|
}
|
|
@@ -502,25 +503,25 @@ async function handleTelegramQueueMenuPriority<Context>(
|
|
|
502
503
|
deps,
|
|
503
504
|
);
|
|
504
505
|
}
|
|
505
|
-
await
|
|
506
|
+
await updateTelegramQueueMenuLane(
|
|
506
507
|
callbackQueryId,
|
|
507
508
|
replyChatId,
|
|
508
509
|
replyMessageId,
|
|
509
510
|
chatId,
|
|
510
511
|
msgId,
|
|
511
|
-
|
|
512
|
+
item.queueLane === "priority" ? "default" : "priority",
|
|
512
513
|
ctx,
|
|
513
514
|
deps,
|
|
514
515
|
);
|
|
515
516
|
}
|
|
516
517
|
|
|
517
|
-
async function
|
|
518
|
+
async function handleTelegramQueueMenuLaneSet<Context>(
|
|
518
519
|
callbackQueryId: string,
|
|
519
520
|
replyChatId: number,
|
|
520
521
|
replyMessageId: number,
|
|
521
522
|
chatId: number,
|
|
522
523
|
msgId: number,
|
|
523
|
-
|
|
524
|
+
lane: Exclude<Queue.TelegramQueueLane, "control">,
|
|
524
525
|
ctx: Context,
|
|
525
526
|
deps: TelegramQueueMenuCallbackDeps<Context>,
|
|
526
527
|
): Promise<void> {
|
|
@@ -533,29 +534,32 @@ async function handleTelegramQueueMenuPrioritySet<Context>(
|
|
|
533
534
|
deps,
|
|
534
535
|
);
|
|
535
536
|
}
|
|
536
|
-
await
|
|
537
|
+
await updateTelegramQueueMenuLane(
|
|
537
538
|
callbackQueryId,
|
|
538
539
|
replyChatId,
|
|
539
540
|
replyMessageId,
|
|
540
541
|
chatId,
|
|
541
542
|
msgId,
|
|
542
|
-
|
|
543
|
+
lane,
|
|
543
544
|
ctx,
|
|
544
545
|
deps,
|
|
545
546
|
);
|
|
546
547
|
}
|
|
547
548
|
|
|
548
|
-
async function
|
|
549
|
+
async function updateTelegramQueueMenuLane<Context>(
|
|
549
550
|
callbackQueryId: string,
|
|
550
551
|
replyChatId: number,
|
|
551
552
|
replyMessageId: number,
|
|
552
553
|
chatId: number,
|
|
553
554
|
msgId: number,
|
|
554
|
-
|
|
555
|
+
lane: Exclude<Queue.TelegramQueueLane, "control">,
|
|
555
556
|
ctx: Context,
|
|
556
557
|
deps: TelegramQueueMenuCallbackDeps<Context>,
|
|
557
558
|
): Promise<void> {
|
|
558
|
-
deps.
|
|
559
|
+
if (!deps.setLane(chatId, msgId, lane)) {
|
|
560
|
+
await deps.answerCallbackQuery(callbackQueryId, "Queue item is protected");
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
559
563
|
deps.updateStatus(ctx);
|
|
560
564
|
const updated = deps.findItem(chatId, msgId);
|
|
561
565
|
if (!updated) {
|
|
@@ -570,16 +574,11 @@ async function updateTelegramQueueMenuPriority<Context>(
|
|
|
570
574
|
replyChatId,
|
|
571
575
|
replyMessageId,
|
|
572
576
|
getTelegramQueueMenuItemText(updated),
|
|
573
|
-
buildTelegramQueueItemSubmenuReplyMarkup(
|
|
574
|
-
chatId,
|
|
575
|
-
msgId,
|
|
576
|
-
updated.isPriority,
|
|
577
|
-
updated.reactionSuppressionEmoji !== undefined,
|
|
578
|
-
),
|
|
577
|
+
buildTelegramQueueItemSubmenuReplyMarkup(updated),
|
|
579
578
|
);
|
|
580
579
|
await deps.answerCallbackQuery(
|
|
581
580
|
callbackQueryId,
|
|
582
|
-
updated.
|
|
581
|
+
updated.queueLane === "steer" ? "Steer selected" : updated.queueLane === "priority" ? "Prioritized" : "Normal priority",
|
|
583
582
|
);
|
|
584
583
|
}
|
|
585
584
|
|
|
@@ -602,7 +601,10 @@ async function handleTelegramQueueMenuSkipSet<Context>(
|
|
|
602
601
|
deps,
|
|
603
602
|
);
|
|
604
603
|
}
|
|
605
|
-
deps.setSkipped(chatId, msgId, skipped)
|
|
604
|
+
if (!deps.setSkipped(chatId, msgId, skipped)) {
|
|
605
|
+
await deps.answerCallbackQuery(callbackQueryId, "Queue item is protected");
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
606
608
|
deps.updateStatus(ctx);
|
|
607
609
|
const updated = deps.findItem(chatId, msgId);
|
|
608
610
|
if (!updated) {
|
|
@@ -617,12 +619,7 @@ async function handleTelegramQueueMenuSkipSet<Context>(
|
|
|
617
619
|
replyChatId,
|
|
618
620
|
replyMessageId,
|
|
619
621
|
getTelegramQueueMenuItemText(updated),
|
|
620
|
-
buildTelegramQueueItemSubmenuReplyMarkup(
|
|
621
|
-
chatId,
|
|
622
|
-
msgId,
|
|
623
|
-
updated.isPriority,
|
|
624
|
-
updated.reactionSuppressionEmoji !== undefined,
|
|
625
|
-
),
|
|
622
|
+
buildTelegramQueueItemSubmenuReplyMarkup(updated),
|
|
626
623
|
);
|
|
627
624
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
628
625
|
}
|
|
@@ -649,6 +646,7 @@ export function createTelegramQueueMenuRuntime<
|
|
|
649
646
|
Context,
|
|
650
647
|
TModel extends MenuModel = MenuModel,
|
|
651
648
|
>(deps: {
|
|
649
|
+
hasPendingDispatch?: () => boolean;
|
|
652
650
|
telegramQueueStore: Queue.TelegramQueueStateStore<Context>;
|
|
653
651
|
queueMutationRuntime: Queue.TelegramQueueMutationController<Context>;
|
|
654
652
|
sendInteractiveMessage: TelegramMenuMessageRuntimeDeps["sendInteractiveMessage"];
|
|
@@ -695,6 +693,7 @@ export function createTelegramQueueMenuRuntime<
|
|
|
695
693
|
);
|
|
696
694
|
return createOpenQueueMenu<Context, TModel>({
|
|
697
695
|
getQueuedItems: deps.telegramQueueStore.getQueuedItems,
|
|
696
|
+
hasPendingDispatch: deps.hasPendingDispatch,
|
|
698
697
|
getModelMenuState: (chat, context) =>
|
|
699
698
|
deps.getModelMenuState(chat, context, threadId),
|
|
700
699
|
storeModelMenuState: delivery.storeModelMenuState,
|
|
@@ -704,6 +703,7 @@ export function createTelegramQueueMenuRuntime<
|
|
|
704
703
|
})(chatId, replyToMessageId, ctx);
|
|
705
704
|
},
|
|
706
705
|
handleCallbackQuery: createQueueMenuCallbackHandler<Context, TModel>({
|
|
706
|
+
hasPendingDispatch: deps.hasPendingDispatch,
|
|
707
707
|
telegramQueueStore: deps.telegramQueueStore,
|
|
708
708
|
queueMutationRuntime: deps.queueMutationRuntime,
|
|
709
709
|
editInteractiveMessage: editQueueMenuMessage,
|
|
@@ -721,6 +721,7 @@ function createOpenQueueMenu<
|
|
|
721
721
|
TModel extends MenuModel = MenuModel,
|
|
722
722
|
>(deps: {
|
|
723
723
|
getQueuedItems: () => Queue.TelegramQueueItem<Context>[];
|
|
724
|
+
hasPendingDispatch?: () => boolean;
|
|
724
725
|
getModelMenuState: (
|
|
725
726
|
chatId: number,
|
|
726
727
|
ctx: Context,
|
|
@@ -739,7 +740,7 @@ function createOpenQueueMenu<
|
|
|
739
740
|
ctx: Context,
|
|
740
741
|
): Promise<void> => {
|
|
741
742
|
const state = await deps.getModelMenuState(chatId, ctx);
|
|
742
|
-
const menuItems = toTelegramQueueMenuItems(deps.getQueuedItems());
|
|
743
|
+
const menuItems = toTelegramQueueMenuItems(deps.getQueuedItems(), deps.hasPendingDispatch?.());
|
|
743
744
|
const text = getTelegramQueueMenuListText(menuItems);
|
|
744
745
|
const messageId = await deps.sendInteractiveMessage(
|
|
745
746
|
chatId,
|
|
@@ -758,6 +759,7 @@ function createQueueMenuCallbackHandler<
|
|
|
758
759
|
Context,
|
|
759
760
|
TModel extends MenuModel = MenuModel,
|
|
760
761
|
>(deps: {
|
|
762
|
+
hasPendingDispatch?: () => boolean;
|
|
761
763
|
telegramQueueStore: Queue.TelegramQueueStateStore<Context>;
|
|
762
764
|
queueMutationRuntime: Queue.TelegramQueueMutationController<Context>;
|
|
763
765
|
editInteractiveMessage: (
|
|
@@ -797,7 +799,7 @@ function createQueueMenuCallbackHandler<
|
|
|
797
799
|
return true;
|
|
798
800
|
}
|
|
799
801
|
const menuItems = toTelegramQueueMenuItems(
|
|
800
|
-
deps.telegramQueueStore.getQueuedItems(),
|
|
802
|
+
deps.telegramQueueStore.getQueuedItems(), deps.hasPendingDispatch?.(),
|
|
801
803
|
);
|
|
802
804
|
await deps.editInteractiveMessage(
|
|
803
805
|
chatId,
|
|
@@ -814,7 +816,7 @@ function createQueueMenuCallbackHandler<
|
|
|
814
816
|
return deps.telegramQueueStore.getQueuedItems();
|
|
815
817
|
};
|
|
816
818
|
const toMenuItems = () => {
|
|
817
|
-
return toTelegramQueueMenuItems(getQueueSnapshot());
|
|
819
|
+
return toTelegramQueueMenuItems(getQueueSnapshot(), deps.hasPendingDispatch?.());
|
|
818
820
|
};
|
|
819
821
|
const findItem = (cId: number, rId: number) => {
|
|
820
822
|
return findTelegramQueueItem(toMenuItems(), cId, rId);
|
|
@@ -827,6 +829,7 @@ function createQueueMenuCallbackHandler<
|
|
|
827
829
|
const flagDeps = {
|
|
828
830
|
getQueueSnapshot,
|
|
829
831
|
queueMutationRuntime: deps.queueMutationRuntime,
|
|
832
|
+
hasPendingDispatch: deps.hasPendingDispatch,
|
|
830
833
|
};
|
|
831
834
|
return handleTelegramQueueMenuCallback(
|
|
832
835
|
query.id,
|
|
@@ -845,34 +848,10 @@ function createQueueMenuCallbackHandler<
|
|
|
845
848
|
dismissGuestPlaceholder: deps.dismissGuestPlaceholder,
|
|
846
849
|
});
|
|
847
850
|
},
|
|
848
|
-
|
|
849
|
-
applyQueuedTelegramPromptMenuFlags(
|
|
850
|
-
cId,
|
|
851
|
-
rId,
|
|
852
|
-
ctx,
|
|
853
|
-
flagDeps,
|
|
854
|
-
(item) => ({
|
|
855
|
-
priority: item.queueLane !== "priority",
|
|
856
|
-
skipped: isQueuedTelegramPromptSkipped(item),
|
|
857
|
-
}),
|
|
858
|
-
),
|
|
859
|
-
setPriority: (cId, rId, enabled) =>
|
|
860
|
-
applyQueuedTelegramPromptMenuFlags(
|
|
861
|
-
cId,
|
|
862
|
-
rId,
|
|
863
|
-
ctx,
|
|
864
|
-
flagDeps,
|
|
865
|
-
(item) => ({
|
|
866
|
-
priority: enabled,
|
|
867
|
-
skipped: isQueuedTelegramPromptSkipped(item),
|
|
868
|
-
}),
|
|
869
|
-
),
|
|
851
|
+
setLane: (cId, rId, lane) =>
|
|
852
|
+
applyQueuedTelegramPromptMenuFlags(cId, rId, ctx, flagDeps, () => ({ lane })),
|
|
870
853
|
setSkipped: (cId, rId, skipped) =>
|
|
871
|
-
applyQueuedTelegramPromptMenuFlags(cId, rId, ctx, flagDeps, (
|
|
872
|
-
item.kind === "prompt"
|
|
873
|
-
? { priority: item.queueLane === "priority", skipped }
|
|
874
|
-
: undefined,
|
|
875
|
-
),
|
|
854
|
+
applyQueuedTelegramPromptMenuFlags(cId, rId, ctx, flagDeps, () => ({ skipped })),
|
|
876
855
|
updateQueueMessage: deps.editInteractiveMessage,
|
|
877
856
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
878
857
|
updateStatus: deps.updateStatus,
|
|
@@ -911,43 +890,12 @@ async function skipQueuedTelegramGuestPrompt<Context>(
|
|
|
911
890
|
return true;
|
|
912
891
|
}
|
|
913
892
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
skipped: boolean,
|
|
918
|
-
): Queue.TelegramQueueReactionDisposition {
|
|
919
|
-
if (priority && skipped) {
|
|
920
|
-
return {
|
|
921
|
-
kind: "priority-suppressed",
|
|
922
|
-
priorityEmoji:
|
|
923
|
-
item.kind === "prompt" ? (item.priorityEmoji ?? "⚡") : "⚡",
|
|
924
|
-
suppressionEmoji:
|
|
925
|
-
item.kind === "prompt" ? (item.reactionSuppressionEmoji ?? "👎") : "👎",
|
|
926
|
-
};
|
|
927
|
-
}
|
|
928
|
-
if (priority) {
|
|
929
|
-
return {
|
|
930
|
-
kind: "priority",
|
|
931
|
-
emoji: item.kind === "prompt" ? (item.priorityEmoji ?? "⚡") : "⚡",
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
if (skipped) {
|
|
935
|
-
return {
|
|
936
|
-
kind: "suppressed",
|
|
937
|
-
emoji:
|
|
938
|
-
item.kind === "prompt" ? (item.reactionSuppressionEmoji ?? "👎") : "👎",
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
return { kind: "default" };
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
function isQueuedTelegramPromptSkipped<Context>(
|
|
945
|
-
item: Queue.TelegramQueueItem<Context>,
|
|
946
|
-
): boolean {
|
|
947
|
-
return item.kind === "prompt" && item.reactionSuppressionEmoji !== undefined;
|
|
893
|
+
interface TelegramQueueMenuFlags {
|
|
894
|
+
lane?: Exclude<Queue.TelegramQueueLane, "control">;
|
|
895
|
+
skipped?: boolean;
|
|
948
896
|
}
|
|
949
897
|
|
|
950
|
-
/**
|
|
898
|
+
/** Each menu selection changes one dimension through the existing Queue owner. */
|
|
951
899
|
function applyQueuedTelegramPromptMenuFlags<Context>(
|
|
952
900
|
chatId: number,
|
|
953
901
|
replyToMessageId: number,
|
|
@@ -955,10 +903,9 @@ function applyQueuedTelegramPromptMenuFlags<Context>(
|
|
|
955
903
|
deps: {
|
|
956
904
|
getQueueSnapshot: () => Queue.TelegramQueueItem<Context>[];
|
|
957
905
|
queueMutationRuntime: Queue.TelegramQueueMutationController<Context>;
|
|
906
|
+
hasPendingDispatch?: () => boolean;
|
|
958
907
|
},
|
|
959
|
-
chooseFlags: (
|
|
960
|
-
item: Queue.TelegramQueueItem<Context>,
|
|
961
|
-
) => { priority: boolean; skipped: boolean } | undefined,
|
|
908
|
+
chooseFlags: (item: Queue.TelegramQueueItem<Context>) => TelegramQueueMenuFlags | undefined,
|
|
962
909
|
): boolean {
|
|
963
910
|
const item = findTelegramQueueItem(
|
|
964
911
|
deps.getQueueSnapshot(),
|
|
@@ -966,13 +913,18 @@ function applyQueuedTelegramPromptMenuFlags<Context>(
|
|
|
966
913
|
replyToMessageId,
|
|
967
914
|
);
|
|
968
915
|
const flags = item ? chooseFlags(item) : undefined;
|
|
969
|
-
if (!item || !flags) return false;
|
|
970
|
-
deps.
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
916
|
+
if (!item || item.kind !== "prompt" || !flags) return false;
|
|
917
|
+
const view = findTelegramQueueItem(toTelegramQueueMenuItems(deps.getQueueSnapshot(), deps.hasPendingDispatch?.()), chatId, replyToMessageId);
|
|
918
|
+
if (!view || (flags.lane !== undefined && !view.canSelectLane) || (flags.skipped !== undefined && !view.canSelectDisposition)) return false;
|
|
919
|
+
const disposition: Extract<Queue.TelegramQueueReactionDisposition, { kind: "reaction-transition" }> = { kind: "reaction-transition" };
|
|
920
|
+
if (flags.lane !== undefined) {
|
|
921
|
+
disposition.promptLane = flags.lane;
|
|
922
|
+
disposition.priorityEmoji = flags.lane === "steer" ? "🔥" : flags.lane === "priority" ? "👍" : null;
|
|
923
|
+
}
|
|
924
|
+
if (flags.skipped !== undefined) disposition.suppressionEmoji = flags.skipped ? "👎" : null;
|
|
925
|
+
return deps.queueMutationRuntime.applyReactionByMessageId(replyToMessageId, disposition, ctx) ||
|
|
926
|
+
((flags.lane === undefined || flags.lane === item.queueLane) &&
|
|
927
|
+
(flags.skipped === undefined || flags.skipped === (item.reactionSuppressionEmoji !== undefined)));
|
|
976
928
|
}
|
|
977
929
|
|
|
978
930
|
function createQueueMenuSendMessageAdapter(
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
type SessionShutdownEvent,
|
|
21
21
|
type SessionStartEvent,
|
|
22
22
|
type SlashCommandInfo,
|
|
23
|
+
type TurnEndEvent,
|
|
24
|
+
type TurnEndEventResult,
|
|
23
25
|
type UIPromptEndEvent,
|
|
24
26
|
type UIPromptStartEvent,
|
|
25
27
|
SettingsManager,
|
|
@@ -41,6 +43,8 @@ export type {
|
|
|
41
43
|
SessionShutdownEvent,
|
|
42
44
|
SessionStartEvent,
|
|
43
45
|
SlashCommandInfo,
|
|
46
|
+
TurnEndEvent,
|
|
47
|
+
TurnEndEventResult,
|
|
44
48
|
UIPromptEndEvent,
|
|
45
49
|
UIPromptStartEvent,
|
|
46
50
|
};
|
|
@@ -294,6 +298,10 @@ export function isExtensionContextIdle(ctx: ExtensionContext): boolean {
|
|
|
294
298
|
return ctx.isIdle();
|
|
295
299
|
}
|
|
296
300
|
|
|
301
|
+
export function isExtensionContextAbortRequested(ctx: ExtensionContext): boolean {
|
|
302
|
+
return ctx.signal?.aborted === true;
|
|
303
|
+
}
|
|
304
|
+
|
|
297
305
|
export function hasExtensionContextPendingMessages(
|
|
298
306
|
ctx: ExtensionContext,
|
|
299
307
|
): boolean {
|
|
@@ -757,6 +757,7 @@ export interface TelegramThreadAwarePollingPorts<TContext, TOwner> {
|
|
|
757
757
|
registerFollowerWithOwner: (
|
|
758
758
|
ctx: TContext,
|
|
759
759
|
owner: TOwner,
|
|
760
|
+
options?: { recoverConnection?: boolean },
|
|
760
761
|
) => Promise<boolean | undefined>;
|
|
761
762
|
restoreFollowerWithOwner: (
|
|
762
763
|
ctx: TContext,
|
|
@@ -776,6 +777,7 @@ export interface TelegramThreadAwarePollingControls<TContext, TOwner> {
|
|
|
776
777
|
registerFollowerWithLeader: (
|
|
777
778
|
ctx: TContext,
|
|
778
779
|
owner: TOwner,
|
|
780
|
+
options?: { recoverConnection?: boolean },
|
|
779
781
|
) => Promise<boolean | undefined>;
|
|
780
782
|
restoreFollowerWithLeader?: (
|
|
781
783
|
ctx: TContext,
|
|
@@ -1180,9 +1182,10 @@ export function createTelegramThreadAwarePollingPorts<TContext, TOwner>(
|
|
|
1180
1182
|
const registerFollowerWithOwner = async (
|
|
1181
1183
|
ctx: TContext,
|
|
1182
1184
|
owner: TOwner,
|
|
1185
|
+
options?: { recoverConnection?: boolean },
|
|
1183
1186
|
): Promise<boolean | undefined> => {
|
|
1184
1187
|
if (!(await refreshFollowerState())) return undefined;
|
|
1185
|
-
return deps.registerFollowerWithLeader(ctx, owner);
|
|
1188
|
+
return deps.registerFollowerWithLeader(ctx, owner, options);
|
|
1186
1189
|
};
|
|
1187
1190
|
const restoreFollowerWithOwner = async (
|
|
1188
1191
|
ctx: TContext,
|
|
@@ -1303,7 +1306,9 @@ export function createTelegramThreadCapabilityMonitor<TContext>(
|
|
|
1303
1306
|
if (threadModeEnabled) consecutiveDisabledProbes = 0;
|
|
1304
1307
|
const botState = deps.topicTargetStore.getBotState();
|
|
1305
1308
|
const current = botState.threadMode;
|
|
1306
|
-
|
|
1309
|
+
// Persisted capability is not proof that this owner started its local bus.
|
|
1310
|
+
if (threadModeEnabled && current === "enabled" &&
|
|
1311
|
+
(deps.getPollingStartedWithTelegramBus() || !deps.ownsLock(ctx))) return;
|
|
1307
1312
|
if (!threadModeEnabled && current === "disabled") {
|
|
1308
1313
|
if (
|
|
1309
1314
|
!deps.ownsLock(ctx) ||
|