@llblab/pi-telegram 0.52.2 → 0.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +15 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +65 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +252 -127
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +128 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +516 -301
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/dist/lib/outbound.js
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { mkdir } from "node:fs/promises";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
-
import { resolveTelegramAttachmentsDir } from "./paths.js";
|
|
10
|
-
import * as Replies from "./replies.js";
|
|
11
|
-
import { isTelegramApiCommitUnknownError } from "./telegram-api.js";
|
|
12
9
|
import { planTelegramButtonReply, } from "./outbound-buttons.js";
|
|
13
10
|
import { planTelegramVoiceReply, stripTelegramCommentMarkupForDelivery, } from "./outbound-markup.js";
|
|
14
11
|
import { createTelegramVoiceReplySender as createTelegramVoiceReplySenderWithPorts } from "./outbound-voice.js";
|
|
12
|
+
import { resolveTelegramAttachmentsDir } from "./paths.js";
|
|
13
|
+
import * as Replies from "./replies.js";
|
|
14
|
+
import { assertTelegramApiCallAuthority, isTelegramApiCommitUnknownError, } from "./telegram-api.js";
|
|
15
15
|
const OUTBOUND_HANDLER_REGISTRY_KEY = "__piTelegramOutboundHandlers__";
|
|
16
16
|
const VOICE_EVENT_RECORDER_KEY = "__piTelegramVoiceEventRecorder__";
|
|
17
|
-
import { buildCommandTemplateInvocation,
|
|
17
|
+
import { buildCommandTemplateInvocation, createCommandTemplateRecovery, getCommandTemplateCompositionSteps, shouldRunCommandTemplateConfig, getCommandTemplateConfiguredTimeout, getCommandTemplateStepTimeout, resolveCommandTemplateNumericField, } from "./command-templates.js";
|
|
18
18
|
const DEFAULT_VOICE_TIMEOUT_MS = 120_000;
|
|
19
19
|
export function bindTelegramRuntimeEventRecorder(recorder) {
|
|
20
20
|
globalThis[VOICE_EVENT_RECORDER_KEY] = recorder;
|
|
@@ -25,7 +25,6 @@ export function recordTelegramRuntimeEvent(category, error, details) {
|
|
|
25
25
|
recorder(category, error, details);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
export { normalizeMarkdownAfterVoiceExtraction, planTelegramVoiceReply, stripTelegramCommentMarkupForDelivery, stripTelegramCommentMarkupForPreview, stripTelegramVoiceMarkupForPreview, } from "./outbound-markup.js";
|
|
29
28
|
// --- Programmatic Outbound Handler Registry Runtime ---
|
|
30
29
|
function getOrCreateOutboundHandlerRegistry() {
|
|
31
30
|
const existing = globalThis[OUTBOUND_HANDLER_REGISTRY_KEY];
|
|
@@ -62,35 +61,8 @@ function getTelegramOutboundProgrammaticHandlers(kind) {
|
|
|
62
61
|
return [...(registry.handlers.get(kind) ?? [])];
|
|
63
62
|
}
|
|
64
63
|
// --- Voice Reply Timeout Helpers ---
|
|
65
|
-
function resolveOutboundNumericControlField(value, values, label) {
|
|
66
|
-
if (value === undefined)
|
|
67
|
-
return undefined;
|
|
68
|
-
const resolved = typeof value === "string"
|
|
69
|
-
? substituteCommandTemplateToken(value, values, label)
|
|
70
|
-
: value;
|
|
71
|
-
if (resolved === "")
|
|
72
|
-
return undefined;
|
|
73
|
-
const numeric = Number(resolved);
|
|
74
|
-
if (!Number.isFinite(numeric) || numeric < 0)
|
|
75
|
-
throw new Error(`Command template ${label} must be a non-negative number.`);
|
|
76
|
-
return numeric;
|
|
77
|
-
}
|
|
78
|
-
function getVoiceReplyConfiguredTimeout(config) {
|
|
79
|
-
const timeout = typeof config === "string" ? undefined : config?.timeout;
|
|
80
|
-
return resolveOutboundNumericControlField(timeout, {}, "timeout");
|
|
81
|
-
}
|
|
82
64
|
function getVoiceReplyTimeout(config) {
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
function getRemainingVoiceReplyTimeout(timeout, startedAt) {
|
|
86
|
-
return Math.max(1, timeout - (Date.now() - startedAt));
|
|
87
|
-
}
|
|
88
|
-
function getVoiceReplyCompositionStepTimeout(handlerTimeout, step, startedAt) {
|
|
89
|
-
const remaining = getRemainingVoiceReplyTimeout(handlerTimeout, startedAt);
|
|
90
|
-
const stepTimeout = getVoiceReplyConfiguredTimeout(step);
|
|
91
|
-
return stepTimeout === undefined
|
|
92
|
-
? remaining
|
|
93
|
-
: Math.min(stepTimeout, remaining);
|
|
65
|
+
return (getCommandTemplateConfiguredTimeout(config) ?? DEFAULT_VOICE_TIMEOUT_MS);
|
|
94
66
|
}
|
|
95
67
|
function formatVoiceReplyExecutionFailure(label, result) {
|
|
96
68
|
const parts = [
|
|
@@ -102,6 +74,9 @@ function formatVoiceReplyExecutionFailure(label, result) {
|
|
|
102
74
|
parts.push(`stdout:\n${result.stdout.trimEnd()}`);
|
|
103
75
|
return parts.join("\n\n");
|
|
104
76
|
}
|
|
77
|
+
function withCommandTemplateRecovery(recover) {
|
|
78
|
+
return recover ? { recover } : {};
|
|
79
|
+
}
|
|
105
80
|
async function runVoiceReplyCommand(label, config, values, options) {
|
|
106
81
|
if (!options.execCommand) {
|
|
107
82
|
throw new Error("execCommand is required for command template execution");
|
|
@@ -115,7 +90,12 @@ async function runVoiceReplyCommand(label, config, values, options) {
|
|
|
115
90
|
timeout: options.timeout,
|
|
116
91
|
...(typeof config === "object" && config.retry !== undefined
|
|
117
92
|
? {
|
|
118
|
-
retry:
|
|
93
|
+
retry: resolveCommandTemplateNumericField(config.retry, {}, "retry"),
|
|
94
|
+
...withCommandTemplateRecovery(createCommandTemplateRecovery(config, values, {
|
|
95
|
+
cwd: options.cwd,
|
|
96
|
+
timeout: options.timeout,
|
|
97
|
+
execCommand: options.execCommand,
|
|
98
|
+
})),
|
|
119
99
|
}
|
|
120
100
|
: {}),
|
|
121
101
|
...(options.stdin !== undefined ? { stdin: options.stdin } : {}),
|
|
@@ -150,12 +130,6 @@ export function findTelegramOutboundHandlers(handlers, type) {
|
|
|
150
130
|
typeof handler === "object" &&
|
|
151
131
|
outboundHandlerMatchesType(handler, type));
|
|
152
132
|
}
|
|
153
|
-
function getTelegramVoiceHandlerCompositionSteps(handler) {
|
|
154
|
-
if (Array.isArray(handler.template)) {
|
|
155
|
-
return expandCommandTemplateConfigs(handler);
|
|
156
|
-
}
|
|
157
|
-
return [];
|
|
158
|
-
}
|
|
159
133
|
function extractVoiceReplyPath(stdout) {
|
|
160
134
|
const path = stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
|
|
161
135
|
if (!path)
|
|
@@ -194,15 +168,19 @@ async function generateTelegramVoiceReplyFileWithHandler(text, options) {
|
|
|
194
168
|
mp3Path: join(options.tempDir, `${artifactId}-voice.mp3`),
|
|
195
169
|
oggPath: join(options.tempDir, `${artifactId}-voice.ogg`),
|
|
196
170
|
});
|
|
197
|
-
|
|
171
|
+
if (!shouldRunCommandTemplateConfig(options.handler, values))
|
|
172
|
+
return undefined;
|
|
173
|
+
const steps = getCommandTemplateCompositionSteps(options.handler);
|
|
198
174
|
if (steps.length > 0) {
|
|
199
175
|
const startedAt = Date.now();
|
|
200
176
|
let stdout = text;
|
|
201
177
|
for (const [index, step] of steps.entries()) {
|
|
178
|
+
if (!shouldRunCommandTemplateConfig(step, values))
|
|
179
|
+
continue;
|
|
202
180
|
try {
|
|
203
181
|
const result = await runVoiceReplyCommand(`Outbound voice template step ${index + 1}`, step, values, {
|
|
204
182
|
cwd: options.cwd,
|
|
205
|
-
timeout:
|
|
183
|
+
timeout: getCommandTemplateStepTimeout(options.timeout, step, Date.now() - startedAt),
|
|
206
184
|
execCommand: options.execCommand,
|
|
207
185
|
stdin: stdout,
|
|
208
186
|
});
|
|
@@ -243,15 +221,19 @@ function getOutboundTextTemplateValues(text) {
|
|
|
243
221
|
}
|
|
244
222
|
async function transformTelegramOutboundTextWithHandler(text, options) {
|
|
245
223
|
const values = getOutboundTextTemplateValues(text);
|
|
246
|
-
|
|
224
|
+
if (!shouldRunCommandTemplateConfig(options.handler, values))
|
|
225
|
+
return text;
|
|
226
|
+
const steps = getCommandTemplateCompositionSteps(options.handler);
|
|
247
227
|
if (steps.length > 0) {
|
|
248
228
|
const startedAt = Date.now();
|
|
249
229
|
let stdout = text;
|
|
250
230
|
for (const [index, step] of steps.entries()) {
|
|
231
|
+
if (!shouldRunCommandTemplateConfig(step, values))
|
|
232
|
+
continue;
|
|
251
233
|
try {
|
|
252
234
|
const result = await runVoiceReplyCommand(`Outbound text template step ${index + 1}`, step, values, {
|
|
253
235
|
cwd: options.cwd,
|
|
254
|
-
timeout:
|
|
236
|
+
timeout: getCommandTemplateStepTimeout(getVoiceReplyTimeout(options.handler), step, Date.now() - startedAt),
|
|
255
237
|
execCommand: options.execCommand,
|
|
256
238
|
stdin: stdout,
|
|
257
239
|
});
|
|
@@ -328,17 +310,33 @@ async function transformTelegramOutboundTextReply(text, options) {
|
|
|
328
310
|
return { text: transformedText, ...(replyMarkup ? { replyMarkup } : {}) };
|
|
329
311
|
}
|
|
330
312
|
export function createTelegramOutboundTextReplyRuntime(deps) {
|
|
313
|
+
const sendTextReply = deps.sendTextReply, sendMarkdownReply = deps.sendMarkdownReply;
|
|
331
314
|
return {
|
|
332
315
|
sendTextReply: async (chatId, replyToMessageId, text, options) => {
|
|
316
|
+
const capturedOptions = options && {
|
|
317
|
+
...options,
|
|
318
|
+
...(options.target ? { target: { ...options.target } } : {}),
|
|
319
|
+
};
|
|
320
|
+
const assertAuthority = capturedOptions?.assertAuthority;
|
|
321
|
+
assertTelegramApiCallAuthority(assertAuthority, false);
|
|
333
322
|
const transformed = await transformTelegramOutboundText(text, {
|
|
334
323
|
handlers: deps.getHandlers?.(),
|
|
335
324
|
cwd: deps.cwd,
|
|
336
325
|
execCommand: deps.execCommand,
|
|
337
326
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
338
327
|
});
|
|
339
|
-
|
|
328
|
+
assertTelegramApiCallAuthority(assertAuthority, false);
|
|
329
|
+
const messageId = await sendTextReply(chatId, replyToMessageId, transformed, capturedOptions);
|
|
330
|
+
assertTelegramApiCallAuthority(assertAuthority, true);
|
|
331
|
+
return messageId;
|
|
340
332
|
},
|
|
341
333
|
sendMarkdownReply: async (chatId, replyToMessageId, markdown, options) => {
|
|
334
|
+
const capturedOptions = options && {
|
|
335
|
+
...options,
|
|
336
|
+
...(options.target ? { target: { ...options.target } } : {}),
|
|
337
|
+
};
|
|
338
|
+
const assertAuthority = capturedOptions?.assertAuthority;
|
|
339
|
+
assertTelegramApiCallAuthority(assertAuthority, false);
|
|
342
340
|
const deliveryMarkdown = stripTelegramCommentMarkupForDelivery(markdown);
|
|
343
341
|
if (!deliveryMarkdown)
|
|
344
342
|
return undefined;
|
|
@@ -347,14 +345,17 @@ export function createTelegramOutboundTextReplyRuntime(deps) {
|
|
|
347
345
|
cwd: deps.cwd,
|
|
348
346
|
execCommand: deps.execCommand,
|
|
349
347
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
350
|
-
replyMarkup:
|
|
348
|
+
replyMarkup: capturedOptions?.replyMarkup,
|
|
351
349
|
});
|
|
352
|
-
|
|
353
|
-
|
|
350
|
+
assertTelegramApiCallAuthority(assertAuthority, false);
|
|
351
|
+
const messageId = await sendMarkdownReply(chatId, replyToMessageId, transformed.text, {
|
|
352
|
+
...capturedOptions,
|
|
354
353
|
...(transformed.replyMarkup
|
|
355
354
|
? { replyMarkup: transformed.replyMarkup }
|
|
356
355
|
: {}),
|
|
357
356
|
});
|
|
357
|
+
assertTelegramApiCallAuthority(assertAuthority, true);
|
|
358
|
+
return messageId;
|
|
358
359
|
},
|
|
359
360
|
};
|
|
360
361
|
}
|
|
@@ -378,7 +379,12 @@ export function createTelegramOutboundTextPreviewRuntime(deps) {
|
|
|
378
379
|
finalizeMarkdownPreview: wrap(deps.finalizeMarkdownPreview),
|
|
379
380
|
preparePreviewDelivery(isDeliveryActive) {
|
|
380
381
|
const prepared = deps.preparePreviewDelivery?.(isDeliveryActive);
|
|
381
|
-
return prepared
|
|
382
|
+
return prepared
|
|
383
|
+
? {
|
|
384
|
+
...prepared,
|
|
385
|
+
finalizeMarkdownPreview: wrap(prepared.finalizeMarkdownPreview),
|
|
386
|
+
}
|
|
387
|
+
: undefined;
|
|
382
388
|
},
|
|
383
389
|
};
|
|
384
390
|
}
|
|
@@ -431,14 +437,22 @@ export function createTelegramOutboundReplyPlanner(store, getRenderingMode = ()
|
|
|
431
437
|
// --- Outbound Reply Artifacts ---
|
|
432
438
|
export function createTelegramOutboundReplyArtifactSender(deps) {
|
|
433
439
|
return async (turn, plan, options) => {
|
|
434
|
-
const isDeliveryActive = () => deps.isDeliveryActive?.() !== false &&
|
|
435
|
-
|
|
440
|
+
const isDeliveryActive = () => deps.isDeliveryActive?.() !== false &&
|
|
441
|
+
options?.isDeliveryActive?.() !== false;
|
|
442
|
+
const sendVoiceReply = createTelegramVoiceReplySender({
|
|
443
|
+
...deps,
|
|
444
|
+
isDeliveryActive,
|
|
445
|
+
});
|
|
436
446
|
// Normalize voice replies: either use explicit voiceReplies array or fall back to voiceText
|
|
437
447
|
const voiceReplies = plan.voiceReplies?.length
|
|
438
448
|
? plan.voiceReplies
|
|
439
449
|
: plan.voiceText
|
|
440
450
|
? [{ text: plan.voiceText, lang: plan.lang, rate: plan.rate }]
|
|
441
451
|
: [];
|
|
452
|
+
// A plan without voice content has nothing to deliver; absence of
|
|
453
|
+
// content is not a synthesis failure.
|
|
454
|
+
if (voiceReplies.length === 0)
|
|
455
|
+
return;
|
|
442
456
|
let anyDelivered = false;
|
|
443
457
|
for (const reply of voiceReplies) {
|
|
444
458
|
if (!isDeliveryActive())
|
package/dist/lib/ownership.js
CHANGED
|
@@ -52,12 +52,16 @@ export function createTelegramBusMessageOwnershipRuntime(deps) {
|
|
|
52
52
|
const record = store.get(chatId, messageId);
|
|
53
53
|
if (!record?.ownerGeneration || !record.recipientBindingKey)
|
|
54
54
|
return record;
|
|
55
|
-
const follower = deps
|
|
55
|
+
const follower = deps
|
|
56
|
+
.listFollowers()
|
|
57
|
+
.find((candidate) => candidate.instanceId === record.instanceId &&
|
|
56
58
|
candidate.registrationGeneration === record.ownerGeneration &&
|
|
57
59
|
candidate.profileKey === record.recipientBindingKey);
|
|
58
60
|
// Protocol is live registration authority, not message-cache history.
|
|
59
61
|
// Keep an incomplete known owner foreign so the forwarder fails closed.
|
|
60
|
-
return follower?.protocol?.capabilities.includes(TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION)
|
|
62
|
+
return follower?.protocol?.capabilities.includes(TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION)
|
|
63
|
+
? { ...record, protocolIdentity: follower.protocol }
|
|
64
|
+
: record;
|
|
61
65
|
},
|
|
62
66
|
recordLocal(input) {
|
|
63
67
|
return store.record({ ...input, instanceId: deps.instanceId });
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram bridge path resolution for Pi-compatible runtimes
|
|
3
|
+
* Zones: telemetry paths, filesystem, runtime identity
|
|
4
|
+
* Owns agent-dir detection and extension-local path derivation
|
|
5
|
+
*/
|
|
1
6
|
export declare const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
2
7
|
export interface TelegramAgentDirResolutionInput {
|
|
3
8
|
env?: Partial<Pick<NodeJS.ProcessEnv, "PI_CODING_AGENT_DIR">>;
|
package/dist/lib/paths.js
CHANGED
|
@@ -2,16 +2,11 @@
|
|
|
2
2
|
* Telegram bridge path resolution for Pi-compatible runtimes
|
|
3
3
|
* Zones: telemetry paths, filesystem, runtime identity
|
|
4
4
|
* Owns agent-dir detection and extension-local path derivation
|
|
5
|
-
*
|
|
6
|
-
* This domain is path-only: it resolves directories and file paths from
|
|
7
|
-
* environment and runtime identity. Its only filesystem read canonicalizes the
|
|
8
|
-
* existing agent-dir prefix; it does not read config, manage state, or import
|
|
9
|
-
* broader Telegram domains.
|
|
10
5
|
*/
|
|
11
6
|
import { createHash } from "node:crypto";
|
|
12
7
|
import { realpathSync } from "node:fs";
|
|
13
8
|
import { homedir } from "node:os";
|
|
14
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from "node:path";
|
|
15
10
|
export const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
16
11
|
/**
|
|
17
12
|
* Resolve the agent data directory for the current Pi-compatible runtime.
|
|
@@ -41,9 +36,13 @@ export function resolveAgentDir(input = {}) {
|
|
|
41
36
|
* Equality proves spelling only, not file identity, consumer closure or migration readiness.
|
|
42
37
|
*/
|
|
43
38
|
export function requireTelegramStoragePathReference(path, expectedPath) {
|
|
44
|
-
if (typeof path !== "string" ||
|
|
45
|
-
|
|
46
|
-
!isAbsolute(
|
|
39
|
+
if (typeof path !== "string" ||
|
|
40
|
+
typeof expectedPath !== "string" ||
|
|
41
|
+
!isAbsolute(path) ||
|
|
42
|
+
resolve(path) !== path ||
|
|
43
|
+
!isAbsolute(expectedPath) ||
|
|
44
|
+
resolve(expectedPath) !== expectedPath ||
|
|
45
|
+
path !== expectedPath) {
|
|
47
46
|
throw new Error("Telegram storage reference does not match its approved absolute path.");
|
|
48
47
|
}
|
|
49
48
|
return path;
|
|
@@ -55,11 +54,12 @@ export function resolveTelegramConfigPath() {
|
|
|
55
54
|
/**
|
|
56
55
|
* Resolve symlinks in the longest existing prefix of `path` and keep the missing suffix. Strict journal reads require
|
|
57
56
|
* canonical anchors, so a symlinked agent directory (for example macOS `/var` → `/private/var`) must not leak in.
|
|
58
|
-
* Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. Windows
|
|
59
|
-
*
|
|
57
|
+
* Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. On Windows only
|
|
58
|
+
* drive-letter and UNC paths resolve; resolving a drive-less root would add the current drive.
|
|
60
59
|
*/
|
|
61
60
|
function canonicalizeExistingPrefix(path) {
|
|
62
|
-
if (!isAbsolute(path) ||
|
|
61
|
+
if (!isAbsolute(path) ||
|
|
62
|
+
(process.platform === "win32" && !/^(?:[a-zA-Z]:[\\/]|\\\\)/u.test(path)))
|
|
63
63
|
return path;
|
|
64
64
|
const absolute = resolve(path);
|
|
65
65
|
const missing = [];
|
|
@@ -68,7 +68,8 @@ function canonicalizeExistingPrefix(path) {
|
|
|
68
68
|
return join(realpathSync(current), ...missing.reverse());
|
|
69
69
|
}
|
|
70
70
|
catch (error) {
|
|
71
|
-
if (error.code !== "ENOENT" ||
|
|
71
|
+
if (error.code !== "ENOENT" ||
|
|
72
|
+
dirname(current) === current)
|
|
72
73
|
return absolute;
|
|
73
74
|
missing.push(basename(current));
|
|
74
75
|
}
|
|
@@ -140,7 +141,10 @@ export function getTelegramJournalPublicationPaths(path, runtimeDir) {
|
|
|
140
141
|
requireTelegramStoragePathReference(runtimeDir, join(dirname(dirname(path)), "runtime"));
|
|
141
142
|
if (basename(dirname(path)) !== "journals")
|
|
142
143
|
throw new Error("Telegram service journal must use its journals namespace.");
|
|
143
|
-
return {
|
|
144
|
+
return {
|
|
145
|
+
transactionPath: join(runtimeDir, `${basename(path)}.transaction`),
|
|
146
|
+
temporaryBasePath: join(runtimeDir, basename(path)),
|
|
147
|
+
};
|
|
144
148
|
}
|
|
145
149
|
/** Durable inactive Thread cleanup work-set journal. */
|
|
146
150
|
export function resolveTelegramThreadCleanupWorkPath(agentDir = resolveAgentDir(), profileName) {
|
|
@@ -186,12 +190,17 @@ export function encodeTelegramSessionDirectoryName(sessionId) {
|
|
|
186
190
|
let name = "";
|
|
187
191
|
bytes.forEach((byte, index) => {
|
|
188
192
|
const char = String.fromCharCode(byte);
|
|
189
|
-
const safe = /[a-z0-9_-]/u.test(char) ||
|
|
190
|
-
|
|
193
|
+
const safe = /[a-z0-9_-]/u.test(char) ||
|
|
194
|
+
(char === "." && index > 0 && index < bytes.length - 1);
|
|
195
|
+
name += safe
|
|
196
|
+
? char
|
|
197
|
+
: `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
191
198
|
});
|
|
192
199
|
if (WINDOWS_RESERVED_NAME.test(name))
|
|
193
200
|
name = `%${name.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}${name.slice(1)}`;
|
|
194
|
-
return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH
|
|
201
|
+
return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH
|
|
202
|
+
? name
|
|
203
|
+
: undefined;
|
|
195
204
|
}
|
|
196
205
|
/** Inverse of `encodeTelegramSessionDirectoryName`; undefined unless the name is exactly canonical. */
|
|
197
206
|
export function decodeTelegramSessionDirectoryName(name) {
|
|
@@ -210,7 +219,9 @@ export function decodeTelegramSessionDirectoryName(name) {
|
|
|
210
219
|
bytes.push(name.charCodeAt(index));
|
|
211
220
|
}
|
|
212
221
|
const decoded = Buffer.from(bytes).toString("utf8");
|
|
213
|
-
return encodeTelegramSessionDirectoryName(decoded) === name
|
|
222
|
+
return encodeTelegramSessionDirectoryName(decoded) === name
|
|
223
|
+
? decoded
|
|
224
|
+
: undefined;
|
|
214
225
|
}
|
|
215
226
|
/** Root of every session folder (<agentDir>/tmp/pi-telegram/sessions). */
|
|
216
227
|
export function resolveTelegramSessionsDir(agentDir = resolveAgentDir()) {
|
|
@@ -227,7 +238,10 @@ export function resolveTelegramSessionDir(sessionId, agentDir = resolveAgentDir(
|
|
|
227
238
|
export function getTelegramRecipientJournalHash(recipientBindingKey) {
|
|
228
239
|
if (!recipientBindingKey)
|
|
229
240
|
throw new Error("Telegram session journal binding key is required.");
|
|
230
|
-
return createHash("sha256")
|
|
241
|
+
return createHash("sha256")
|
|
242
|
+
.update(recipientBindingKey)
|
|
243
|
+
.digest("hex")
|
|
244
|
+
.slice(0, 16);
|
|
231
245
|
}
|
|
232
246
|
/** Durable per-session journal (`sessions/<session id>/journal.<hash>[.<profile>].json`); the name never reveals a role. */
|
|
233
247
|
export function resolveTelegramSessionJournalPath(sessionId, recipientBindingKey, agentDir = resolveAgentDir(), profileName) {
|
|
@@ -240,8 +254,10 @@ export function resolveTelegramSessionPollingJournalPath(sessionId, agentDir = r
|
|
|
240
254
|
/** Accept only a canonical `sessions/<id>/inbox[.<profile>].json` beneath this agent's runtime. */
|
|
241
255
|
export function isTelegramSessionPollingJournalPath(path, agentDir = resolveAgentDir()) {
|
|
242
256
|
const parts = relative(resolveTelegramSessionsDir(agentDir), path).split(sep);
|
|
243
|
-
return isAbsolute(path) &&
|
|
244
|
-
|
|
257
|
+
return (isAbsolute(path) &&
|
|
258
|
+
parts.length === 2 &&
|
|
259
|
+
decodeTelegramSessionDirectoryName(parts[0]) !== undefined &&
|
|
260
|
+
/^inbox(?:\.[a-zA-Z0-9._-]+)?\.json$/u.test(parts[1]));
|
|
245
261
|
}
|
|
246
262
|
/** Runtime event log (<agentDir>/tmp/pi-telegram/logs.jsonl). */
|
|
247
263
|
export function resolveTelegramRuntimeLogPath(agentDir = resolveAgentDir()) {
|
package/dist/lib/pi.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface PiSettingsManager {
|
|
|
42
42
|
}
|
|
43
43
|
export type PiSlashCommandInfo = SlashCommandInfo;
|
|
44
44
|
export type PiRunMode = "tui" | "rpc" | "json" | "print";
|
|
45
|
+
/** Pi rejects a context captured before session replacement with a stale-context error; this is not a domain failure. */
|
|
46
|
+
export declare function isPiStaleContextError(error: unknown): boolean;
|
|
45
47
|
export declare function getExtensionContextMode(ctx: unknown): PiRunMode | undefined;
|
|
46
48
|
export declare function isExtensionContextPassiveRunMode(ctx: unknown): boolean;
|
|
47
49
|
export declare function canStartPollingInExtensionContext(ctx: unknown): boolean;
|
package/dist/lib/pi.js
CHANGED
|
@@ -7,6 +7,12 @@ import { SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
|
7
7
|
function isPiRunMode(value) {
|
|
8
8
|
return (value === "tui" || value === "rpc" || value === "json" || value === "print");
|
|
9
9
|
}
|
|
10
|
+
/** Pi rejects a context captured before session replacement with a stale-context error; this is not a domain failure. */
|
|
11
|
+
export function isPiStaleContextError(error) {
|
|
12
|
+
return (error instanceof Error &&
|
|
13
|
+
(error.message.includes("stale after session") ||
|
|
14
|
+
error.message.includes("stale ctx")));
|
|
15
|
+
}
|
|
10
16
|
export function getExtensionContextMode(ctx) {
|
|
11
17
|
const mode = typeof ctx === "object" && ctx !== null
|
|
12
18
|
? ctx.mode
|
|
@@ -50,7 +56,8 @@ export function createExtensionApiRuntimePorts(api) {
|
|
|
50
56
|
function readEnabledModels(value) {
|
|
51
57
|
if (value === undefined)
|
|
52
58
|
return undefined;
|
|
53
|
-
if (Array.isArray(value) &&
|
|
59
|
+
if (Array.isArray(value) &&
|
|
60
|
+
value.every((entry) => typeof entry === "string")) {
|
|
54
61
|
return [...value];
|
|
55
62
|
}
|
|
56
63
|
throw new TypeError("Host settings enabledModels must be a string array or undefined.");
|
package/dist/lib/polling.d.ts
CHANGED
|
@@ -225,13 +225,8 @@ export interface TelegramThreadAwarePollingPorts<TContext, TOwner> {
|
|
|
225
225
|
restoreFollowerWithOwner: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
|
|
226
226
|
stopFollowerRegistration: () => void;
|
|
227
227
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
isBusRuntimeEnabled: () => boolean;
|
|
231
|
-
isTopicModeUnavailableError: (error: unknown) => boolean;
|
|
232
|
-
getPollingStartedWithTelegramBus: () => boolean;
|
|
233
|
-
setPollingStartedWithTelegramBus: (started: boolean) => void;
|
|
234
|
-
setForceFreshLeaderThreadOnNextStart: (forceFresh: boolean) => void;
|
|
228
|
+
/** Classic/leader polling and follower registration controls that thread-aware polling switches between. */
|
|
229
|
+
export interface TelegramThreadAwarePollingControls<TContext, TOwner> {
|
|
235
230
|
startClassicPolling: (ctx: TContext) => MaybePromise<void>;
|
|
236
231
|
stopClassicPolling: () => Promise<void>;
|
|
237
232
|
startBusLeaderPolling: (ctx: TContext) => Promise<void>;
|
|
@@ -242,25 +237,21 @@ export interface TelegramThreadAwarePollingDeps<TContext, TOwner> extends Telegr
|
|
|
242
237
|
restoreFollowerWithLeader?: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
|
|
243
238
|
hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
|
|
244
239
|
stopFollowerRegistration: () => void;
|
|
240
|
+
isBusRuntimeEnabled: () => boolean;
|
|
241
|
+
isTopicModeUnavailableError: (error: unknown) => boolean;
|
|
245
242
|
}
|
|
246
|
-
export interface
|
|
243
|
+
export interface TelegramThreadAwarePollingDeps<TContext, TOwner> extends TelegramStartupThreadCapabilityProbeDeps, TelegramThreadAwarePollingControls<TContext, TOwner> {
|
|
244
|
+
lifecycle?: TelegramThreadCapabilityLifecycle;
|
|
245
|
+
getPollingStartedWithTelegramBus: () => boolean;
|
|
246
|
+
setPollingStartedWithTelegramBus: (started: boolean) => void;
|
|
247
|
+
setForceFreshLeaderThreadOnNextStart: (forceFresh: boolean) => void;
|
|
248
|
+
}
|
|
249
|
+
export interface TelegramThreadCapabilityOrchestrationDeps<TContext, TOwner> extends TelegramThreadCapabilityReaderDeps, TelegramThreadAwarePollingControls<TContext, TOwner> {
|
|
247
250
|
state: TelegramThreadCapabilityStateRuntime;
|
|
248
251
|
topicTargetStore: TelegramThreadCapabilityStore;
|
|
249
|
-
isBusRuntimeEnabled: () => boolean;
|
|
250
252
|
ownsLock: (ctx: TContext) => boolean;
|
|
251
253
|
isFollowerRegistered?: () => boolean;
|
|
252
|
-
startClassicPolling: (ctx: TContext) => MaybePromise<void>;
|
|
253
|
-
stopClassicPolling: () => Promise<void>;
|
|
254
|
-
startBusLeaderPolling: (ctx: TContext) => Promise<void>;
|
|
255
|
-
stopBusLeaderPolling: () => Promise<void>;
|
|
256
|
-
startLeaderHealth: () => void;
|
|
257
|
-
stopLeaderHealth: () => void;
|
|
258
|
-
registerFollowerWithLeader: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
|
|
259
|
-
restoreFollowerWithLeader?: (ctx: TContext, owner: TOwner) => Promise<boolean | undefined>;
|
|
260
|
-
hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
|
|
261
254
|
suspendLiveThreadTarget?: () => void;
|
|
262
|
-
stopFollowerRegistration: () => void;
|
|
263
|
-
isTopicModeUnavailableError: (error: unknown) => boolean;
|
|
264
255
|
updateStatus: (ctx: TContext) => void;
|
|
265
256
|
recordEvent: (category: string, message: unknown, details?: Record<string, unknown>) => void;
|
|
266
257
|
}
|
|
@@ -308,10 +299,8 @@ export interface TelegramPollingBatchAdmissionDeps<TUpdate extends TelegramUpdat
|
|
|
308
299
|
onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
|
|
309
300
|
}
|
|
310
301
|
export declare function admitTelegramPollingUpdateBatch<TUpdate extends TelegramUpdate>(deps: TelegramPollingBatchAdmissionDeps<TUpdate>): Promise<TelegramPollingBatchAdmissionResult>;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
signal: AbortSignal;
|
|
314
|
-
config: TelegramPollingConfig;
|
|
302
|
+
/** Bot API, journal and progress ports shared by one poll loop and its restartable runner. */
|
|
303
|
+
export interface TelegramPollLoopTransportPorts<TUpdate extends TelegramUpdate> extends TelegramRuntimeEventRecorderPort {
|
|
315
304
|
deleteWebhook: (signal: AbortSignal) => Promise<unknown>;
|
|
316
305
|
getUpdates: (body: Record<string, unknown>, signal: AbortSignal) => Promise<TUpdate[]>;
|
|
317
306
|
getUpdatesRequestBudgetMs?: (body: Record<string, unknown>) => number;
|
|
@@ -320,26 +309,21 @@ export interface TelegramPollLoopDeps<TUpdate extends TelegramUpdate, TContext =
|
|
|
320
309
|
getAcceptedThroughUpdateId?: () => number | undefined;
|
|
321
310
|
getJournalEntryCount: () => number;
|
|
322
311
|
signalUpdateWorker: () => void;
|
|
312
|
+
onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
|
|
313
|
+
onSuccessfulResponse?: (updateCount: number) => void;
|
|
314
|
+
}
|
|
315
|
+
export interface TelegramPollLoopDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends TelegramPollLoopTransportPorts<TUpdate> {
|
|
316
|
+
ctx: TContext;
|
|
317
|
+
signal: AbortSignal;
|
|
318
|
+
config: TelegramPollingConfig;
|
|
323
319
|
onErrorStatus: (message: string) => void;
|
|
324
320
|
onStatusReset: () => void;
|
|
325
321
|
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
326
|
-
onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
|
|
327
|
-
onSuccessfulResponse?: (updateCount: number) => void;
|
|
328
322
|
}
|
|
329
|
-
export interface TelegramPollLoopRunnerDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends
|
|
323
|
+
export interface TelegramPollLoopRunnerDeps<TUpdate extends TelegramUpdate, TContext = unknown> extends TelegramPollLoopTransportPorts<TUpdate> {
|
|
330
324
|
getConfig: () => TelegramPollingConfig;
|
|
331
|
-
deleteWebhook: (signal: AbortSignal) => Promise<unknown>;
|
|
332
|
-
getUpdates: (body: Record<string, unknown>, signal: AbortSignal) => Promise<TUpdate[]>;
|
|
333
|
-
getUpdatesRequestBudgetMs?: (body: Record<string, unknown>) => number;
|
|
334
|
-
persistConfig: (config: TelegramPollingConfig) => Promise<void>;
|
|
335
|
-
appendUpdateBatch: (updates: readonly TUpdate[], acceptedThroughUpdateId?: number) => MaybePromise<unknown>;
|
|
336
|
-
getAcceptedThroughUpdateId?: () => number | undefined;
|
|
337
|
-
getJournalEntryCount: () => number;
|
|
338
|
-
signalUpdateWorker: () => void;
|
|
339
325
|
updateStatus: (ctx: TContext, message?: string) => void;
|
|
340
326
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
341
|
-
onPhaseChange?: (phase: TelegramPollingWorkPhase, currentUpdateId?: number) => void;
|
|
342
|
-
onSuccessfulResponse?: (updateCount: number) => void;
|
|
343
327
|
}
|
|
344
328
|
export declare function sleepTelegramPollingRetry(ms: number, signal?: AbortSignal): Promise<void>;
|
|
345
329
|
export declare function createTelegramPollLoopRunner<TUpdate extends TelegramUpdate, TContext = unknown>(deps: TelegramPollLoopRunnerDeps<TUpdate, TContext>): (ctx: TContext, signal: AbortSignal) => Promise<void>;
|