@markusylisiurunen/tau 0.3.50 → 0.3.52
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/dist/code_mode/command.js +26 -14
- package/dist/code_mode/command.js.map +1 -1
- package/dist/code_mode/index.d.ts +2 -1
- package/dist/code_mode/index.js +1 -0
- package/dist/code_mode/index.js.map +1 -1
- package/dist/core/cli.js +0 -11
- package/dist/core/cli.js.map +1 -1
- package/dist/core/client_tools/command_client_tools.js +236 -151
- package/dist/core/client_tools/command_client_tools.js.map +1 -1
- package/dist/core/config/schema.js +8 -2
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/history/history_manager.js +73 -7
- package/dist/core/history/history_manager.js.map +1 -1
- package/dist/core/history/local_history_store.js +73 -2
- package/dist/core/history/local_history_store.js.map +1 -1
- package/dist/core/history/remote_history_client.js +36 -6
- package/dist/core/history/remote_history_client.js.map +1 -1
- package/dist/core/modes/index.js +0 -1
- package/dist/core/modes/index.js.map +1 -1
- package/dist/core/static/tau_docs/client-tools.md +212 -12
- package/dist/core/static/tau_docs/config-reference.md +1 -1
- package/dist/core/static/tau_docs/configuration.md +1 -1
- package/dist/core/static/tau_docs/credentials.md +4 -4
- package/dist/core/static/tau_docs/history.md +6 -4
- package/dist/core/static/tau_docs/index.md +1 -1
- package/dist/core/static/tau_docs/node-sdk.md +33 -26
- package/dist/core/static/tau_docs/ownership-and-scope.md +3 -4
- package/dist/core/static/tau_docs/prompts-and-project-context.md +2 -2
- package/dist/core/static/tau_docs/remote-sessions.md +4 -47
- package/dist/core/static/tau_docs/security.md +2 -6
- package/dist/core/static/tau_docs/session-protocol-methods.md +48 -7
- package/dist/core/static/tau_docs/session-protocol.md +18 -18
- package/dist/core/static/tau_docs/sessions.md +6 -5
- package/dist/core/static/tau_docs/telegram.md +8 -4
- package/dist/core/static/tau_docs/tools.md +1 -1
- package/dist/core/static/tau_docs/troubleshooting.md +3 -5
- package/dist/core/static/tau_docs/tui.md +4 -6
- package/dist/core/telegram/adapter.js +226 -18
- package/dist/core/telegram/adapter.js.map +1 -1
- package/dist/core/telegram/config.js +1 -1
- package/dist/core/telegram/config.js.map +1 -1
- package/dist/core/telegram/project_preferences.js +42 -12
- package/dist/core/telegram/project_preferences.js.map +1 -1
- package/dist/core/telegram/runtime.js +6 -1
- package/dist/core/telegram/runtime.js.map +1 -1
- package/dist/core/telegram/session_manager.js +50 -4
- package/dist/core/telegram/session_manager.js.map +1 -1
- package/dist/core/telegram/tts.js +137 -0
- package/dist/core/telegram/tts.js.map +1 -0
- package/dist/core/tools/execution_backend.js +13 -3
- package/dist/core/tools/execution_backend.js.map +1 -1
- package/dist/core/tools/presentation.js +55 -17
- package/dist/core/tools/presentation.js.map +1 -1
- package/dist/core/utils/gemini_speech.js +123 -42
- package/dist/core/utils/gemini_speech.js.map +1 -1
- package/dist/core/utils/gemini_transcription.js +1 -1
- package/dist/core/version.js +1 -1
- package/dist/execution/cloudflare_sandbox_execution_environment.js +2 -2
- package/dist/execution/cloudflare_sandbox_execution_environment.js.map +1 -1
- package/dist/execution/fly_sprite_execution_environment.js +2 -2
- package/dist/execution/fly_sprite_execution_environment.js.map +1 -1
- package/dist/host/client_tool_broker.js +71 -17
- package/dist/host/client_tool_broker.js.map +1 -1
- package/dist/host/local_session_host.js +2 -2
- package/dist/host/local_session_host.js.map +1 -1
- package/dist/host/session_host.js.map +1 -1
- package/dist/host/session_protocol_handler.js +17 -6
- package/dist/host/session_protocol_handler.js.map +1 -1
- package/dist/main.js +18 -92
- package/dist/main.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +22 -2
- package/dist/protocol/session_protocol.js +47 -3
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/sdk/client_tool_command.d.ts +41 -13
- package/dist/sdk/client_tool_command.js +157 -39
- package/dist/sdk/client_tool_command.js.map +1 -1
- package/dist/sdk/client_tool_presentation.d.ts +17 -0
- package/dist/sdk/client_tool_presentation.js +9 -0
- package/dist/sdk/client_tool_presentation.js.map +1 -0
- package/dist/sdk/code_mode.js +10 -1
- package/dist/sdk/code_mode.js.map +1 -1
- package/dist/sdk/index.d.ts +5 -4
- package/dist/sdk/index.js +2 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/session.js +28 -9
- package/dist/sdk/session.js.map +1 -1
- package/dist/sdk/types.d.ts +11 -1
- package/dist/transport/errors.d.ts +0 -11
- package/dist/transport/errors.js +0 -12
- package/dist/transport/errors.js.map +1 -1
- package/dist/transport/index.d.ts +1 -3
- package/dist/transport/index.js +1 -2
- package/dist/transport/index.js.map +1 -1
- package/dist/tui/session_chat_app.js +28 -15
- package/dist/tui/session_chat_app.js.map +1 -1
- package/dist/tui/session_chat_controller.js +0 -51
- package/dist/tui/session_chat_controller.js.map +1 -1
- package/dist/tui/speech_playback.js +3 -3
- package/dist/tui/speech_playback.js.map +1 -1
- package/package.json +2 -2
- package/dist/core/modes/rpc_server.js +0 -105
- package/dist/core/modes/rpc_server.js.map +0 -1
- package/dist/transport/stdio_session_transport.d.ts +0 -48
- package/dist/transport/stdio_session_transport.js +0 -338
- package/dist/transport/stdio_session_transport.js.map +0 -1
|
@@ -9,6 +9,7 @@ import { collectSpeechToTextContext, } from "../utils/speech_to_text_context.js"
|
|
|
9
9
|
import { formatTauUserText, splitTauUserText } from "../utils/user_metadata.js";
|
|
10
10
|
import { formatZodError } from "../utils/zod.js";
|
|
11
11
|
import { createScopedTelegramSessionManager, TelegramSessionManagerError, } from "./session_manager.js";
|
|
12
|
+
import { generateTelegramVoice } from "./tts.js";
|
|
12
13
|
const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
13
14
|
const DEFAULT_REQUEST_TIMEOUT_SECONDS = 30;
|
|
14
15
|
const MAX_COMMAND_PREVIEW_CHARS = 128;
|
|
@@ -36,6 +37,8 @@ const CALLBACK_ACTION_PREFIX = "tau:action:";
|
|
|
36
37
|
const MAX_TELEGRAM_ATTACHMENTS_PER_TURN = 10;
|
|
37
38
|
const MAX_TELEGRAM_ATTACHMENT_FILE_BYTES = 20 * 1024 * 1024;
|
|
38
39
|
const MAX_TELEGRAM_ATTACHMENT_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
40
|
+
const MAX_TELEGRAM_VOICE_BYTES = 50 * 1024 * 1024;
|
|
41
|
+
const TELEGRAM_TTS_JOB_TIMEOUT_MS = 5 * 60_000;
|
|
39
42
|
const MAX_TELEGRAM_GROUP_PENDING_MESSAGES = 50;
|
|
40
43
|
const TELEGRAM_ATTACHMENT_TEMP_DIR_PREFIX = "tau-telegram-attachments-";
|
|
41
44
|
const TELEGRAM_REASONING_EFFORTS = [
|
|
@@ -617,15 +620,20 @@ function asPermanentTelegramRequestError(error) {
|
|
|
617
620
|
}
|
|
618
621
|
function createTelegramApi(botToken) {
|
|
619
622
|
const apiUrl = `https://api.telegram.org/bot${botToken}`;
|
|
620
|
-
async function callTelegramMethod(method, payload, resultSchema, signal) {
|
|
623
|
+
async function callTelegramMethod(method, payload, resultSchema, signal, upload) {
|
|
624
|
+
const body = upload ? new FormData() : JSON.stringify(payload);
|
|
625
|
+
if (body instanceof FormData && upload) {
|
|
626
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
627
|
+
body.append(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
628
|
+
}
|
|
629
|
+
body.append(upload.field, new Blob([Uint8Array.from(upload.data)], { type: upload.mimeType }), upload.fileName);
|
|
630
|
+
}
|
|
621
631
|
let response;
|
|
622
632
|
try {
|
|
623
633
|
response = await fetch(`${apiUrl}/${method}`, {
|
|
624
634
|
method: "POST",
|
|
625
|
-
headers: {
|
|
626
|
-
|
|
627
|
-
},
|
|
628
|
-
body: JSON.stringify(payload),
|
|
635
|
+
...(upload ? {} : { headers: { "content-type": "application/json" } }),
|
|
636
|
+
body,
|
|
629
637
|
signal,
|
|
630
638
|
});
|
|
631
639
|
}
|
|
@@ -716,6 +724,14 @@ function createTelegramApi(botToken) {
|
|
|
716
724
|
...(options?.replyMarkup ? { reply_markup: options.replyMarkup } : {}),
|
|
717
725
|
}, z.unknown(), options.signal);
|
|
718
726
|
},
|
|
727
|
+
async sendVoice(chatId, voice, options) {
|
|
728
|
+
await callTelegramMethod("sendVoice", { chat_id: chatId }, z.unknown(), options.signal, {
|
|
729
|
+
field: "voice",
|
|
730
|
+
fileName: "response.ogg",
|
|
731
|
+
mimeType: "audio/ogg",
|
|
732
|
+
data: voice,
|
|
733
|
+
});
|
|
734
|
+
},
|
|
719
735
|
async sendChatAction(chatId, action) {
|
|
720
736
|
await callTelegramMethod("sendChatAction", {
|
|
721
737
|
chat_id: chatId,
|
|
@@ -777,6 +793,7 @@ class TelegramAdapterImpl {
|
|
|
777
793
|
allowedProjectIds;
|
|
778
794
|
api;
|
|
779
795
|
fetchImpl;
|
|
796
|
+
generateVoice;
|
|
780
797
|
onLog;
|
|
781
798
|
commandDefinitions;
|
|
782
799
|
commandHandlers;
|
|
@@ -796,6 +813,8 @@ class TelegramAdapterImpl {
|
|
|
796
813
|
inFlightUpdateTasks = new Set();
|
|
797
814
|
notificationQueueTailByChat = new Map();
|
|
798
815
|
inFlightNotificationTasks = new Set();
|
|
816
|
+
ttsQueueTailBySession = new Map();
|
|
817
|
+
inFlightTtsTasks = new Set();
|
|
799
818
|
unsubscribeSessionEvents;
|
|
800
819
|
loopPromise;
|
|
801
820
|
nextUpdateOffset = 0;
|
|
@@ -829,6 +848,7 @@ class TelegramAdapterImpl {
|
|
|
829
848
|
this.allowedProjectIds = Object.keys(options.projects);
|
|
830
849
|
this.api = options.api;
|
|
831
850
|
this.fetchImpl = options.fetchImpl;
|
|
851
|
+
this.generateVoice = options.generateVoice ?? generateTelegramVoice;
|
|
832
852
|
this.onLog = options.onLog;
|
|
833
853
|
this.commandDefinitions = this.createCommandDefinitions();
|
|
834
854
|
this.commandHandlers = new Map();
|
|
@@ -868,6 +888,8 @@ class TelegramAdapterImpl {
|
|
|
868
888
|
try {
|
|
869
889
|
await this.loopPromise;
|
|
870
890
|
await this.waitForInFlightUpdateTasks();
|
|
891
|
+
await Promise.allSettled(Array.from(this.inFlightTtsTasks));
|
|
892
|
+
this.ttsQueueTailBySession.clear();
|
|
871
893
|
await Promise.allSettled(Array.from(this.inFlightNotificationTasks));
|
|
872
894
|
for (const sessionId of Array.from(this.chatsBySession.keys())) {
|
|
873
895
|
this.stopTypingIndicators(sessionId);
|
|
@@ -910,6 +932,16 @@ class TelegramAdapterImpl {
|
|
|
910
932
|
callbackAction: "interrupt",
|
|
911
933
|
handler: async (chatId) => this.handleInterrupt(chatId),
|
|
912
934
|
},
|
|
935
|
+
{
|
|
936
|
+
command: "/tts_on",
|
|
937
|
+
description: "enable voice responses",
|
|
938
|
+
handler: async (chatId, args) => this.handleTtsPreference(chatId, true, args),
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
command: "/tts_off",
|
|
942
|
+
description: "disable voice responses",
|
|
943
|
+
handler: async (chatId, args) => this.handleTtsPreference(chatId, false, args),
|
|
944
|
+
},
|
|
913
945
|
];
|
|
914
946
|
for (const reasoning of TELEGRAM_REASONING_EFFORTS) {
|
|
915
947
|
definitions.push({
|
|
@@ -1588,6 +1620,24 @@ class TelegramAdapterImpl {
|
|
|
1588
1620
|
await this.reply(chatId, `failed to save project preference: ${error instanceof Error ? error.message : String(error)}`);
|
|
1589
1621
|
}
|
|
1590
1622
|
}
|
|
1623
|
+
async handleTtsPreference(chatId, enabled, args) {
|
|
1624
|
+
const command = enabled ? "/tts_on" : "/tts_off";
|
|
1625
|
+
if (args.length > 0) {
|
|
1626
|
+
await this.reply(chatId, `usage: ${command}`);
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
if (enabled && !this.geminiApiKey) {
|
|
1630
|
+
await this.reply(chatId, "set GEMINI_API_KEY or apiKeys.google to enable voice responses.");
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
try {
|
|
1634
|
+
await this.projectPreferences.setTtsEnabled(this.ownerIdForChat(chatId), enabled);
|
|
1635
|
+
await this.reply(chatId, `voice responses ${enabled ? "enabled" : "disabled"}.`);
|
|
1636
|
+
}
|
|
1637
|
+
catch (error) {
|
|
1638
|
+
await this.reply(chatId, `failed to save voice response preference: ${error instanceof Error ? error.message : String(error)}`);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1591
1641
|
async handleSetReasoning(chatId, reasoning, args) {
|
|
1592
1642
|
if (args.length > 0) {
|
|
1593
1643
|
await this.reply(chatId, `usage: /effort_${reasoning}`);
|
|
@@ -2058,6 +2108,12 @@ class TelegramAdapterImpl {
|
|
|
2058
2108
|
this.handleSessionProgress(event);
|
|
2059
2109
|
return;
|
|
2060
2110
|
}
|
|
2111
|
+
if (event.type === "session-response-completed") {
|
|
2112
|
+
if (this.chatsBySession.has(event.sessionId)) {
|
|
2113
|
+
this.startTtsNotification(event.sessionId, event.text);
|
|
2114
|
+
}
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2061
2117
|
if (event.type === "session-turn-failed" || event.type === "session-turn-rejected") {
|
|
2062
2118
|
if (!this.chatsBySession.has(event.sessionId)) {
|
|
2063
2119
|
return;
|
|
@@ -2147,6 +2203,104 @@ class TelegramAdapterImpl {
|
|
|
2147
2203
|
? [formatSessionHeadline(event.sessionId, "assistant message"), event.progress.text].join("\n")
|
|
2148
2204
|
: event.progress.text, { rich: true, messageId: event.progress.messageId });
|
|
2149
2205
|
}
|
|
2206
|
+
startTtsNotification(sessionId, sourceText) {
|
|
2207
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
2208
|
+
if (!chatIds ||
|
|
2209
|
+
![...chatIds].some((chatId) => this.projectPreferences.isTtsEnabled(this.ownerIdForChat(chatId)))) {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
const previousTask = this.ttsQueueTailBySession.get(sessionId) ?? Promise.resolve();
|
|
2213
|
+
const task = previousTask
|
|
2214
|
+
.then(async () => {
|
|
2215
|
+
if (!this.abortController.signal.aborted) {
|
|
2216
|
+
await this.generateAndSendTtsNotification(sessionId, sourceText);
|
|
2217
|
+
}
|
|
2218
|
+
})
|
|
2219
|
+
.finally(() => {
|
|
2220
|
+
this.inFlightTtsTasks.delete(task);
|
|
2221
|
+
if (this.ttsQueueTailBySession.get(sessionId) === task) {
|
|
2222
|
+
this.ttsQueueTailBySession.delete(sessionId);
|
|
2223
|
+
}
|
|
2224
|
+
});
|
|
2225
|
+
this.ttsQueueTailBySession.set(sessionId, task);
|
|
2226
|
+
this.inFlightTtsTasks.add(task);
|
|
2227
|
+
void task;
|
|
2228
|
+
}
|
|
2229
|
+
async generateAndSendTtsNotification(sessionId, sourceText) {
|
|
2230
|
+
let timedOut = false;
|
|
2231
|
+
const timeoutController = new AbortController();
|
|
2232
|
+
const timeout = setTimeout(() => {
|
|
2233
|
+
timedOut = true;
|
|
2234
|
+
timeoutController.abort();
|
|
2235
|
+
}, TELEGRAM_TTS_JOB_TIMEOUT_MS);
|
|
2236
|
+
timeout.unref?.();
|
|
2237
|
+
try {
|
|
2238
|
+
if (!this.geminiApiKey) {
|
|
2239
|
+
throw new Error("missing Google credential for Telegram voice responses");
|
|
2240
|
+
}
|
|
2241
|
+
const voice = await this.generateVoice({
|
|
2242
|
+
apiKey: this.geminiApiKey,
|
|
2243
|
+
sourceText,
|
|
2244
|
+
fetchImpl: this.fetchImpl,
|
|
2245
|
+
signal: AbortSignal.any([this.abortController.signal, timeoutController.signal]),
|
|
2246
|
+
});
|
|
2247
|
+
if (voice.length > MAX_TELEGRAM_VOICE_BYTES) {
|
|
2248
|
+
throw new Error("generated voice response exceeds Telegram's 50 MB limit");
|
|
2249
|
+
}
|
|
2250
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
2251
|
+
if (!chatIds) {
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
await Promise.all([...chatIds]
|
|
2255
|
+
.filter((chatId) => this.projectPreferences.isTtsEnabled(this.ownerIdForChat(chatId)))
|
|
2256
|
+
.map(async (chatId) => {
|
|
2257
|
+
const result = await this.enqueueVoiceNotification(sessionId, chatId, voice, timeoutController.signal);
|
|
2258
|
+
if (result === "timed-out") {
|
|
2259
|
+
this.log("error", "Telegram voice response job timed out", {
|
|
2260
|
+
sessionId,
|
|
2261
|
+
chatId,
|
|
2262
|
+
cause: "voice response job timed out after 5 minutes",
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
if (result === "failed" || result === "timed-out") {
|
|
2266
|
+
await this.enqueueTtsFailureNotification(sessionId, chatId);
|
|
2267
|
+
}
|
|
2268
|
+
}));
|
|
2269
|
+
}
|
|
2270
|
+
catch (error) {
|
|
2271
|
+
if (this.abortController.signal.aborted) {
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
this.log("error", "failed to generate Telegram voice response", {
|
|
2275
|
+
sessionId,
|
|
2276
|
+
cause: timedOut
|
|
2277
|
+
? "voice generation timed out after 5 minutes"
|
|
2278
|
+
: error instanceof Error
|
|
2279
|
+
? error.message
|
|
2280
|
+
: String(error),
|
|
2281
|
+
});
|
|
2282
|
+
await this.notifyTtsFailure(sessionId);
|
|
2283
|
+
}
|
|
2284
|
+
finally {
|
|
2285
|
+
clearTimeout(timeout);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
async notifyTtsFailure(sessionId) {
|
|
2289
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
2290
|
+
if (!chatIds) {
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
await Promise.all([...chatIds]
|
|
2294
|
+
.filter((chatId) => this.projectPreferences.isTtsEnabled(this.ownerIdForChat(chatId)))
|
|
2295
|
+
.map(async (chatId) => await this.enqueueTtsFailureNotification(sessionId, chatId)));
|
|
2296
|
+
}
|
|
2297
|
+
async enqueueTtsFailureNotification(sessionId, chatId) {
|
|
2298
|
+
if (this.abortController.signal.aborted ||
|
|
2299
|
+
!this.projectPreferences.isTtsEnabled(this.ownerIdForChat(chatId))) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
await this.enqueueNotification(sessionId, chatId, "voice response failed. please try again.", {});
|
|
2303
|
+
}
|
|
2150
2304
|
notifyLifecycle(sessionId, projectId, state) {
|
|
2151
2305
|
const sessionName = formatTelegramSessionName(sessionId, projectId);
|
|
2152
2306
|
if (state === "failed") {
|
|
@@ -2207,6 +2361,54 @@ class TelegramAdapterImpl {
|
|
|
2207
2361
|
this.inFlightNotificationTasks.add(trackedTask);
|
|
2208
2362
|
return deliveryTask;
|
|
2209
2363
|
}
|
|
2364
|
+
enqueueVoiceNotification(sessionId, chatId, voice, jobSignal) {
|
|
2365
|
+
const previousTask = this.notificationQueueTailByChat.get(chatId) ?? Promise.resolve();
|
|
2366
|
+
const deliveryTask = previousTask
|
|
2367
|
+
.then(async () => {
|
|
2368
|
+
if (this.abortController.signal.aborted ||
|
|
2369
|
+
!this.projectPreferences.isTtsEnabled(this.ownerIdForChat(chatId))) {
|
|
2370
|
+
return "skipped";
|
|
2371
|
+
}
|
|
2372
|
+
if (jobSignal.aborted) {
|
|
2373
|
+
return "timed-out";
|
|
2374
|
+
}
|
|
2375
|
+
const result = await this.sendWithRetry(async (signal) => await this.api.sendVoice(chatId, voice, { signal }), jobSignal);
|
|
2376
|
+
if (result === ABORTED) {
|
|
2377
|
+
return jobSignal.aborted && !this.abortController.signal.aborted
|
|
2378
|
+
? "timed-out"
|
|
2379
|
+
: "skipped";
|
|
2380
|
+
}
|
|
2381
|
+
return "sent";
|
|
2382
|
+
})
|
|
2383
|
+
.catch((error) => {
|
|
2384
|
+
if (this.abortController.signal.aborted) {
|
|
2385
|
+
return "skipped";
|
|
2386
|
+
}
|
|
2387
|
+
const deliveryError = error instanceof TelegramDeliveryError
|
|
2388
|
+
? error
|
|
2389
|
+
: new TelegramDeliveryError(error, 1, false);
|
|
2390
|
+
this.log("error", deliveryError.retryable
|
|
2391
|
+
? "telegram voice response delivery retries exhausted"
|
|
2392
|
+
: "failed to send Telegram voice response", {
|
|
2393
|
+
sessionId,
|
|
2394
|
+
chatId,
|
|
2395
|
+
attempts: deliveryError.attempts,
|
|
2396
|
+
cause: deliveryError.message,
|
|
2397
|
+
});
|
|
2398
|
+
return "failed";
|
|
2399
|
+
});
|
|
2400
|
+
const trackedTask = deliveryTask
|
|
2401
|
+
.then(() => undefined)
|
|
2402
|
+
.finally(() => {
|
|
2403
|
+
this.inFlightNotificationTasks.delete(trackedTask);
|
|
2404
|
+
if (this.notificationQueueTailByChat.get(chatId) === trackedTask) {
|
|
2405
|
+
this.notificationQueueTailByChat.delete(chatId);
|
|
2406
|
+
}
|
|
2407
|
+
});
|
|
2408
|
+
this.notificationQueueTailByChat.set(chatId, trackedTask);
|
|
2409
|
+
this.inFlightNotificationTasks.add(trackedTask);
|
|
2410
|
+
return deliveryTask;
|
|
2411
|
+
}
|
|
2210
2412
|
startTypingIndicators(sessionId) {
|
|
2211
2413
|
const chatIds = this.chatsBySession.get(sessionId);
|
|
2212
2414
|
if (!chatIds) {
|
|
@@ -2346,17 +2548,17 @@ class TelegramAdapterImpl {
|
|
|
2346
2548
|
}
|
|
2347
2549
|
}
|
|
2348
2550
|
}
|
|
2349
|
-
async sendWithRetry(send) {
|
|
2551
|
+
async sendWithRetry(send, externalSignal) {
|
|
2350
2552
|
for (let attempt = 1;; attempt += 1) {
|
|
2351
2553
|
try {
|
|
2352
|
-
const result = await this.runDeliveryAttempt(send);
|
|
2554
|
+
const result = await this.runDeliveryAttempt(send, externalSignal);
|
|
2353
2555
|
if (result === ABORTED) {
|
|
2354
2556
|
return ABORTED;
|
|
2355
2557
|
}
|
|
2356
2558
|
return;
|
|
2357
2559
|
}
|
|
2358
2560
|
catch (error) {
|
|
2359
|
-
if (this.abortController.signal.aborted) {
|
|
2561
|
+
if (this.abortController.signal.aborted || externalSignal?.aborted) {
|
|
2360
2562
|
return ABORTED;
|
|
2361
2563
|
}
|
|
2362
2564
|
const retryDelayMs = TELEGRAM_DELIVERY_RETRY_DELAYS_MS[attempt - 1];
|
|
@@ -2364,15 +2566,18 @@ class TelegramAdapterImpl {
|
|
|
2364
2566
|
if (!retryable || retryDelayMs === undefined) {
|
|
2365
2567
|
throw new TelegramDeliveryError(error, attempt, retryable);
|
|
2366
2568
|
}
|
|
2367
|
-
await this.wait(Math.max(retryDelayMs, error.retryAfterMs ?? 0));
|
|
2368
|
-
if (this.abortController.signal.aborted) {
|
|
2569
|
+
await this.wait(Math.max(retryDelayMs, error.retryAfterMs ?? 0), externalSignal);
|
|
2570
|
+
if (this.abortController.signal.aborted || externalSignal?.aborted) {
|
|
2369
2571
|
return ABORTED;
|
|
2370
2572
|
}
|
|
2371
2573
|
}
|
|
2372
2574
|
}
|
|
2373
2575
|
}
|
|
2374
|
-
async runDeliveryAttempt(send) {
|
|
2375
|
-
|
|
2576
|
+
async runDeliveryAttempt(send, externalSignal) {
|
|
2577
|
+
const deliverySignal = externalSignal
|
|
2578
|
+
? AbortSignal.any([this.abortController.signal, externalSignal])
|
|
2579
|
+
: this.abortController.signal;
|
|
2580
|
+
if (deliverySignal.aborted) {
|
|
2376
2581
|
return ABORTED;
|
|
2377
2582
|
}
|
|
2378
2583
|
const attemptController = new AbortController();
|
|
@@ -2382,7 +2587,7 @@ class TelegramAdapterImpl {
|
|
|
2382
2587
|
resolve(ABORTED);
|
|
2383
2588
|
attemptController.abort();
|
|
2384
2589
|
};
|
|
2385
|
-
|
|
2590
|
+
deliverySignal.addEventListener("abort", abortListener, { once: true });
|
|
2386
2591
|
});
|
|
2387
2592
|
let timeout;
|
|
2388
2593
|
const timeoutPromise = new Promise((resolve) => {
|
|
@@ -2405,7 +2610,7 @@ class TelegramAdapterImpl {
|
|
|
2405
2610
|
clearTimeout(timeout);
|
|
2406
2611
|
}
|
|
2407
2612
|
if (abortListener) {
|
|
2408
|
-
|
|
2613
|
+
deliverySignal.removeEventListener("abort", abortListener);
|
|
2409
2614
|
}
|
|
2410
2615
|
if (raceResult === ABORTED) {
|
|
2411
2616
|
return ABORTED;
|
|
@@ -2417,13 +2622,16 @@ class TelegramAdapterImpl {
|
|
|
2417
2622
|
throw raceResult.error;
|
|
2418
2623
|
}
|
|
2419
2624
|
}
|
|
2420
|
-
async wait(durationMs) {
|
|
2421
|
-
|
|
2625
|
+
async wait(durationMs, externalSignal) {
|
|
2626
|
+
const waitSignal = externalSignal
|
|
2627
|
+
? AbortSignal.any([this.abortController.signal, externalSignal])
|
|
2628
|
+
: this.abortController.signal;
|
|
2629
|
+
if (durationMs <= 0 || waitSignal.aborted) {
|
|
2422
2630
|
return;
|
|
2423
2631
|
}
|
|
2424
2632
|
await new Promise((resolve) => {
|
|
2425
2633
|
const timeout = setTimeout(() => {
|
|
2426
|
-
|
|
2634
|
+
waitSignal.removeEventListener("abort", onAbort);
|
|
2427
2635
|
resolve();
|
|
2428
2636
|
}, durationMs);
|
|
2429
2637
|
timeout.unref?.();
|
|
@@ -2431,7 +2639,7 @@ class TelegramAdapterImpl {
|
|
|
2431
2639
|
clearTimeout(timeout);
|
|
2432
2640
|
resolve();
|
|
2433
2641
|
};
|
|
2434
|
-
|
|
2642
|
+
waitSignal.addEventListener("abort", onAbort, { once: true });
|
|
2435
2643
|
});
|
|
2436
2644
|
}
|
|
2437
2645
|
}
|