@llblab/pi-telegram 0.51.6 → 0.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +15 -12
- package/BACKLOG.md +16 -9
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +17 -6
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +5 -7
- package/dist/lib/bindings.d.ts +5 -1
- package/dist/lib/bindings.js +7 -1
- package/dist/lib/bus-api.js +8 -5
- package/dist/lib/bus-follower.d.ts +54 -56
- package/dist/lib/bus-follower.js +206 -139
- package/dist/lib/bus-leader.d.ts +21 -2
- package/dist/lib/bus-leader.js +181 -25
- package/dist/lib/bus-transport.d.ts +4 -1
- package/dist/lib/bus-transport.js +28 -4
- package/dist/lib/bus.d.ts +52 -56
- package/dist/lib/bus.js +78 -246
- package/dist/lib/channel-posts.d.ts +1 -3
- package/dist/lib/channel-posts.js +23 -26
- package/dist/lib/command-templates.d.ts +0 -5
- package/dist/lib/command-templates.js +5 -5
- package/dist/lib/commands.d.ts +0 -23
- package/dist/lib/commands.js +15 -15
- package/dist/lib/config.d.ts +0 -17
- package/dist/lib/config.js +12 -14
- package/dist/lib/delivery.d.ts +0 -4
- package/dist/lib/delivery.js +2 -5
- package/dist/lib/extension.js +258 -61
- package/dist/lib/generative-apps.d.ts +0 -19
- package/dist/lib/inbound.d.ts +0 -2
- package/dist/lib/inbound.js +2 -2
- package/dist/lib/journal.d.ts +183 -16
- package/dist/lib/journal.js +1175 -248
- package/dist/lib/keyboard.d.ts +0 -2
- package/dist/lib/keyboard.js +2 -2
- package/dist/lib/lifecycle.js +1 -1
- package/dist/lib/locks.d.ts +100 -12
- package/dist/lib/locks.js +380 -14
- package/dist/lib/logging.d.ts +30 -12
- package/dist/lib/logging.js +129 -72
- package/dist/lib/media.d.ts +28 -5
- package/dist/lib/media.js +26 -8
- package/dist/lib/menu-model.d.ts +0 -11
- package/dist/lib/menu-model.js +8 -8
- package/dist/lib/menu-settings.d.ts +0 -18
- package/dist/lib/menu-settings.js +18 -18
- package/dist/lib/menu-status.d.ts +4 -4
- package/dist/lib/menu-status.js +12 -7
- package/dist/lib/menu-thinking.d.ts +1 -1
- package/dist/lib/menu-thinking.js +1 -1
- package/dist/lib/menu.d.ts +1 -0
- package/dist/lib/menu.js +4 -3
- package/dist/lib/outbound-attachments.d.ts +0 -1
- package/dist/lib/outbound-attachments.js +1 -1
- package/dist/lib/outbound-buttons.js +1 -8
- package/dist/lib/outbound-markup.d.ts +1 -6
- package/dist/lib/outbound-markup.js +4 -21
- package/dist/lib/outbound.d.ts +0 -8
- package/dist/lib/outbound.js +5 -5
- package/dist/lib/paths.d.ts +50 -7
- package/dist/lib/paths.js +155 -17
- package/dist/lib/pi.d.ts +1 -0
- package/dist/lib/pi.js +2 -1
- package/dist/lib/polling.d.ts +0 -5
- package/dist/lib/polling.js +5 -5
- package/dist/lib/preview.d.ts +0 -3
- package/dist/lib/preview.js +3 -3
- package/dist/lib/prompt-templates.d.ts +0 -1
- package/dist/lib/prompt-templates.js +1 -1
- package/dist/lib/queue.d.ts +5 -15
- package/dist/lib/queue.js +94 -11
- package/dist/lib/recovery.d.ts +29 -9
- package/dist/lib/recovery.js +104 -33
- package/dist/lib/replies.d.ts +0 -4
- package/dist/lib/replies.js +1 -1
- package/dist/lib/routing.d.ts +74 -8
- package/dist/lib/routing.js +2280 -307
- package/dist/lib/sections.d.ts +0 -2
- package/dist/lib/sections.js +1 -1
- package/dist/lib/status.d.ts +44 -9
- package/dist/lib/status.js +149 -27
- package/dist/lib/sync.d.ts +0 -5
- package/dist/lib/sync.js +5 -4
- package/dist/lib/telegram-api.d.ts +13 -3
- package/dist/lib/telegram-api.js +38 -23
- package/dist/lib/thread-cleanup-manager.d.ts +1 -0
- package/dist/lib/thread-cleanup-manager.js +18 -21
- package/dist/lib/thread-naming.d.ts +32 -5
- package/dist/lib/thread-naming.js +191 -7
- package/dist/lib/thread-reconciler.d.ts +2 -0
- package/dist/lib/thread-reconciler.js +1 -1
- package/dist/lib/threads.d.ts +293 -59
- package/dist/lib/threads.js +1689 -369
- package/dist/lib/turns.d.ts +0 -1
- package/dist/lib/turns.js +1 -1
- package/dist/lib/updates.d.ts +191 -28
- package/dist/lib/updates.js +1048 -114
- package/dist/lib/wire.d.ts +12 -0
- package/dist/lib/wire.js +21 -0
- package/dist/lib/workspace-admission.d.ts +25 -17
- package/dist/lib/workspace-admission.js +95 -20
- package/dist/lib/workspace-identity.d.ts +20 -0
- package/dist/lib/workspace-identity.js +103 -0
- package/dist/lib/workspace-retirement.d.ts +43 -8
- package/dist/lib/workspace-retirement.js +148 -52
- package/dist/package.json +6 -6
- package/dist/skills/generated-control-surface/SKILL.md +3 -3
- package/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/docs/README.md +1 -1
- package/docs/activity.md +1 -1
- package/docs/architecture.md +156 -36
- package/docs/callback-namespaces.md +1 -1
- package/docs/delivery.md +1 -1
- package/docs/generative-apps.md +2 -2
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +125 -19
- package/docs/public-api.md +3 -1
- package/docs/ui-style.md +23 -9
- package/docs/updates.md +1 -1
- package/lib/activity-verbosity.ts +5 -9
- package/lib/bindings.ts +7 -0
- package/lib/bus-api.ts +9 -4
- package/lib/bus-follower.ts +243 -224
- package/lib/bus-leader.ts +178 -31
- package/lib/bus-transport.ts +30 -8
- package/lib/bus.ts +110 -334
- package/lib/channel-posts.ts +29 -29
- package/lib/command-templates.ts +5 -5
- package/lib/commands.ts +15 -15
- package/lib/config.ts +12 -17
- package/lib/delivery.ts +2 -8
- package/lib/extension.ts +261 -72
- package/lib/generative-apps.ts +0 -22
- package/lib/inbound.ts +2 -2
- package/lib/journal.ts +1250 -325
- package/lib/keyboard.ts +2 -2
- package/lib/lifecycle.ts +1 -1
- package/lib/locks.ts +402 -23
- package/lib/logging.ts +151 -101
- package/lib/media.ts +51 -9
- package/lib/menu-model.ts +8 -8
- package/lib/menu-settings.ts +18 -18
- package/lib/menu-status.ts +11 -0
- package/lib/menu-thinking.ts +2 -2
- package/lib/menu.ts +5 -1
- package/lib/outbound-attachments.ts +1 -1
- package/lib/outbound-buttons.ts +1 -10
- package/lib/outbound-markup.ts +5 -24
- package/lib/outbound.ts +5 -5
- package/lib/paths.ts +177 -17
- package/lib/pi.ts +5 -2
- package/lib/polling.ts +5 -5
- package/lib/preview.ts +3 -3
- package/lib/prompt-templates.ts +1 -1
- package/lib/queue.ts +93 -12
- package/lib/recovery.ts +104 -44
- package/lib/replies.ts +1 -1
- package/lib/routing.ts +2010 -375
- package/lib/sections.ts +1 -1
- package/lib/status.ts +156 -36
- package/lib/sync.ts +4 -4
- package/lib/telegram-api.ts +53 -33
- package/lib/thread-cleanup-manager.ts +24 -21
- package/lib/thread-naming.ts +267 -9
- package/lib/thread-reconciler.ts +3 -1
- package/lib/threads.ts +1736 -488
- package/lib/turns.ts +1 -1
- package/lib/updates.ts +1094 -151
- package/lib/wire.ts +28 -0
- package/lib/workspace-admission.ts +97 -47
- package/lib/workspace-identity.ts +147 -0
- package/lib/workspace-retirement.ts +188 -89
- package/package.json +6 -6
- package/scripts/audit-exports.mjs +100 -0
- package/scripts/check-downgrade.mjs +80 -43
- package/skills/generated-control-surface/SKILL.md +3 -3
- package/skills/telegram-bridge/references/diagnosis.md +3 -3
package/dist/lib/queue.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { isVoiceTurn } from "./voice.js";
|
|
8
|
+
import { isWireRecord as isQueuePayloadRecord } from "./wire.js";
|
|
8
9
|
import { isTelegramApiCommitUnknownError } from "./telegram-api.js";
|
|
9
10
|
export const TELEGRAM_QUEUE_LANE_CONTRACTS = [
|
|
10
11
|
// Control lane intentionally accepts both direct controls and resume prompts.
|
|
@@ -30,7 +31,67 @@ export const TELEGRAM_QUEUE_LANE_CONTRACTS = [
|
|
|
30
31
|
},
|
|
31
32
|
];
|
|
32
33
|
export const TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
33
|
-
|
|
34
|
+
const TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS = 256;
|
|
35
|
+
function parseQueuePayloadReceipt(value, kind) {
|
|
36
|
+
if (!isQueuePayloadRecord(value) || value.queueKind !== kind ||
|
|
37
|
+
typeof value.receiptId !== "string" || !value.receiptId ||
|
|
38
|
+
(value.journalBindingKey !== undefined && (typeof value.journalBindingKey !== "string" || !value.journalBindingKey.trim())) ||
|
|
39
|
+
!Array.isArray(value.sourceUpdateIds) || value.sourceUpdateIds.length === 0 ||
|
|
40
|
+
value.sourceUpdateIds.some((id, index) => !Number.isSafeInteger(id) || id < 0 ||
|
|
41
|
+
(index > 0 && id <= value.sourceUpdateIds[index - 1])))
|
|
42
|
+
return undefined;
|
|
43
|
+
return { queueKind: kind, receiptId: value.receiptId, sourceUpdateIds: value.sourceUpdateIds,
|
|
44
|
+
...(typeof value.journalBindingKey === "string" ? { journalBindingKey: value.journalBindingKey } : {}) };
|
|
45
|
+
}
|
|
46
|
+
/** Data decoder shared by authenticated IPC and cold storage; decoding grants no admission. */
|
|
47
|
+
export function parseTelegramQueueHandoffPayload(value) {
|
|
48
|
+
if (!isQueuePayloadRecord(value) || (value.kind !== "prompt" && value.kind !== "control"))
|
|
49
|
+
return undefined;
|
|
50
|
+
const target = isQueuePayloadRecord(value.target) && typeof value.target.chatId === "number"
|
|
51
|
+
? { chatId: value.target.chatId, ...(typeof value.target.threadId === "number" ? { threadId: value.target.threadId } : {}) } : undefined;
|
|
52
|
+
const transportStamp = isQueuePayloadRecord(value.transportStamp) &&
|
|
53
|
+
typeof value.transportStamp.profile === "string" && typeof value.transportStamp.generation === "string"
|
|
54
|
+
? { profile: value.transportStamp.profile, generation: value.transportStamp.generation } : undefined;
|
|
55
|
+
if (!Number.isSafeInteger(value.chatId) || (value.target !== undefined && !target) ||
|
|
56
|
+
(value.transportStamp !== undefined && !transportStamp) || !Number.isSafeInteger(value.replyToMessageId) ||
|
|
57
|
+
(value.guestQueryId !== undefined && typeof value.guestQueryId !== "string") ||
|
|
58
|
+
(value.guestInlineMessageId !== undefined && typeof value.guestInlineMessageId !== "string") ||
|
|
59
|
+
!Number.isSafeInteger(value.queueOrder) || (value.queueLane !== "control" && value.queueLane !== "priority" && value.queueLane !== "default") ||
|
|
60
|
+
!Number.isSafeInteger(value.laneOrder) || typeof value.statusSummary !== "string" || !Array.isArray(value.admissionReceipts))
|
|
61
|
+
return undefined;
|
|
62
|
+
const admissionReceipts = value.admissionReceipts.map(receipt => parseQueuePayloadReceipt(receipt, value.kind));
|
|
63
|
+
if (admissionReceipts.length === 0 || admissionReceipts.length > TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS ||
|
|
64
|
+
admissionReceipts.some(receipt => !receipt))
|
|
65
|
+
return undefined;
|
|
66
|
+
const base = { chatId: value.chatId, ...(target ? { target } : {}), ...(transportStamp ? { transportStamp } : {}),
|
|
67
|
+
replyToMessageId: value.replyToMessageId,
|
|
68
|
+
...(typeof value.guestQueryId === "string" ? { guestQueryId: value.guestQueryId } : {}),
|
|
69
|
+
...(typeof value.guestInlineMessageId === "string" ? { guestInlineMessageId: value.guestInlineMessageId } : {}),
|
|
70
|
+
queueOrder: value.queueOrder, queueLane: value.queueLane,
|
|
71
|
+
laneOrder: value.laneOrder, statusSummary: value.statusSummary,
|
|
72
|
+
admissionReceipts: admissionReceipts };
|
|
73
|
+
if (value.kind === "control") {
|
|
74
|
+
return value.queueLane === "control" && (value.controlType === "status" || value.controlType === "model")
|
|
75
|
+
? { kind: "control", controlType: value.controlType, ...base } : undefined;
|
|
76
|
+
}
|
|
77
|
+
if (value.queueLane === "control" || !Array.isArray(value.sourceMessageIds) || value.sourceMessageIds.some(id => !Number.isSafeInteger(id)) ||
|
|
78
|
+
!Array.isArray(value.queuedAttachments) || value.queuedAttachments.some(attachment => !isQueuePayloadRecord(attachment) ||
|
|
79
|
+
typeof attachment.path !== "string" || typeof attachment.fileName !== "string") ||
|
|
80
|
+
!Array.isArray(value.content) || value.content.some(content => !isQueuePayloadRecord(content) ||
|
|
81
|
+
(content.type === "text" ? typeof content.text !== "string" : content.type === "image"
|
|
82
|
+
? typeof content.data !== "string" || typeof content.mimeType !== "string" : true)) ||
|
|
83
|
+
typeof value.historyText !== "string" || (value.priorityEmoji !== undefined && typeof value.priorityEmoji !== "string") ||
|
|
84
|
+
(value.reactionSuppressionEmoji !== undefined && typeof value.reactionSuppressionEmoji !== "string") ||
|
|
85
|
+
(value.voiceReplyPreferred !== undefined && typeof value.voiceReplyPreferred !== "boolean") ||
|
|
86
|
+
(value.voiceReplyRequired !== undefined && typeof value.voiceReplyRequired !== "boolean"))
|
|
87
|
+
return undefined;
|
|
88
|
+
return { kind: "prompt", ...base, sourceMessageIds: value.sourceMessageIds,
|
|
89
|
+
queuedAttachments: value.queuedAttachments, content: value.content, historyText: value.historyText,
|
|
90
|
+
...(typeof value.priorityEmoji === "string" ? { priorityEmoji: value.priorityEmoji } : {}),
|
|
91
|
+
...(typeof value.reactionSuppressionEmoji === "string" ? { reactionSuppressionEmoji: value.reactionSuppressionEmoji } : {}),
|
|
92
|
+
...(typeof value.voiceReplyPreferred === "boolean" ? { voiceReplyPreferred: value.voiceReplyPreferred } : {}),
|
|
93
|
+
...(typeof value.voiceReplyRequired === "boolean" ? { voiceReplyRequired: value.voiceReplyRequired } : {}) };
|
|
94
|
+
}
|
|
34
95
|
export function createTelegramQueueAdmissionReceipt(options) {
|
|
35
96
|
if (options.sourceUpdateIds.length === 0)
|
|
36
97
|
return undefined;
|
|
@@ -100,7 +161,7 @@ export function assertTelegramQueueItemAdmissionValid(item) {
|
|
|
100
161
|
function getTelegramQueueLaneRank(lane) {
|
|
101
162
|
return getTelegramQueueLaneContract(lane).dispatchRank;
|
|
102
163
|
}
|
|
103
|
-
|
|
164
|
+
function isPendingTelegramTurn(item) {
|
|
104
165
|
return item.kind === "prompt";
|
|
105
166
|
}
|
|
106
167
|
export function createTelegramQueueStore(initialItems = []) {
|
|
@@ -148,7 +209,7 @@ export function createTelegramTransportStampedQueueStore(store, getTransportStam
|
|
|
148
209
|
},
|
|
149
210
|
};
|
|
150
211
|
}
|
|
151
|
-
|
|
212
|
+
function isTelegramQueueItemSkipped(item) {
|
|
152
213
|
return item.kind === "prompt" && Boolean(item.reactionSuppressionEmoji);
|
|
153
214
|
}
|
|
154
215
|
export function countExecutableTelegramQueueItems(items) {
|
|
@@ -503,7 +564,7 @@ export function removeTelegramQueueItemsByMessageIds(items, messageIds, scope) {
|
|
|
503
564
|
removedCount: removedItems.length,
|
|
504
565
|
};
|
|
505
566
|
}
|
|
506
|
-
|
|
567
|
+
function removeTelegramQueuedGuestPromptByOrder(items, queueOrder) {
|
|
507
568
|
const index = items.findIndex((item) => {
|
|
508
569
|
return isPendingTelegramTurn(item) &&
|
|
509
570
|
item.guestQueryId !== undefined &&
|
|
@@ -521,7 +582,7 @@ export function removeTelegramQueuedGuestPromptByOrder(items, queueOrder) {
|
|
|
521
582
|
export function applyTelegramQueuePromptReactionDisposition(items, messageId, disposition, destinationLaneOrder, scope) {
|
|
522
583
|
let nextItems = items;
|
|
523
584
|
for (const [index, item] of items.entries()) {
|
|
524
|
-
if (!isPendingTelegramTurn(item) ||
|
|
585
|
+
if (!isPendingTelegramTurn(item) || item.queueLane === "control" ||
|
|
525
586
|
!isTelegramQueueItemInMessageScope(item, scope) ||
|
|
526
587
|
!item.sourceMessageIds.includes(messageId)) {
|
|
527
588
|
continue;
|
|
@@ -1489,7 +1550,15 @@ function updateTelegramQueueStatusRuntime(deps) {
|
|
|
1489
1550
|
}
|
|
1490
1551
|
}
|
|
1491
1552
|
function commitReorderedTelegramQueueItemsRuntime(items, deps) {
|
|
1492
|
-
deps.
|
|
1553
|
+
const pendingHead = deps.hasPendingDispatch?.() ? deps.getQueuedItems()[0] : undefined;
|
|
1554
|
+
const ordered = [...items].sort(compareTelegramQueueItems);
|
|
1555
|
+
if (pendingHead) {
|
|
1556
|
+
const index = ordered.findIndex(item => item.queueOrder === pendingHead.queueOrder && item.kind === pendingHead.kind &&
|
|
1557
|
+
item.chatId === pendingHead.chatId && item.replyToMessageId === pendingHead.replyToMessageId);
|
|
1558
|
+
if (index > 0)
|
|
1559
|
+
ordered.unshift(ordered.splice(index, 1)[0]);
|
|
1560
|
+
}
|
|
1561
|
+
deps.setQueuedItems(ordered);
|
|
1493
1562
|
updateTelegramQueueStatusRuntime(deps);
|
|
1494
1563
|
}
|
|
1495
1564
|
function appendTelegramQueueItemRuntime(item, deps) {
|
|
@@ -1499,7 +1568,7 @@ function appendTelegramQueueItemRuntime(item, deps) {
|
|
|
1499
1568
|
return;
|
|
1500
1569
|
commitReorderedTelegramQueueItemsRuntime(nextItems, deps);
|
|
1501
1570
|
}
|
|
1502
|
-
|
|
1571
|
+
function reorderTelegramQueueItemsRuntime(deps) {
|
|
1503
1572
|
commitReorderedTelegramQueueItemsRuntime(deps.getQueuedItems(), deps);
|
|
1504
1573
|
}
|
|
1505
1574
|
export function clearTelegramQueueItemsRuntime(deps) {
|
|
@@ -1528,7 +1597,7 @@ export function removeTelegramQueueItemsByMessageIdsRuntime(messageIds, deps, sc
|
|
|
1528
1597
|
updateTelegramQueueStatusRuntime(deps);
|
|
1529
1598
|
return removedCount;
|
|
1530
1599
|
}
|
|
1531
|
-
|
|
1600
|
+
function removeTelegramQueuedGuestPromptByOrderRuntime(queueOrder, deps) {
|
|
1532
1601
|
const { items, removedItems, removedCount } = removeTelegramQueuedGuestPromptByOrder(deps.getQueuedItems(), queueOrder);
|
|
1533
1602
|
if (removedCount === 0)
|
|
1534
1603
|
return false;
|
|
@@ -1541,8 +1610,22 @@ export function removeTelegramQueuedGuestPromptByOrderRuntime(queueOrder, deps)
|
|
|
1541
1610
|
}
|
|
1542
1611
|
export function applyTelegramQueuePromptReactionDispositionRuntime(messageId, disposition, deps, scope) {
|
|
1543
1612
|
const queuedItems = deps.getQueuedItems();
|
|
1613
|
+
const suppression = disposition.kind === "reaction-transition" ? disposition.suppressionEmoji :
|
|
1614
|
+
disposition.kind === "suppressed" ? disposition.emoji : disposition.kind === "priority-suppressed" ? disposition.suppressionEmoji : undefined;
|
|
1615
|
+
// Source-addressed control-lane prompts are explicit continuations. Synthetic model-switch turns have no source IDs.
|
|
1616
|
+
// A Pi-owned dispatched head cannot be removed: agent_start must consume that exact item, not its successor.
|
|
1617
|
+
const cancelled = suppression && deps.hasPendingDispatch ? queuedItems.filter((item, index) => isPendingTelegramTurn(item) && item.queueLane === "control" && isTelegramQueueItemInMessageScope(item, scope) &&
|
|
1618
|
+
item.sourceMessageIds.includes(messageId) && !(index === 0 && deps.hasPendingDispatch())) : [];
|
|
1619
|
+
if (cancelled.length) {
|
|
1620
|
+
if (cancelled.some(item => (item.admissionReceipts?.length ?? 0) > 0) && !deps.onItemsDiscarded)
|
|
1621
|
+
return false;
|
|
1622
|
+
deps.onItemsDiscarded?.(cancelled, deps.ctx);
|
|
1623
|
+
deps.setQueuedItems(queuedItems.filter(item => !cancelled.includes(item)));
|
|
1624
|
+
updateTelegramQueueStatusRuntime(deps);
|
|
1625
|
+
return true;
|
|
1626
|
+
}
|
|
1544
1627
|
const changesLane = queuedItems.some((item) => {
|
|
1545
|
-
if (!isPendingTelegramTurn(item) ||
|
|
1628
|
+
if (!isPendingTelegramTurn(item) || item.queueLane === "control" ||
|
|
1546
1629
|
!isTelegramQueueItemInMessageScope(item, scope) ||
|
|
1547
1630
|
!item.sourceMessageIds.includes(messageId)) {
|
|
1548
1631
|
return false;
|
|
@@ -1639,7 +1722,7 @@ export async function executeTelegramControlItemRuntime(item, deps) {
|
|
|
1639
1722
|
* Production debounce for deferred queue dispatch; the factory defaults to this
|
|
1640
1723
|
* so the entrypoint wires ports instead of policy constants.
|
|
1641
1724
|
*/
|
|
1642
|
-
|
|
1725
|
+
const TELEGRAM_DEFERRED_DISPATCH_DELAY_MS = 50;
|
|
1643
1726
|
export function createTelegramDeferredQueueDispatchRuntime(deps = {}) {
|
|
1644
1727
|
let boundContext;
|
|
1645
1728
|
let generation = 0;
|
|
@@ -2004,7 +2087,7 @@ export function createTelegramQueueDispatchController(deps) {
|
|
|
2004
2087
|
const announcementGeneration = nextDispatchAnnouncementGeneration;
|
|
2005
2088
|
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
2006
2089
|
deps.updateStatus(ctx);
|
|
2007
|
-
void deps.sendTextReply(dispatchPlan.item.chatId, dispatchPlan.item.replyToMessageId, "<b>⏩ Dispatching next queued turn.</b>", { target: dispatchPlan.item.target }).catch((error) => {
|
|
2090
|
+
void deps.sendTextReply(dispatchPlan.item.chatId, dispatchPlan.item.replyToMessageId, "<b>⏩ Dispatching next queued turn.</b>", { target: dispatchPlan.item.target, parseMode: "HTML" }).catch((error) => {
|
|
2008
2091
|
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
2009
2092
|
phase: "next-announcement",
|
|
2010
2093
|
});
|
package/dist/lib/recovery.d.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Zones: filesystem diagnostics, unclean-shutdown recovery
|
|
4
4
|
* Owns fail-safe classification of temporary ownership and routing artifacts
|
|
5
5
|
*/
|
|
6
|
-
import { renameSync } from "node:fs";
|
|
7
6
|
import { type TelegramFileTransactionOptions } from "./locks.ts";
|
|
8
7
|
export type TelegramRuntimeArtifactKind = "owners" | "state" | "transaction";
|
|
9
8
|
export interface TelegramRuntimeCorruptArtifact {
|
|
@@ -35,19 +34,19 @@ export type TelegramRuntimeRecoveryResult = {
|
|
|
35
34
|
} | {
|
|
36
35
|
kind: "blocked-live-owner";
|
|
37
36
|
livePids: number[];
|
|
38
|
-
|
|
37
|
+
deletedPaths?: string[];
|
|
39
38
|
} | {
|
|
40
39
|
kind: "recovered";
|
|
41
40
|
artifacts: TelegramRuntimeCorruptArtifact[];
|
|
42
|
-
|
|
41
|
+
/** Damaged disposable artifacts deleted per the approved corruption policy; nothing is quarantined. */
|
|
42
|
+
deletedPaths: string[];
|
|
43
43
|
};
|
|
44
44
|
export interface TelegramRuntimeRecoveryOptions extends TelegramRuntimeRecoveryClassificationOptions {
|
|
45
45
|
recoveryTransactionPath?: string;
|
|
46
|
-
quarantineRoot?: string;
|
|
47
46
|
pid?: number;
|
|
48
47
|
getNowMs?: () => number;
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
/** Fault-injection seam; production uses bounded-retry recursive removal. */
|
|
49
|
+
removePath?: (path: string) => void;
|
|
51
50
|
transactionOptions?: TelegramFileTransactionOptions;
|
|
52
51
|
}
|
|
53
52
|
export type TelegramPollingStartRecoveryDecision = {
|
|
@@ -74,13 +73,34 @@ export interface TelegramPollingStartRecoveryHandlerDeps {
|
|
|
74
73
|
*/
|
|
75
74
|
export declare function classifyTelegramRuntimeRecovery(options: TelegramRuntimeRecoveryClassificationOptions): TelegramRuntimeRecoveryClassification;
|
|
76
75
|
/**
|
|
77
|
-
*
|
|
76
|
+
* Delete classifier-approved disposable corruption under two guards.
|
|
78
77
|
*
|
|
79
78
|
* A dedicated recovery transaction serializes recoverers. The ownership
|
|
80
79
|
* transaction then prevents a new Telegram owner from appearing between the
|
|
81
|
-
* final classification and mutation.
|
|
82
|
-
*
|
|
80
|
+
* final classification and mutation. Damaged ownership debris and canonical
|
|
81
|
+
* state are deleted (operator policy: unfinished Restores in unreadable state
|
|
82
|
+
* are acceptable loss). Durable config and diagnostics never enter the set.
|
|
83
83
|
*/
|
|
84
84
|
export declare function recoverTelegramRuntimeState(options: TelegramRuntimeRecoveryOptions): TelegramRuntimeRecoveryResult;
|
|
85
|
+
/**
|
|
86
|
+
* Remove recovery folders written by earlier releases (runtime root and session folders).
|
|
87
|
+
* Current releases delete damaged files instead of quarantining them; nothing reads these copies.
|
|
88
|
+
*/
|
|
89
|
+
export declare function removeTelegramLegacyRecoveryStorage(runtimeDir: string): string[];
|
|
90
|
+
export interface TelegramSessionFolderSweeperDeps {
|
|
91
|
+
getSessionsDir: () => string;
|
|
92
|
+
getProfileName: () => string | undefined;
|
|
93
|
+
/** Sessions holding a Workspace slot binding, live registrations and this process's own session. */
|
|
94
|
+
getKeptSessionIds: () => Iterable<string | undefined>;
|
|
95
|
+
getNowMs?: () => number;
|
|
96
|
+
intervalMs?: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Leader housekeeping (operator policy): a session without a Workspace slot loses its
|
|
100
|
+
* current-profile journal family; the folder disappears once no profile uses it.
|
|
101
|
+
*/
|
|
102
|
+
export declare function createTelegramSessionFolderSweeper(deps: TelegramSessionFolderSweeperDeps): {
|
|
103
|
+
sweep: () => string[];
|
|
104
|
+
};
|
|
85
105
|
/** Build the `/telegram-connect` recovery boundary around runtime artifacts. */
|
|
86
106
|
export declare function createTelegramPollingStartRecoveryHandler(deps: TelegramPollingStartRecoveryHandlerDeps): () => Promise<TelegramPollingStartRecoveryDecision>;
|
package/dist/lib/recovery.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Zones: filesystem diagnostics, unclean-shutdown recovery
|
|
4
4
|
* Owns fail-safe classification of temporary ownership and routing artifacts
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, } from "node:fs";
|
|
6
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, rmdirSync, rmSync, } from "node:fs";
|
|
8
7
|
import { basename, dirname, join } from "node:path";
|
|
9
|
-
import {
|
|
8
|
+
import { decodeTelegramSessionDirectoryName, getTelegramProfilePathSuffix } from "./paths.js";
|
|
9
|
+
import { isProcessAlive as defaultIsProcessAlive, parseTelegramLockEntry, TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS, withTelegramFileTransaction, } from "./locks.js";
|
|
10
10
|
const OWNER_FILE_PATTERN = /^owner\.([A-Za-z0-9-]+)\.json$/u;
|
|
11
11
|
const RECLAIM_FILE_PATTERN = /^owner\.reclaim\.(\d+)\.([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.json$/u;
|
|
12
12
|
function corruption(kind, path, reason) {
|
|
@@ -40,6 +40,9 @@ function inspectState(path) {
|
|
|
40
40
|
if (!existsSync(path))
|
|
41
41
|
return { source: "state", ownerPids: [] };
|
|
42
42
|
try {
|
|
43
|
+
const stat = lstatSync(path);
|
|
44
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > 8 * 1024 * 1024)
|
|
45
|
+
return corruption("state", path, `${basename(path)} cannot be safely inspected`);
|
|
43
46
|
JSON.parse(readFileSync(path, "utf8"));
|
|
44
47
|
return { source: "state", ownerPids: [] };
|
|
45
48
|
}
|
|
@@ -136,19 +139,19 @@ export function classifyTelegramRuntimeRecovery(options) {
|
|
|
136
139
|
: { kind: "recoverable-corruption", artifacts };
|
|
137
140
|
}
|
|
138
141
|
/**
|
|
139
|
-
*
|
|
142
|
+
* Delete classifier-approved disposable corruption under two guards.
|
|
140
143
|
*
|
|
141
144
|
* A dedicated recovery transaction serializes recoverers. The ownership
|
|
142
145
|
* transaction then prevents a new Telegram owner from appearing between the
|
|
143
|
-
* final classification and mutation.
|
|
144
|
-
*
|
|
146
|
+
* final classification and mutation. Damaged ownership debris and canonical
|
|
147
|
+
* state are deleted (operator policy: unfinished Restores in unreadable state
|
|
148
|
+
* are acceptable loss). Durable config and diagnostics never enter the set.
|
|
145
149
|
*/
|
|
146
150
|
export function recoverTelegramRuntimeState(options) {
|
|
147
|
-
const pid = options.pid ?? process.pid;
|
|
148
151
|
const transactionPath = options.transactionPath ?? `${options.ownersPath}.transaction`;
|
|
149
152
|
const recoveryTransactionPath = options.recoveryTransactionPath ??
|
|
150
153
|
join(dirname(options.ownersPath), "runtime-recovery.transaction");
|
|
151
|
-
const
|
|
154
|
+
const removePath = options.removePath ?? ((path) => rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 }));
|
|
152
155
|
const classificationOptions = {
|
|
153
156
|
ownersPath: options.ownersPath,
|
|
154
157
|
statePaths: options.statePaths,
|
|
@@ -167,29 +170,18 @@ export function recoverTelegramRuntimeState(options) {
|
|
|
167
170
|
livePids: initial.livePids,
|
|
168
171
|
};
|
|
169
172
|
}
|
|
170
|
-
|
|
173
|
+
const deletedPaths = [];
|
|
171
174
|
const recoveredArtifacts = [];
|
|
172
|
-
const
|
|
173
|
-
if (quarantineDir)
|
|
174
|
-
return quarantineDir;
|
|
175
|
-
quarantineDir = join(quarantineRoot, `${options.getNowMs?.() ?? Date.now()}-${pid}-${randomUUID()}`);
|
|
176
|
-
mkdirSync(quarantineDir, { recursive: true, mode: 0o700 });
|
|
177
|
-
return quarantineDir;
|
|
178
|
-
};
|
|
179
|
-
const quarantineArtifact = (artifact) => {
|
|
175
|
+
const deleteArtifact = (artifact) => {
|
|
180
176
|
if (!existsSync(artifact.path))
|
|
181
177
|
return;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
retryDelayMs: options.quarantineRenameRetryDelayMs,
|
|
186
|
-
})) {
|
|
187
|
-
recoveredArtifacts.push(artifact);
|
|
188
|
-
}
|
|
178
|
+
removePath(artifact.path);
|
|
179
|
+
deletedPaths.push(artifact.path);
|
|
180
|
+
recoveredArtifacts.push(artifact);
|
|
189
181
|
};
|
|
190
182
|
for (const artifact of initial.artifacts) {
|
|
191
183
|
if (artifact.kind === "transaction")
|
|
192
|
-
|
|
184
|
+
deleteArtifact(artifact);
|
|
193
185
|
}
|
|
194
186
|
return withTelegramFileTransaction(transactionPath, () => {
|
|
195
187
|
const current = classifyTelegramRuntimeRecovery({
|
|
@@ -200,25 +192,107 @@ export function recoverTelegramRuntimeState(options) {
|
|
|
200
192
|
return {
|
|
201
193
|
kind: "blocked-live-owner",
|
|
202
194
|
livePids: current.livePids,
|
|
203
|
-
|
|
195
|
+
...(deletedPaths.length > 0 ? { deletedPaths } : {}),
|
|
204
196
|
};
|
|
205
197
|
}
|
|
206
198
|
if (current.kind === "recoverable-corruption") {
|
|
207
199
|
for (const artifact of current.artifacts) {
|
|
208
200
|
if (artifact.kind !== "transaction")
|
|
209
|
-
|
|
201
|
+
deleteArtifact(artifact);
|
|
210
202
|
}
|
|
211
203
|
}
|
|
212
|
-
return recoveredArtifacts.length > 0
|
|
204
|
+
return recoveredArtifacts.length > 0
|
|
213
205
|
? {
|
|
214
206
|
kind: "recovered",
|
|
215
207
|
artifacts: recoveredArtifacts,
|
|
216
|
-
|
|
208
|
+
deletedPaths,
|
|
217
209
|
}
|
|
218
210
|
: { kind: "not-needed" };
|
|
219
211
|
}, options.transactionOptions);
|
|
220
212
|
}, options.transactionOptions);
|
|
221
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Remove recovery folders written by earlier releases (runtime root and session folders).
|
|
216
|
+
* Current releases delete damaged files instead of quarantining them; nothing reads these copies.
|
|
217
|
+
*/
|
|
218
|
+
export function removeTelegramLegacyRecoveryStorage(runtimeDir) {
|
|
219
|
+
const removed = [];
|
|
220
|
+
const remove = (path) => {
|
|
221
|
+
try {
|
|
222
|
+
if (!lstatSync(path).isDirectory())
|
|
223
|
+
return;
|
|
224
|
+
rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
225
|
+
removed.push(path);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Best-effort housekeeping; a later startup retries.
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
remove(join(runtimeDir, "recovery"));
|
|
232
|
+
let sessions = [];
|
|
233
|
+
try {
|
|
234
|
+
sessions = readdirSync(join(runtimeDir, "sessions"));
|
|
235
|
+
}
|
|
236
|
+
catch { /* no sessions yet */ }
|
|
237
|
+
for (const session of sessions)
|
|
238
|
+
remove(join(runtimeDir, "sessions", session, "recovery"));
|
|
239
|
+
return removed;
|
|
240
|
+
}
|
|
241
|
+
const TELEGRAM_SESSION_SWEEP_INTERVAL_MS = 10 * 60 * 1000;
|
|
242
|
+
/**
|
|
243
|
+
* Leader housekeeping (operator policy): a session without a Workspace slot loses its
|
|
244
|
+
* current-profile journal family; the folder disappears once no profile uses it.
|
|
245
|
+
*/
|
|
246
|
+
export function createTelegramSessionFolderSweeper(deps) {
|
|
247
|
+
const getNowMs = deps.getNowMs ?? Date.now;
|
|
248
|
+
const intervalMs = deps.intervalMs ?? TELEGRAM_SESSION_SWEEP_INTERVAL_MS;
|
|
249
|
+
let lastSweepAtMs;
|
|
250
|
+
return {
|
|
251
|
+
sweep() {
|
|
252
|
+
const now = getNowMs();
|
|
253
|
+
if (lastSweepAtMs !== undefined && now - lastSweepAtMs < intervalMs)
|
|
254
|
+
return [];
|
|
255
|
+
lastSweepAtMs = now;
|
|
256
|
+
const sessionsDir = deps.getSessionsDir();
|
|
257
|
+
const kept = new Set(deps.getKeptSessionIds());
|
|
258
|
+
const suffix = getTelegramProfilePathSuffix(deps.getProfileName()).replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
259
|
+
const family = new RegExp(`^journal\\.[a-f0-9]{16}${suffix}\\.json(?:\\.segments|\\.retained)?$`, "u");
|
|
260
|
+
const removed = [];
|
|
261
|
+
let names;
|
|
262
|
+
try {
|
|
263
|
+
names = readdirSync(sessionsDir);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return removed;
|
|
267
|
+
}
|
|
268
|
+
for (const name of names) {
|
|
269
|
+
const sessionId = decodeTelegramSessionDirectoryName(name);
|
|
270
|
+
if (sessionId === undefined || kept.has(sessionId))
|
|
271
|
+
continue;
|
|
272
|
+
const folder = join(sessionsDir, name);
|
|
273
|
+
try {
|
|
274
|
+
if (!lstatSync(folder).isDirectory())
|
|
275
|
+
continue;
|
|
276
|
+
for (const entry of readdirSync(folder)) {
|
|
277
|
+
if (!family.test(entry))
|
|
278
|
+
continue;
|
|
279
|
+
const path = join(folder, entry);
|
|
280
|
+
rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
281
|
+
removed.push(path);
|
|
282
|
+
}
|
|
283
|
+
if (readdirSync(folder).length === 0) {
|
|
284
|
+
rmdirSync(folder);
|
|
285
|
+
removed.push(folder);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Best-effort housekeeping; the next sweep retries.
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return removed;
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
222
296
|
/** Build the `/telegram-connect` recovery boundary around runtime artifacts. */
|
|
223
297
|
export function createTelegramPollingStartRecoveryHandler(deps) {
|
|
224
298
|
return async () => {
|
|
@@ -257,10 +331,7 @@ export function createTelegramPollingStartRecoveryHandler(deps) {
|
|
|
257
331
|
});
|
|
258
332
|
}
|
|
259
333
|
try {
|
|
260
|
-
const recovery = recoverTelegramRuntimeState({
|
|
261
|
-
ownersPath,
|
|
262
|
-
statePaths,
|
|
263
|
-
});
|
|
334
|
+
const recovery = recoverTelegramRuntimeState({ ownersPath, statePaths });
|
|
264
335
|
if (recovery.kind === "blocked-live-owner") {
|
|
265
336
|
return {
|
|
266
337
|
kind: "blocked",
|
package/dist/lib/replies.d.ts
CHANGED
|
@@ -117,10 +117,6 @@ export declare function sendTelegramNativeMarkdownReply<TReplyMarkup = unknown>(
|
|
|
117
117
|
}, options?: TelegramReplyTargetOptions & {
|
|
118
118
|
replyMarkup?: TReplyMarkup;
|
|
119
119
|
}): Promise<number | undefined>;
|
|
120
|
-
export declare function sendTelegramNativeRichMessage(chatId: number, richMessage: TelegramInputRichMessage, deps: {
|
|
121
|
-
recordOwnership?: TelegramReplyOwnershipRecorder["record"];
|
|
122
|
-
sendRichMessage: (body: TelegramSendRichMessageBody) => Promise<TelegramSentMessage>;
|
|
123
|
-
}, options?: TelegramReplyTargetOptions): Promise<number>;
|
|
124
120
|
export type TelegramAssistantRenderingMode = "rich" | "html";
|
|
125
121
|
export interface TelegramRenderedMessageRuntimeDeps<TReplyMarkup> {
|
|
126
122
|
renderTelegramMessage: (text: string, options?: {
|
package/dist/lib/replies.js
CHANGED
|
@@ -498,7 +498,7 @@ export async function sendTelegramNativeMarkdownReply(chatId, replyToMessageId,
|
|
|
498
498
|
}
|
|
499
499
|
return lastMessageId;
|
|
500
500
|
}
|
|
501
|
-
|
|
501
|
+
async function sendTelegramNativeRichMessage(chatId, richMessage, deps, options) {
|
|
502
502
|
const sent = await deps.sendRichMessage({
|
|
503
503
|
chat_id: chatId,
|
|
504
504
|
rich_message: richMessage,
|
package/dist/lib/routing.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import * as Bus from "./bus.ts";
|
|
|
7
7
|
import * as Commands from "./commands.ts";
|
|
8
8
|
import type { TelegramConfigStore } from "./config.ts";
|
|
9
9
|
import type { TelegramInboundHandlerRuntime } from "./inbound.ts";
|
|
10
|
+
import type { TelegramUpdateJournalQueuedCompletion, TelegramUpdateJournalQueuedReceiptEvidence } from "./journal.ts";
|
|
10
11
|
import * as Media from "./media.ts";
|
|
11
12
|
import * as Menu from "./menu.ts";
|
|
12
13
|
import * as Model from "./model.ts";
|
|
@@ -15,11 +16,12 @@ import * as PromptTemplates from "./prompt-templates.ts";
|
|
|
15
16
|
import * as Queue from "./queue.ts";
|
|
16
17
|
import type { TelegramBridgeRuntime } from "./runtime.ts";
|
|
17
18
|
import type { TelegramSectionRegistry } from "./sections.ts";
|
|
18
|
-
import type { TelegramInputRichMessage } from "./telegram-api.ts";
|
|
19
|
+
import type { TelegramApiCallOptions, TelegramInputRichMessage } from "./telegram-api.ts";
|
|
19
20
|
import * as TextGroups from "./text-groups.ts";
|
|
20
21
|
import * as ThreadReconciler from "./thread-reconciler.ts";
|
|
21
22
|
import type { TelegramInstanceThreadIdentityCandidate, TelegramTopicTargetRecord } from "./threads.ts";
|
|
22
23
|
import * as Turns from "./turns.ts";
|
|
24
|
+
import type { createTelegramWorkspaceExternalProtectionCapture } from "./workspace-retirement.ts";
|
|
23
25
|
interface TelegramPromptPeerView {
|
|
24
26
|
id?: unknown;
|
|
25
27
|
is_bot?: unknown;
|
|
@@ -37,9 +39,37 @@ export declare function resolveTelegramGuestPromptPeer(input: {
|
|
|
37
39
|
guestBotCallerChat?: TelegramPromptPeerView;
|
|
38
40
|
ownerUserId?: number;
|
|
39
41
|
}): string | undefined;
|
|
42
|
+
/** Stable file scope of the remote Guest Mode peer: username, else numeric id; never the bot's own scope. */
|
|
43
|
+
export declare function resolveTelegramGuestFileScope(input: Parameters<typeof resolveTelegramGuestPromptPeer>[0]): string;
|
|
40
44
|
import * as Threads from "./threads.ts";
|
|
41
45
|
import * as Updates from "./updates.ts";
|
|
46
|
+
/** One admitted Restore attempt through recipient readiness, never source dispatch or cleanup. */
|
|
47
|
+
export declare function advanceTelegramWorkspaceRestore(input: {
|
|
48
|
+
request: Threads.TelegramWorkspaceRestoreRequest;
|
|
49
|
+
authority: Threads.TelegramWorkspaceRestoreAuthority;
|
|
50
|
+
restoreStore: Threads.TelegramWorkspaceRestore;
|
|
51
|
+
getRecipient: () => Threads.TelegramWorkspaceRestoreRecipient | undefined;
|
|
52
|
+
/** Adapter proves current canonical ownership and authenticates observations; apply needs a fresh issuance. */
|
|
53
|
+
runRecipient: (input: {
|
|
54
|
+
intent: Threads.TelegramWorkspaceRestoreIntent;
|
|
55
|
+
mode: "apply" | "inspect";
|
|
56
|
+
isCurrent: () => boolean;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
operationId: string;
|
|
59
|
+
recipient: Threads.TelegramWorkspaceRestoreRecipient;
|
|
60
|
+
target: Threads.TelegramWorkspaceRestoreRequest["target"];
|
|
61
|
+
slot: string;
|
|
62
|
+
ready: boolean;
|
|
63
|
+
} | undefined>;
|
|
64
|
+
/**
|
|
65
|
+
* Recovery for a same-session successor already on the relocated target: it may receive the first grant, but only
|
|
66
|
+
* read-only inspection runs. It never commits a new relocation or applies a target.
|
|
67
|
+
*/
|
|
68
|
+
inspectOnly?: true;
|
|
69
|
+
}): Promise<Threads.TelegramWorkspaceRestoreIntent | undefined>;
|
|
42
70
|
export declare const TELEGRAM_ALL_TAB_COMMAND_MAX_AGE_MS: number;
|
|
71
|
+
/** One user-facing answer for expired or previous-process routing controls. */
|
|
72
|
+
export declare const TELEGRAM_ROUTING_CHOICE_EXPIRED = "\u231B Routing choice expired.";
|
|
43
73
|
export declare function isTelegramAllTabCommandExpired(message: {
|
|
44
74
|
date?: number;
|
|
45
75
|
message_thread_id?: number;
|
|
@@ -66,7 +96,7 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
|
|
|
66
96
|
configStore: Pick<TelegramConfigStore, "get" | "getAllowedUserId" | "persistAllowedUserId" | "persist"> & {
|
|
67
97
|
set?: TelegramConfigStore["set"];
|
|
68
98
|
};
|
|
69
|
-
callApi?: <TResponse>(method: string, body: Record<string, unknown
|
|
99
|
+
callApi?: <TResponse>(method: string, body: Record<string, unknown>, options?: TelegramApiCallOptions) => Promise<TResponse>;
|
|
70
100
|
getCurrentInstanceId?: () => string | undefined;
|
|
71
101
|
getAdmissionScope?: () => string | undefined;
|
|
72
102
|
getAdmissionJournalBinding?: () => string | undefined;
|
|
@@ -87,11 +117,33 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
|
|
|
87
117
|
handleTelegramTopicLifecycleUpdate?: (lifecycle: Updates.TelegramTopicLifecycleUpdate<TMessage>, ctx: TContext) => Promise<void> | void;
|
|
88
118
|
handleTelegramThreadTargetObserved?: (target: Threads.TelegramTopicTargetRecord["target"], ctx: TContext) => Promise<void> | void;
|
|
89
119
|
foreignOwnedUpdateForwarder?: Updates.TelegramForeignOwnedUpdateForwarder<TContext, Updates.TelegramMessageReactionUpdated, TCallbackQuery, TMessage>;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
getWorkspaceRestoreStore?: () => Threads.TelegramWorkspaceRestore | undefined;
|
|
121
|
+
captureWorkspaceExternalProtection?: ReturnType<typeof createTelegramWorkspaceExternalProtectionCapture>;
|
|
122
|
+
/** Strict committed abandonment plus retained original; shared by Restore and temporary-input cancellation. */
|
|
123
|
+
inspectRestoreSourceAbandonment?: (updateId: number, journalBindingKey: string) => Threads.TelegramTemporaryThreadCancellationEvidence | undefined;
|
|
124
|
+
inspectRoutingInputGroupExpiry?: (input: Threads.TelegramTemporaryThreadInput) => readonly Threads.TelegramTemporaryThreadCancellationEvidence[] | undefined;
|
|
125
|
+
/** Strict active-journal observation only; a hint or missing source never substitutes for this ACK. */
|
|
126
|
+
inspectRestoreSourceCompletion?: (expected: Updates.TelegramDeferredSourceEvidence & {
|
|
127
|
+
completionSha256: string;
|
|
128
|
+
}) => (Updates.TelegramDeferredSourceEvidence & {
|
|
129
|
+
completionSha256: string;
|
|
130
|
+
}) | undefined;
|
|
131
|
+
inspectRestoreQueuedReceipt?: (expected: TelegramUpdateJournalQueuedCompletion & {
|
|
132
|
+
journalBindingKey: string;
|
|
133
|
+
}) => TelegramUpdateJournalQueuedReceiptEvidence | undefined;
|
|
134
|
+
hasWorkspaceRestoreAuthority?: () => boolean;
|
|
135
|
+
/** Strict complete namespace plus exact current/historical references; only an empty result clears journal protection. */
|
|
136
|
+
inspectTemporaryThreadSources?: (target: Queue.TelegramQueueTarget, requiredJournalBindingKeys: readonly string[]) => readonly number[] | undefined;
|
|
137
|
+
/** Quiet period after the last cancelled input before one cleanup attempt; defaults to 1000 ms. */
|
|
138
|
+
temporaryThreadCleanupDelayMs?: number;
|
|
139
|
+
getSessionGeneration?: () => number;
|
|
140
|
+
workspaceRestoreRecipient?: {
|
|
141
|
+
getSessionId: (ctx: TContext) => string | undefined;
|
|
142
|
+
getCwd: (ctx: TContext) => string | undefined;
|
|
143
|
+
getLeaderIdentity: Threads.TelegramLeaderThreadStateRuntime["getIdentity"];
|
|
144
|
+
followerRegistry: Pick<Bus.TelegramBusFollowerRegistry, "get" | "register">;
|
|
145
|
+
runFollower: ReturnType<typeof Bus.createTelegramBusWorkspaceRestoreController>;
|
|
146
|
+
};
|
|
95
147
|
bridgeRuntime: TelegramBridgeRuntime;
|
|
96
148
|
activeTurnRuntime: Queue.TelegramActiveTurnStore;
|
|
97
149
|
mediaGroupRuntime: Media.TelegramMediaGroupController<TMessage, TContext>;
|
|
@@ -183,7 +235,21 @@ export declare function createTelegramInboundRouteRuntime<TUpdate extends Update
|
|
|
183
235
|
message?: TMessage;
|
|
184
236
|
edited_message?: TMessage;
|
|
185
237
|
callback_query?: TCallbackQuery;
|
|
186
|
-
}, TMessage extends TelegramRoutedMessage, TCallbackQuery extends TelegramRoutedCallbackQuery, TContext, TModel extends Model.MenuModel>(deps: TelegramInboundRouteRuntimeDeps<TMessage, TCallbackQuery, TContext, TModel>): Updates.TelegramUpdateRuntimeController<TContext, TUpdate
|
|
238
|
+
}, TMessage extends TelegramRoutedMessage, TCallbackQuery extends TelegramRoutedCallbackQuery, TContext, TModel extends Model.MenuModel>(deps: TelegramInboundRouteRuntimeDeps<TMessage, TCallbackQuery, TContext, TModel>): Updates.TelegramUpdateRuntimeController<TContext, TUpdate> & {
|
|
239
|
+
expireRoutingInput: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["expireRoutingInput"]>;
|
|
240
|
+
shouldReviewHistoricalInput: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["shouldReviewHistoricalInput"]>;
|
|
241
|
+
shouldHoldPendingInput: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["shouldHoldPendingInput"]>;
|
|
242
|
+
forgetPreviousWorld(input: Updates.TelegramHeldSourcePreparation<TContext>, captureTransport?: (ctx: TContext) => (() => boolean) | undefined): Promise<{
|
|
243
|
+
forgotten: number;
|
|
244
|
+
deleted: number;
|
|
245
|
+
}>;
|
|
246
|
+
beforeQueueReceiptPublished: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["beforeQueueReceiptPublished"]>;
|
|
247
|
+
onQueueReceiptCommitted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onQueueReceiptCommitted"]>;
|
|
248
|
+
onQueueReceiptCompleted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onQueueReceiptCompleted"]>;
|
|
249
|
+
onUpdateCompleted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onUpdateCompleted"]>;
|
|
250
|
+
onWorkspaceRestoreRecipientObserved(follower: Bus.TelegramBusFollowerView, isCurrent: () => boolean, ctx: TContext | undefined): Promise<void> | undefined;
|
|
251
|
+
waitForRestoreSettlement(): Promise<void>;
|
|
252
|
+
};
|
|
187
253
|
export interface TelegramAssistantOutputAuthority<TTransportStamp> {
|
|
188
254
|
transportStamp: TTransportStamp;
|
|
189
255
|
route: "direct" | "follower" | "none";
|