agents 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/dist/{agent-tool-types-CzGGB-20.d.ts → agent-routing-DE5zmCQ8.d.ts} +1844 -1336
- package/dist/agent-routing.d.ts +14 -0
- package/dist/agent-routing.js +187 -0
- package/dist/agent-routing.js.map +1 -0
- package/dist/agent-tool-types.d.ts +26 -26
- package/dist/{agent-tools-zR2d5uij.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
- package/dist/agent-tools.d.ts +21 -21
- package/dist/agent-tools.js +2 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/browser/ai.js +6 -2
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
- package/dist/callable-decorator.d.ts +18 -0
- package/dist/callable-decorator.js +71 -0
- package/dist/callable-decorator.js.map +1 -0
- package/dist/capability-BjSKYpzg.js +42 -0
- package/dist/capability-BjSKYpzg.js.map +1 -0
- package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
- package/dist/channel-Bnm4S7T2.d.ts +491 -0
- package/dist/channels/ai-sdk.d.ts +46 -0
- package/dist/channels/ai-sdk.js +120 -0
- package/dist/channels/ai-sdk.js.map +1 -0
- package/dist/channels/email.d.ts +95 -0
- package/dist/channels/email.js +323 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/index.d.ts +233 -0
- package/dist/channels/index.js +608 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/slack.d.ts +140 -0
- package/dist/channels/slack.js +614 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/tanstack-ai.d.ts +39 -0
- package/dist/channels/tanstack-ai.js +17 -0
- package/dist/channels/tanstack-ai.js.map +1 -0
- package/dist/channels/telegram.d.ts +106 -0
- package/dist/channels/telegram.js +427 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/voice.d.ts +45 -0
- package/dist/channels/voice.js +122 -0
- package/dist/channels/voice.js.map +1 -0
- package/dist/chat/index.d.ts +2328 -2015
- package/dist/chat/index.js +891 -521
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +14 -1
- package/dist/chat/react.js +82 -52
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +7 -7
- package/dist/chat-sdk/index.js +1 -1
- package/dist/{client-zqKcsyFa.js → client-jagG8a9_.js} +129 -37
- package/dist/client-jagG8a9_.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/{cloudflare-BduZwmYK.js → cloudflare-Dzvc7V2N.js} +10 -3
- package/dist/{cloudflare-BduZwmYK.js.map → cloudflare-Dzvc7V2N.js.map} +1 -1
- package/dist/context/index.d.ts +216 -0
- package/dist/context/index.js +454 -0
- package/dist/context/index.js.map +1 -0
- package/dist/current-agent-Da_C9a3b.d.ts +266 -0
- package/dist/current-agent-DhoDkSnH.js +51 -0
- package/dist/current-agent-DhoDkSnH.js.map +1 -0
- package/dist/diagnostics-BzvaX2UT.js +45 -0
- package/dist/diagnostics-BzvaX2UT.js.map +1 -0
- package/dist/diagnostics-C4jcz3VK.js +360 -0
- package/dist/diagnostics-C4jcz3VK.js.map +1 -0
- package/dist/{do-oauth-client-provider-VTZj2VtM.d.ts → do-oauth-client-provider-Tmf1vgKz.d.ts} +2 -2
- package/dist/{email-CL27preh.d.ts → email-7TatiTnl.d.ts} +38 -9
- package/dist/email-send.d.ts +15 -0
- package/dist/email-send.js +32 -0
- package/dist/email-send.js.map +1 -0
- package/dist/email.d.ts +14 -10
- package/dist/email.js.map +1 -1
- package/dist/{handler-stateless-C_bo-Ytq.d.ts → handler-stateless-DxYpJ_XF.d.ts} +3 -3
- package/dist/{handler-stateless-CIkKPETH.js → handler-stateless-VvrWSAVA.js} +5 -5
- package/dist/handler-stateless-VvrWSAVA.js.map +1 -0
- package/dist/index-BB0kqhIz.d.ts +101 -0
- package/dist/index-XDkuQ7zm.d.ts +89 -0
- package/dist/{index-BRnybD6X.d.ts → index-YSKgfgg9.d.ts} +21 -31
- package/dist/index.d.ts +113 -101
- package/dist/index.js +11 -7234
- package/dist/ingress-BfetZbMO.js +83 -0
- package/dist/ingress-BfetZbMO.js.map +1 -0
- package/dist/internal-CYlgHl1l.js +59 -0
- package/dist/internal-CYlgHl1l.js.map +1 -0
- package/dist/internal_context-BlxFEWfn.d.ts +19 -0
- package/dist/internal_context.d.ts +10 -4
- package/dist/internal_context.js +1 -10
- package/dist/{client-invoker-BNSZxAkv.d.ts → invoker-CG0_p_Wq.d.ts} +2 -2
- package/dist/{client-invoker-VNZ7X0nn.js → invoker-CHMnoxIA.js} +2 -2
- package/dist/invoker-CHMnoxIA.js.map +1 -0
- package/dist/lifecycle/index.d.ts +66 -0
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle-CMRGjZdw.js +1299 -0
- package/dist/lifecycle-CMRGjZdw.js.map +1 -0
- package/dist/mcp/{do-oauth-client-provider.d.ts → client/do-oauth-client-provider.d.ts} +1 -1
- package/dist/mcp/{do-oauth-client-provider.js → client/do-oauth-client-provider.js} +1 -1
- package/dist/mcp/client/do-oauth-client-provider.js.map +1 -0
- package/dist/mcp/client/index.d.ts +42 -0
- package/dist/mcp/{client.js → client/index.js} +1 -1
- package/dist/mcp/{x402.d.ts → client/x402.d.ts} +2 -2
- package/dist/mcp/{x402.js → client/x402.js} +2 -2
- package/dist/mcp/client/x402.js.map +1 -0
- package/dist/mcp/index.d.ts +36 -36
- package/dist/mcp/index.js +14 -16
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/{server.d.ts → server/index.d.ts} +1 -1
- package/dist/mcp/{server.js → server/index.js} +1 -1
- package/dist/observability/ai/index.js +50 -35
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/observability/index.d.ts +4 -4
- package/dist/observability/index.js +3 -50
- package/dist/observability/index.js.map +1 -1
- package/dist/{protocol-Dqc2MQxo.js → protocol-B0nh6KNf.js} +19 -21
- package/dist/protocol-B0nh6KNf.js.map +1 -0
- package/dist/react.d.ts +4 -4
- package/dist/react.js +1 -1
- package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +13 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +137 -0
- package/dist/routing/index.js +244 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sanitize-D9TujEK8.js +79 -0
- package/dist/sanitize-D9TujEK8.js.map +1 -0
- package/dist/schedule.d.ts +25 -94
- package/dist/schedule.js +1 -98
- package/dist/schedule.js.map +1 -1
- package/dist/scheduler-DD9NdYbF.js +665 -0
- package/dist/scheduler-DD9NdYbF.js.map +1 -0
- package/dist/scheduler-Dwh85ZGl.d.ts +223 -0
- package/dist/schedules/index.d.ts +22 -0
- package/dist/schedules/index.js +2 -0
- package/dist/schedules/parser.d.ts +79 -0
- package/dist/schedules/parser.js +103 -0
- package/dist/schedules/parser.js.map +1 -0
- package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +441 -0
- package/dist/sessions/index.js +2063 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/skills/index.d.ts +99 -0
- package/dist/skills/index.js +254 -5
- package/dist/skills/index.js.map +1 -1
- package/dist/sql-error-CPY-GXyI.d.ts +12 -0
- package/dist/sql-error.d.ts +2 -0
- package/dist/sql-error.js +16 -0
- package/dist/sql-error.js.map +1 -0
- package/dist/src-DlSHshb2.js +6963 -0
- package/dist/src-DlSHshb2.js.map +1 -0
- package/dist/streams/index.d.ts +120 -0
- package/dist/streams/index.js +107 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams-D6tJ0NN9.d.ts +370 -0
- package/dist/streams-DZKgAj9b.js +709 -0
- package/dist/streams-DZKgAj9b.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/surface-bZZJqBka.js +17 -0
- package/dist/surface-bZZJqBka.js.map +1 -0
- package/dist/tasks/index.d.ts +64 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks-BRJ5zgya.d.ts +517 -0
- package/dist/tasks-ylZgBjhj.js +1656 -0
- package/dist/tasks-ylZgBjhj.js.map +1 -0
- package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
- package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
- package/dist/text-stream-CpdiKrJB.js +272 -0
- package/dist/text-stream-CpdiKrJB.js.map +1 -0
- package/dist/tokens-nHAKcN6M.js +52 -0
- package/dist/tokens-nHAKcN6M.js.map +1 -0
- package/dist/tool-schema-CBjGPrsQ.js +31 -0
- package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
- package/dist/types-B7LojTe4.d.ts +202 -0
- package/dist/types-_Faxb570.d.ts +439 -0
- package/dist/voice/client.d.ts +226 -0
- package/dist/voice/client.js +932 -0
- package/dist/voice/client.js.map +1 -0
- package/dist/voice/errors.d.ts +43 -0
- package/dist/voice/errors.js +41 -0
- package/dist/voice/errors.js.map +1 -0
- package/dist/voice/index.d.ts +271 -0
- package/dist/voice/index.js +1812 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/react.d.ts +167 -0
- package/dist/voice/react.js +234 -0
- package/dist/voice/react.js.map +1 -0
- package/dist/voice/sfu.d.ts +71 -0
- package/dist/voice/sfu.js +157 -0
- package/dist/voice/sfu.js.map +1 -0
- package/dist/voice/text.d.ts +6 -0
- package/dist/voice/text.js +2 -0
- package/dist/voice/types.d.ts +58 -0
- package/dist/voice/types.js +18 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/workers-ai.d.ts +136 -0
- package/dist/voice/workers-ai.js +568 -0
- package/dist/voice/workers-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +192 -0
- package/dist/websockets/index.js +2 -0
- package/dist/websockets-DUfRHPRq.js +502 -0
- package/dist/websockets-DUfRHPRq.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +22 -22
- package/dist/workflows.js +2 -1
- package/dist/workflows.js.map +1 -1
- package/dist/{ws-chat-transport-CIoOBbO7.js → ws-chat-transport-rWwta645.js} +152 -15
- package/dist/ws-chat-transport-rWwta645.js.map +1 -0
- package/docs/agent-class.md +29 -87
- package/docs/agent-tools.md +2 -1
- package/docs/channels.md +323 -0
- package/docs/chat-agents.md +19 -25
- package/docs/context.md +131 -0
- package/docs/durable-execution.md +1 -1
- package/docs/http-websockets.md +1 -11
- package/docs/human-in-the-loop.md +1 -1
- package/docs/index.md +16 -12
- package/docs/lifecycle.md +370 -0
- package/docs/long-running-agents.md +4 -6
- package/docs/mcp-client.md +55 -0
- package/docs/mcp-servers.md +5 -1
- package/docs/observability.md +11 -11
- package/docs/resumable-streaming.md +2 -2
- package/docs/routing.md +105 -0
- package/docs/scheduling.md +175 -15
- package/docs/server-driven-messages.md +1 -1
- package/docs/sessions.md +237 -871
- package/docs/streams.md +213 -0
- package/docs/sub-agents.md +185 -125
- package/docs/tasks.md +246 -0
- package/docs/voice.md +745 -0
- package/package.json +144 -33
- package/dist/cli/index.js +0 -26
- package/dist/cli/index.js.map +0 -1
- package/dist/client-invoker-VNZ7X0nn.js.map +0 -1
- package/dist/client-zqKcsyFa.js.map +0 -1
- package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
- package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
- package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
- package/dist/experimental/memory/session/index.d.ts +0 -670
- package/dist/experimental/memory/session/index.js +0 -2374
- package/dist/experimental/memory/session/index.js.map +0 -1
- package/dist/experimental/memory/utils/index.d.ts +0 -96
- package/dist/experimental/memory/utils/index.js +0 -79
- package/dist/experimental/memory/utils/index.js.map +0 -1
- package/dist/handler-stateless-CIkKPETH.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal_context-Dg4Cgjcu.d.ts +0 -37
- package/dist/internal_context.js.map +0 -1
- package/dist/mcp/client.d.ts +0 -42
- package/dist/mcp/do-oauth-client-provider.js.map +0 -1
- package/dist/mcp/x402.js.map +0 -1
- package/dist/protocol-Dqc2MQxo.js.map +0 -1
- package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
- package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
- package/dist/ws-chat-transport-CIoOBbO7.js.map +0 -1
- /package/dist/{cli/index.d.ts → index-BVVgDSdq.d.ts} +0 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { t as isChannelMessageSurface } from "../surface-bZZJqBka.js";
|
|
2
|
+
import { a as isRecord, i as encodeUtf8, n as defaultText, o as renderInput, r as emptyIngressResponse, s as uncertain } from "../internal-CYlgHl1l.js";
|
|
3
|
+
import { i as createPacer, r as consumeChunks, t as matchesPath } from "../ingress-BfetZbMO.js";
|
|
4
|
+
//#region src/channels/adapters/telegram.ts
|
|
5
|
+
const TELEGRAM_MESSAGE_LIMIT = 4096;
|
|
6
|
+
const DEFAULT_STREAM_INTERVAL_MS = 500;
|
|
7
|
+
const DEFAULT_TELEGRAM_WEBHOOK_PATH = "/webhooks/telegram";
|
|
8
|
+
const APPROVAL_INSTRUCTIONS = "Reply YES to approve or NO to reject.";
|
|
9
|
+
const INTERACTION_FOOTER = /\n\nReply YES to approve or NO to reject\.\n\n\[channel-interaction:v1:([A-Za-z0-9_-]+)\]$/;
|
|
10
|
+
function channelIdentity(chatId) {
|
|
11
|
+
return `telegram:chat:${chatId}`;
|
|
12
|
+
}
|
|
13
|
+
function userIdentity(userId) {
|
|
14
|
+
return `telegram:user:${userId}`;
|
|
15
|
+
}
|
|
16
|
+
function messageIdentity(chatId, messageId) {
|
|
17
|
+
return `${channelIdentity(chatId)}:message:${messageId}`;
|
|
18
|
+
}
|
|
19
|
+
function updateIdentity(chatId, updateId) {
|
|
20
|
+
return `${channelIdentity(chatId)}:update:${updateId}`;
|
|
21
|
+
}
|
|
22
|
+
function encodeInteractionId(interactionId) {
|
|
23
|
+
const bytes = encodeUtf8(interactionId);
|
|
24
|
+
let binary = "";
|
|
25
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
26
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
27
|
+
}
|
|
28
|
+
function decodeInteractionId(value) {
|
|
29
|
+
try {
|
|
30
|
+
const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
31
|
+
const padding = "=".repeat((4 - base64.length % 4) % 4);
|
|
32
|
+
const binary = atob(base64 + padding);
|
|
33
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
34
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes) || void 0;
|
|
35
|
+
} catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function approvalText(interactionId, request) {
|
|
40
|
+
return [
|
|
41
|
+
...request.title ? [request.title] : [],
|
|
42
|
+
request.summary,
|
|
43
|
+
`Input:\n${renderInput(request.input)}`,
|
|
44
|
+
APPROVAL_INSTRUCTIONS,
|
|
45
|
+
`[channel-interaction:v1:${encodeInteractionId(interactionId)}]`
|
|
46
|
+
].join("\n\n");
|
|
47
|
+
}
|
|
48
|
+
function asApiResponse(value) {
|
|
49
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
50
|
+
}
|
|
51
|
+
function classifyResponse(response, payload) {
|
|
52
|
+
if (payload.ok === true) {
|
|
53
|
+
const result = asApiResponse(payload.result);
|
|
54
|
+
if (typeof result?.message_id === "number") return {
|
|
55
|
+
status: "delivered",
|
|
56
|
+
reference: String(result.message_id)
|
|
57
|
+
};
|
|
58
|
+
return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
|
|
59
|
+
}
|
|
60
|
+
if (payload.ok === false) {
|
|
61
|
+
const errorCode = typeof payload.error_code === "number" ? payload.error_code : response.status;
|
|
62
|
+
const message = typeof payload.description === "string" ? payload.description : "Telegram rejected the message";
|
|
63
|
+
const error = {
|
|
64
|
+
code: `TELEGRAM_API_ERROR_${errorCode}`,
|
|
65
|
+
message
|
|
66
|
+
};
|
|
67
|
+
if (errorCode >= 500) return {
|
|
68
|
+
status: "uncertain",
|
|
69
|
+
error
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
status: "failed",
|
|
73
|
+
retryable: errorCode === 429,
|
|
74
|
+
error
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
|
|
78
|
+
}
|
|
79
|
+
function asTelegramMessage(value) {
|
|
80
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
81
|
+
}
|
|
82
|
+
function telegramActor(message) {
|
|
83
|
+
const sender = message.from;
|
|
84
|
+
if (sender) {
|
|
85
|
+
const fullName = [sender.first_name, sender.last_name].filter(Boolean).join(" ");
|
|
86
|
+
return {
|
|
87
|
+
id: userIdentity(sender.id),
|
|
88
|
+
identity: { subject: `user:${sender.id}` },
|
|
89
|
+
...sender.username && { username: sender.username },
|
|
90
|
+
...fullName && { fullName },
|
|
91
|
+
isBot: sender.is_bot
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const senderChat = message.sender_chat;
|
|
95
|
+
if (!senderChat) return void 0;
|
|
96
|
+
const fullName = senderChat.title ?? [senderChat.first_name, senderChat.last_name].filter(Boolean).join(" ");
|
|
97
|
+
return {
|
|
98
|
+
id: channelIdentity(senderChat.id),
|
|
99
|
+
identity: { subject: `chat:${senderChat.id}` },
|
|
100
|
+
...senderChat.username && { username: senderChat.username },
|
|
101
|
+
...fullName && { fullName },
|
|
102
|
+
isBot: "unknown"
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function chatLabel(chat) {
|
|
106
|
+
return `Telegram · ${(chat.title ?? (chat.username ? `@${chat.username}` : void 0) ?? [chat.first_name, chat.last_name].filter(Boolean).join(" ") ?? String(chat.id)) || chat.id}`;
|
|
107
|
+
}
|
|
108
|
+
function eventContext(message, eventId, botUserId) {
|
|
109
|
+
const actor = telegramActor(message);
|
|
110
|
+
const channelId = channelIdentity(message.chat.id);
|
|
111
|
+
return {
|
|
112
|
+
eventId,
|
|
113
|
+
thread: {
|
|
114
|
+
id: typeof message.message_thread_id === "number" ? `${channelId}:topic:${message.message_thread_id}` : channelId,
|
|
115
|
+
isDirectMessage: message.chat.type === "private" ? true : message.chat.type === "group" || message.chat.type === "supergroup" || message.chat.type === "channel" ? false : "unknown"
|
|
116
|
+
},
|
|
117
|
+
replySurface: {
|
|
118
|
+
version: 1,
|
|
119
|
+
address: {
|
|
120
|
+
chatId: String(message.chat.id),
|
|
121
|
+
...botUserId && { botUserId },
|
|
122
|
+
...typeof message.message_thread_id === "number" && { messageThreadId: message.message_thread_id },
|
|
123
|
+
replyToMessageId: message.message_id
|
|
124
|
+
},
|
|
125
|
+
label: chatLabel(message.chat)
|
|
126
|
+
},
|
|
127
|
+
...actor && { actor }
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function telegramTimestamp(value) {
|
|
131
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
132
|
+
const date = /* @__PURE__ */ new Date(value * 1e3);
|
|
133
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : void 0;
|
|
134
|
+
}
|
|
135
|
+
function normalizedMessage(message, eventId, edited, botUserId) {
|
|
136
|
+
const reply = asTelegramMessage(message.reply_to_message);
|
|
137
|
+
const sentAt = telegramTimestamp(message.date);
|
|
138
|
+
const editedAt = telegramTimestamp(message.edit_date);
|
|
139
|
+
return {
|
|
140
|
+
type: "message",
|
|
141
|
+
...eventContext(message, eventId, botUserId),
|
|
142
|
+
message: {
|
|
143
|
+
id: messageIdentity(message.chat.id, message.message_id),
|
|
144
|
+
text: message.text,
|
|
145
|
+
...typeof reply?.message_id === "number" && { reply: {
|
|
146
|
+
id: messageIdentity(message.chat.id, reply.message_id),
|
|
147
|
+
...typeof reply.text === "string" && { text: reply.text }
|
|
148
|
+
} },
|
|
149
|
+
...(sentAt || edited || editedAt) && { metadata: {
|
|
150
|
+
...sentAt && { sentAt },
|
|
151
|
+
...edited && { edited: true },
|
|
152
|
+
...editedAt && { editedAt }
|
|
153
|
+
} }
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function approvalResponse(message, eventId, botUserId) {
|
|
158
|
+
if (botUserId === void 0) return void 0;
|
|
159
|
+
if (message.text !== "YES" && message.text !== "NO") return void 0;
|
|
160
|
+
const reply = asTelegramMessage(message.reply_to_message);
|
|
161
|
+
if (typeof reply?.message_id !== "number") return void 0;
|
|
162
|
+
const author = reply.from;
|
|
163
|
+
if (author?.is_bot !== true || author.id !== botUserId) return void 0;
|
|
164
|
+
const encodedInteractionId = typeof reply.text === "string" ? reply.text.match(INTERACTION_FOOTER)?.[1] : void 0;
|
|
165
|
+
const interactionId = encodedInteractionId ? decodeInteractionId(encodedInteractionId) : void 0;
|
|
166
|
+
if (!interactionId) return void 0;
|
|
167
|
+
return {
|
|
168
|
+
type: "approval-response",
|
|
169
|
+
...eventContext(message, eventId, botUserId),
|
|
170
|
+
interactionId,
|
|
171
|
+
decision: message.text === "YES" ? "approve" : "reject",
|
|
172
|
+
reference: messageIdentity(message.chat.id, message.message_id)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function inboundEvent(update, botUserId) {
|
|
176
|
+
const edited = update.edited_message !== void 0;
|
|
177
|
+
const message = asTelegramMessage(edited ? update.edited_message : update.message);
|
|
178
|
+
if (typeof update.update_id !== "number" || typeof message?.message_id !== "number" || typeof message.text !== "string" || typeof message.chat?.id !== "number") return;
|
|
179
|
+
const eventId = updateIdentity(message.chat.id, update.update_id);
|
|
180
|
+
return approvalResponse(message, eventId, botUserId) ?? normalizedMessage(message, eventId, edited, botUserId);
|
|
181
|
+
}
|
|
182
|
+
/** Recover this bot's numeric user id from its BotFather token prefix. */
|
|
183
|
+
function telegramBotUserId(botToken) {
|
|
184
|
+
const prefix = botToken.match(/^(\d+):/)?.[1];
|
|
185
|
+
if (!prefix) return void 0;
|
|
186
|
+
const botUserId = Number(prefix);
|
|
187
|
+
return Number.isSafeInteger(botUserId) && botUserId > 0 ? botUserId : void 0;
|
|
188
|
+
}
|
|
189
|
+
/** Create dependency-free Telegram webhook ingress for a destination chat. */
|
|
190
|
+
function telegramWebhook(options) {
|
|
191
|
+
if (!options.secretToken.trim()) throw new Error("secretToken is required to create Telegram webhook ingress");
|
|
192
|
+
if (options.botUserId !== void 0 && (!Number.isSafeInteger(options.botUserId) || options.botUserId <= 0)) throw new Error("botUserId must be a positive Telegram user id");
|
|
193
|
+
const botUserId = options.botUserId;
|
|
194
|
+
const path = options.path ?? DEFAULT_TELEGRAM_WEBHOOK_PATH;
|
|
195
|
+
return { async receive(request) {
|
|
196
|
+
if (!matchesPath(request, path)) return null;
|
|
197
|
+
if (request.method !== "POST") return emptyIngressResponse(405);
|
|
198
|
+
if (request.headers.get("x-telegram-bot-api-secret-token") !== options.secretToken) return emptyIngressResponse(401);
|
|
199
|
+
let update;
|
|
200
|
+
try {
|
|
201
|
+
update = await request.json();
|
|
202
|
+
} catch {
|
|
203
|
+
return emptyIngressResponse(400);
|
|
204
|
+
}
|
|
205
|
+
if (update === null || typeof update !== "object") return emptyIngressResponse(400);
|
|
206
|
+
const raw = update;
|
|
207
|
+
const event = inboundEvent(raw, botUserId);
|
|
208
|
+
return {
|
|
209
|
+
events: event ? [{
|
|
210
|
+
event,
|
|
211
|
+
raw
|
|
212
|
+
}] : [],
|
|
213
|
+
response: new Response(null, { status: 200 })
|
|
214
|
+
};
|
|
215
|
+
} };
|
|
216
|
+
}
|
|
217
|
+
function telegramSurface(surface) {
|
|
218
|
+
if (!isChannelMessageSurface(surface) || surface.version !== 1 || !isRecord(surface.address) || typeof surface.address.chatId !== "string" || surface.address.chatId.length === 0 || surface.address.botUserId !== void 0 && (!Number.isSafeInteger(surface.address.botUserId) || Number(surface.address.botUserId) <= 0)) return;
|
|
219
|
+
if (surface.address.messageThreadId !== void 0 && !Number.isSafeInteger(surface.address.messageThreadId) || surface.address.replyToMessageId !== void 0 && !Number.isSafeInteger(surface.address.replyToMessageId)) return;
|
|
220
|
+
return surface;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Whether a chat can show an animated draft.
|
|
224
|
+
*
|
|
225
|
+
* `sendMessageDraft` is private chats only, and Telegram gives private chats
|
|
226
|
+
* positive ids. Anywhere else the answer is simply sent once at the end.
|
|
227
|
+
*/
|
|
228
|
+
function supportsDraft(chatId) {
|
|
229
|
+
const numeric = Number(chatId);
|
|
230
|
+
return Number.isSafeInteger(numeric) && numeric > 0;
|
|
231
|
+
}
|
|
232
|
+
/** A non-zero draft identifier; reusing one animates between snapshots. */
|
|
233
|
+
function newDraftId() {
|
|
234
|
+
const [random] = crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(1));
|
|
235
|
+
return (random ?? 1) % 2147483647 + 1;
|
|
236
|
+
}
|
|
237
|
+
function splitText(text, limit) {
|
|
238
|
+
const pieces = [];
|
|
239
|
+
let piece = "";
|
|
240
|
+
let length = 0;
|
|
241
|
+
for (const character of text) {
|
|
242
|
+
if (length === limit) {
|
|
243
|
+
pieces.push(piece);
|
|
244
|
+
piece = "";
|
|
245
|
+
length = 0;
|
|
246
|
+
}
|
|
247
|
+
piece += character;
|
|
248
|
+
length += 1;
|
|
249
|
+
}
|
|
250
|
+
if (piece.length > 0) pieces.push(piece);
|
|
251
|
+
return pieces;
|
|
252
|
+
}
|
|
253
|
+
function textLength(text) {
|
|
254
|
+
return [...text].length;
|
|
255
|
+
}
|
|
256
|
+
/** Create a configured Telegram Bot API Channel. */
|
|
257
|
+
function telegram(options) {
|
|
258
|
+
if (!options.botToken.trim()) throw new Error("botToken is required to create a Telegram channel");
|
|
259
|
+
const fetch = options.fetch ?? globalThis.fetch;
|
|
260
|
+
const apiBaseUrl = (options.apiBaseUrl ?? "https://api.telegram.org").replace(/\/$/, "");
|
|
261
|
+
const maxLength = options.maxLength ?? TELEGRAM_MESSAGE_LIMIT;
|
|
262
|
+
if (!Number.isInteger(maxLength) || maxLength < 1 || maxLength > TELEGRAM_MESSAGE_LIMIT) throw new Error(`maxLength must be an integer between 1 and ${TELEGRAM_MESSAGE_LIMIT}`);
|
|
263
|
+
const toText = options.toText ?? defaultText;
|
|
264
|
+
const streamIntervalMs = options.streamIntervalMs ?? DEFAULT_STREAM_INTERVAL_MS;
|
|
265
|
+
if (!Number.isInteger(streamIntervalMs) || streamIntervalMs < 0) throw new Error("streamIntervalMs must be a non-negative integer");
|
|
266
|
+
const botUserId = telegramBotUserId(options.botToken);
|
|
267
|
+
const ingress = options.webhook ? telegramWebhook({
|
|
268
|
+
...options.webhook,
|
|
269
|
+
botUserId
|
|
270
|
+
}) : void 0;
|
|
271
|
+
async function send(destinationValue, text, parseMode) {
|
|
272
|
+
const destination = telegramSurface(destinationValue);
|
|
273
|
+
if (!destination || botUserId !== void 0 && destination.address.botUserId !== void 0 && destination.address.botUserId !== botUserId) return {
|
|
274
|
+
status: "failed",
|
|
275
|
+
retryable: false,
|
|
276
|
+
error: {
|
|
277
|
+
code: "TELEGRAM_SURFACE_INVALID",
|
|
278
|
+
message: `Telegram cannot parse the address for Channel "${destinationValue.channelKey}"`
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
if (textLength(text) > maxLength) return {
|
|
282
|
+
status: "failed",
|
|
283
|
+
retryable: false,
|
|
284
|
+
error: {
|
|
285
|
+
code: "TELEGRAM_MESSAGE_TOO_LONG",
|
|
286
|
+
message: `Telegram message exceeds the configured ${maxLength}-character limit`
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
let response;
|
|
290
|
+
try {
|
|
291
|
+
response = await fetch(`${apiBaseUrl}/bot${options.botToken}/sendMessage`, {
|
|
292
|
+
method: "POST",
|
|
293
|
+
headers: { "content-type": "application/json" },
|
|
294
|
+
body: JSON.stringify({
|
|
295
|
+
chat_id: destination.address.chatId,
|
|
296
|
+
text,
|
|
297
|
+
...destination.address.messageThreadId !== void 0 && { message_thread_id: destination.address.messageThreadId },
|
|
298
|
+
...destination.address.replyToMessageId !== void 0 && { reply_parameters: { message_id: destination.address.replyToMessageId } },
|
|
299
|
+
...parseMode && { parse_mode: parseMode }
|
|
300
|
+
})
|
|
301
|
+
});
|
|
302
|
+
} catch {
|
|
303
|
+
return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram delivery failed with an unknown outcome");
|
|
304
|
+
}
|
|
305
|
+
let payload;
|
|
306
|
+
try {
|
|
307
|
+
payload = await response.json();
|
|
308
|
+
} catch {
|
|
309
|
+
return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
|
|
310
|
+
}
|
|
311
|
+
const apiResponse = asApiResponse(payload);
|
|
312
|
+
return apiResponse ? classifyResponse(response, apiResponse) : uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Show one ephemeral preview. Failures are swallowed on purpose: a draft is
|
|
316
|
+
* a 30-second animation, and losing one must never cost the real message.
|
|
317
|
+
*
|
|
318
|
+
* The configured parse mode is deliberately not applied. A partial answer
|
|
319
|
+
* routinely holds unbalanced markup, which Telegram rejects outright.
|
|
320
|
+
*/
|
|
321
|
+
async function sendDraft(destination, draftId, text) {
|
|
322
|
+
try {
|
|
323
|
+
return asApiResponse(await (await fetch(`${apiBaseUrl}/bot${options.botToken}/sendMessageDraft`, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: { "content-type": "application/json" },
|
|
326
|
+
body: JSON.stringify({
|
|
327
|
+
chat_id: Number(destination.address.chatId),
|
|
328
|
+
draft_id: draftId,
|
|
329
|
+
text,
|
|
330
|
+
...destination.address.messageThreadId !== void 0 && { message_thread_id: destination.address.messageThreadId }
|
|
331
|
+
})
|
|
332
|
+
})).json())?.ok === true;
|
|
333
|
+
} catch {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/** Persist the answer, splitting it when it outgrows one Telegram message. */
|
|
338
|
+
async function sendComplete(destination, text) {
|
|
339
|
+
const [head, ...tail] = splitText(text, maxLength);
|
|
340
|
+
const parseMode = tail.length === 0 ? options.parseMode : void 0;
|
|
341
|
+
const first = await send(destination, head, parseMode);
|
|
342
|
+
if (first.status !== "delivered") return first;
|
|
343
|
+
for (const piece of tail) if ((await send(destination, piece, parseMode)).status !== "delivered") return uncertain("TELEGRAM_STREAM_PARTIAL", "Telegram accepted only part of a split answer", first.reference);
|
|
344
|
+
return first;
|
|
345
|
+
}
|
|
346
|
+
async function streamMessage(destinationValue, chunks, streamOptions) {
|
|
347
|
+
const destination = telegramSurface(destinationValue);
|
|
348
|
+
if (!destination || botUserId !== void 0 && destination.address.botUserId !== void 0 && destination.address.botUserId !== botUserId) {
|
|
349
|
+
await chunks.cancel().catch(() => {});
|
|
350
|
+
return {
|
|
351
|
+
status: "failed",
|
|
352
|
+
retryable: false,
|
|
353
|
+
error: {
|
|
354
|
+
code: "TELEGRAM_SURFACE_INVALID",
|
|
355
|
+
message: `Telegram cannot parse the address for Channel "${destinationValue.channelKey}"`
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const draftId = supportsDraft(destination.address.chatId) ? newDraftId() : void 0;
|
|
360
|
+
const prefix = streamOptions.title ? `${streamOptions.title}\n\n` : "";
|
|
361
|
+
const shouldPreview = createPacer(streamIntervalMs);
|
|
362
|
+
let answer = "";
|
|
363
|
+
let draftsStopped = draftId === void 0;
|
|
364
|
+
return consumeChunks(chunks, {
|
|
365
|
+
async onChunk(chunk) {
|
|
366
|
+
if (chunk.type !== "text" || chunk.text.length === 0) return;
|
|
367
|
+
answer += chunk.text;
|
|
368
|
+
if (draftsStopped || !shouldPreview()) return;
|
|
369
|
+
if (!await sendDraft(destination, draftId, splitText(`${prefix}${answer}`, maxLength)[0] ?? "")) draftsStopped = true;
|
|
370
|
+
},
|
|
371
|
+
async onFinish(outcome) {
|
|
372
|
+
if (answer.length === 0) return {
|
|
373
|
+
status: "failed",
|
|
374
|
+
retryable: false,
|
|
375
|
+
error: outcome.interrupted ? {
|
|
376
|
+
code: "TELEGRAM_STREAM_INTERRUPTED",
|
|
377
|
+
message: "The answer ended before producing any text to send"
|
|
378
|
+
} : {
|
|
379
|
+
code: "TELEGRAM_STREAM_EMPTY",
|
|
380
|
+
message: "The stream carried no text to send"
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const result = await sendComplete(destination, `${prefix}${answer}`);
|
|
384
|
+
if (!outcome.interrupted || result.status !== "delivered") return result;
|
|
385
|
+
return uncertain("TELEGRAM_STREAM_INTERRUPTED", "An incomplete answer was sent because the stream ended early", result.reference);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
...options.route && { route: options.route },
|
|
391
|
+
...ingress && { ingress },
|
|
392
|
+
contactSurface(identity) {
|
|
393
|
+
if ((identity.scope ?? "default") !== "default") return null;
|
|
394
|
+
const match = identity.subject.match(/^(user|chat):(-?\d+)$/);
|
|
395
|
+
if (!match) return null;
|
|
396
|
+
return {
|
|
397
|
+
version: 1,
|
|
398
|
+
address: {
|
|
399
|
+
chatId: match[2],
|
|
400
|
+
...botUserId && { botUserId }
|
|
401
|
+
},
|
|
402
|
+
label: `Telegram · ${match[1]} ${match[2]}`
|
|
403
|
+
};
|
|
404
|
+
},
|
|
405
|
+
deliver(destination, message) {
|
|
406
|
+
return send(destination, toText(message), options.parseMode);
|
|
407
|
+
},
|
|
408
|
+
stream(destination, chunks, streamOptions) {
|
|
409
|
+
return streamMessage(destination, chunks, streamOptions);
|
|
410
|
+
},
|
|
411
|
+
requestApproval(destination, { interactionId, request }) {
|
|
412
|
+
if (interactionId.length === 0) return Promise.resolve({
|
|
413
|
+
status: "failed",
|
|
414
|
+
retryable: false,
|
|
415
|
+
error: {
|
|
416
|
+
code: "TELEGRAM_INTERACTION_ID_REQUIRED",
|
|
417
|
+
message: "Telegram approval requests require a non-empty interaction id"
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return send(destination, approvalText(interactionId, request));
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
//#endregion
|
|
425
|
+
export { telegram, telegramWebhook };
|
|
426
|
+
|
|
427
|
+
//# sourceMappingURL=telegram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.js","names":[],"sources":["../../src/channels/adapters/telegram.ts"],"sourcesContent":["import type {\n Channel,\n ChannelApprovalRequest,\n ChannelChunk,\n ChannelMessage,\n ChannelRoute,\n ChannelStreamOptions,\n DeliveryResult\n} from \"../channel\";\nimport { consumeChunks, createPacer } from \"../stream\";\nimport type { ChannelIdentity } from \"../identity\";\nimport {\n isChannelMessageSurface,\n type ChannelMessageSurface\n} from \"../surface\";\nimport {\n matchesPath,\n type ChannelApprovalResponseInput,\n type ChannelEventContextInput,\n type ChannelInboundMessageInput,\n type ChannelIngress,\n type ChannelIngressEventInput\n} from \"../ingress\";\nimport {\n defaultText,\n emptyIngressResponse,\n encodeUtf8,\n isRecord,\n renderInput,\n uncertain\n} from \"../internal\";\n\nexport type TelegramUser = {\n id: number;\n is_bot: boolean;\n first_name: string;\n last_name?: string;\n username?: string;\n [key: string]: unknown;\n};\n\nexport type TelegramChat = {\n id: number;\n type: \"private\" | \"group\" | \"supergroup\" | \"channel\";\n title?: string;\n username?: string;\n first_name?: string;\n last_name?: string;\n [key: string]: unknown;\n};\n\nexport type TelegramMessage = {\n message_id: number;\n date: number;\n chat: TelegramChat;\n message_thread_id?: number;\n from?: TelegramUser;\n sender_chat?: TelegramChat;\n text?: string;\n edit_date?: number;\n reply_to_message?: TelegramMessage;\n [key: string]: unknown;\n};\n\n/** Authenticated JSON body delivered by a Telegram webhook. */\nexport type TelegramUpdate = {\n update_id: number;\n message?: TelegramMessage;\n edited_message?: TelegramMessage;\n [key: string]: unknown;\n};\n\nexport type TelegramMessageSurface = ChannelMessageSurface<\n string,\n {\n chatId: string;\n botUserId?: number;\n messageThreadId?: number;\n replyToMessageId?: number;\n }\n>;\n\n/** Configuration for a Telegram Channel. */\nexport type TelegramChannelOptions = {\n /** Bot token issued by BotFather. */\n botToken: string;\n /** Telegram Bot API origin. @default \"https://api.telegram.org\" */\n apiBaseUrl?: string;\n /** Maximum text length accepted by this route. @default 4096 */\n maxLength?: number;\n /** Project the canonical message into Telegram text. */\n toText?: (message: ChannelMessage) => string;\n /** Parse mode for caller-formatted delivery text. */\n parseMode?: \"HTML\" | \"MarkdownV2\";\n /**\n * Smallest gap between `sendMessageDraft` previews. Snapshots produced\n * inside one interval are replaced rather than sent. @default 500\n */\n streamIntervalMs?: number;\n /** Select an application route from the event, raw update, and Host context. */\n route?: ChannelRoute<TelegramUpdate>;\n /** Add secret-verified Telegram webhook ingress to the returned Channel. */\n webhook?: {\n secretToken: string;\n /** Exact webhook pathname accepted by this ingress. @default \"/webhooks/telegram\" */\n path?: string;\n };\n /** Override fetch for testing or custom network routing. */\n fetch?: typeof globalThis.fetch;\n};\n\ntype TelegramApiResponse = {\n ok?: unknown;\n result?: unknown;\n error_code?: unknown;\n description?: unknown;\n};\n\nconst TELEGRAM_MESSAGE_LIMIT = 4096;\nconst DEFAULT_STREAM_INTERVAL_MS = 500;\nconst DEFAULT_TELEGRAM_WEBHOOK_PATH = \"/webhooks/telegram\";\nconst APPROVAL_INSTRUCTIONS = \"Reply YES to approve or NO to reject.\";\nconst INTERACTION_FOOTER =\n /\\n\\nReply YES to approve or NO to reject\\.\\n\\n\\[channel-interaction:v1:([A-Za-z0-9_-]+)\\]$/;\n\nfunction channelIdentity(chatId: number): string {\n return `telegram:chat:${chatId}`;\n}\n\nfunction userIdentity(userId: number): string {\n return `telegram:user:${userId}`;\n}\n\nfunction messageIdentity(chatId: number, messageId: number): string {\n return `${channelIdentity(chatId)}:message:${messageId}`;\n}\n\nfunction updateIdentity(chatId: number, updateId: number): string {\n return `${channelIdentity(chatId)}:update:${updateId}`;\n}\n\nfunction encodeInteractionId(interactionId: string): string {\n const bytes = encodeUtf8(interactionId);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction decodeInteractionId(value: string): string | undefined {\n try {\n const base64 = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(base64 + padding);\n const bytes = Uint8Array.from(binary, (character) =>\n character.charCodeAt(0)\n );\n const interactionId = new TextDecoder(\"utf-8\", { fatal: true }).decode(\n bytes\n );\n return interactionId || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction approvalText(\n interactionId: string,\n request: ChannelApprovalRequest\n): string {\n return [\n ...(request.title ? [request.title] : []),\n request.summary,\n `Input:\\n${renderInput(request.input)}`,\n APPROVAL_INSTRUCTIONS,\n `[channel-interaction:v1:${encodeInteractionId(interactionId)}]`\n ].join(\"\\n\\n\");\n}\n\nfunction asApiResponse(value: unknown): TelegramApiResponse | undefined {\n return value !== null && typeof value === \"object\"\n ? (value as TelegramApiResponse)\n : undefined;\n}\n\nfunction classifyResponse(\n response: Response,\n payload: TelegramApiResponse\n): DeliveryResult {\n if (payload.ok === true) {\n const result = asApiResponse(payload.result) as\n | { message_id?: unknown }\n | undefined;\n if (typeof result?.message_id === \"number\") {\n return { status: \"delivered\", reference: String(result.message_id) };\n }\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n }\n\n if (payload.ok === false) {\n const errorCode =\n typeof payload.error_code === \"number\"\n ? payload.error_code\n : response.status;\n const message =\n typeof payload.description === \"string\"\n ? payload.description\n : \"Telegram rejected the message\";\n const error = { code: `TELEGRAM_API_ERROR_${errorCode}`, message };\n if (errorCode >= 500) return { status: \"uncertain\", error };\n return {\n status: \"failed\",\n retryable: errorCode === 429,\n error\n };\n }\n\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n}\n\nexport type TelegramWebhookOptions = {\n /** Telegram webhook secret configured through setWebhook. */\n secretToken: string;\n /** Exact webhook pathname accepted by this ingress. @default \"/webhooks/telegram\" */\n path?: string;\n /**\n * Numeric user id of this bot, used to prove that an approval reply answers\n * a message this bot sent. Without it, marker replies stay ordinary\n * messages because their provenance cannot be established.\n */\n botUserId?: number;\n};\n\nfunction asTelegramMessage(value: unknown): TelegramMessage | undefined {\n return value !== null && typeof value === \"object\"\n ? (value as TelegramMessage)\n : undefined;\n}\n\nfunction telegramActor(message: TelegramMessage) {\n const sender = message.from;\n if (sender) {\n const fullName = [sender.first_name, sender.last_name]\n .filter(Boolean)\n .join(\" \");\n return {\n id: userIdentity(sender.id),\n identity: {\n subject: `user:${sender.id}`\n },\n ...(sender.username && { username: sender.username }),\n ...(fullName && { fullName }),\n isBot: sender.is_bot\n };\n }\n\n const senderChat = message.sender_chat;\n if (!senderChat) return undefined;\n const fullName =\n senderChat.title ??\n [senderChat.first_name, senderChat.last_name].filter(Boolean).join(\" \");\n return {\n id: channelIdentity(senderChat.id),\n identity: {\n subject: `chat:${senderChat.id}`\n },\n ...(senderChat.username && { username: senderChat.username }),\n ...(fullName && { fullName }),\n isBot: \"unknown\" as const\n };\n}\n\nfunction chatLabel(chat: TelegramChat): string {\n const name =\n chat.title ??\n (chat.username ? `@${chat.username}` : undefined) ??\n [chat.first_name, chat.last_name].filter(Boolean).join(\" \") ??\n String(chat.id);\n return `Telegram · ${name || chat.id}`;\n}\n\nfunction eventContext(\n message: TelegramMessage,\n eventId: string,\n botUserId: number | undefined\n): ChannelEventContextInput {\n const actor = telegramActor(message);\n const channelId = channelIdentity(message.chat.id);\n const threadId =\n typeof message.message_thread_id === \"number\"\n ? `${channelId}:topic:${message.message_thread_id}`\n : channelId;\n const isDirectMessage =\n message.chat.type === \"private\"\n ? true\n : message.chat.type === \"group\" ||\n message.chat.type === \"supergroup\" ||\n message.chat.type === \"channel\"\n ? false\n : (\"unknown\" as const);\n return {\n eventId,\n thread: { id: threadId, isDirectMessage },\n replySurface: {\n version: 1,\n address: {\n chatId: String(message.chat.id),\n ...(botUserId && { botUserId }),\n ...(typeof message.message_thread_id === \"number\" && {\n messageThreadId: message.message_thread_id\n }),\n replyToMessageId: message.message_id\n },\n label: chatLabel(message.chat)\n },\n ...(actor && { actor })\n };\n}\n\nfunction telegramTimestamp(value: unknown): string | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return undefined;\n const date = new Date(value * 1000);\n return Number.isFinite(date.getTime()) ? date.toISOString() : undefined;\n}\n\nfunction normalizedMessage(\n message: TelegramMessage,\n eventId: string,\n edited: boolean,\n botUserId: number | undefined\n): ChannelInboundMessageInput {\n const reply = asTelegramMessage(message.reply_to_message);\n const sentAt = telegramTimestamp(message.date);\n const editedAt = telegramTimestamp(message.edit_date);\n\n return {\n type: \"message\",\n ...eventContext(message, eventId, botUserId),\n message: {\n id: messageIdentity(message.chat.id, message.message_id),\n text: message.text as string,\n ...(typeof reply?.message_id === \"number\" && {\n reply: {\n id: messageIdentity(message.chat.id, reply.message_id),\n ...(typeof reply.text === \"string\" && { text: reply.text })\n }\n }),\n ...((sentAt || edited || editedAt) && {\n metadata: {\n ...(sentAt && { sentAt }),\n ...(edited && { edited: true }),\n ...(editedAt && { editedAt })\n }\n })\n }\n };\n}\n\nfunction approvalResponse(\n message: TelegramMessage,\n eventId: string,\n botUserId: number | undefined\n): ChannelApprovalResponseInput | undefined {\n if (botUserId === undefined) return undefined;\n if (message.text !== \"YES\" && message.text !== \"NO\") return undefined;\n\n const reply = asTelegramMessage(message.reply_to_message);\n if (typeof reply?.message_id !== \"number\") return undefined;\n\n // An interaction marker only authorizes a decision when this bot authored\n // the replied-to message; otherwise any chat member could forge one.\n const author = reply.from;\n if (author?.is_bot !== true || author.id !== botUserId) return undefined;\n\n const encodedInteractionId =\n typeof reply.text === \"string\"\n ? reply.text.match(INTERACTION_FOOTER)?.[1]\n : undefined;\n const interactionId = encodedInteractionId\n ? decodeInteractionId(encodedInteractionId)\n : undefined;\n if (!interactionId) return undefined;\n\n return {\n type: \"approval-response\",\n ...eventContext(message, eventId, botUserId),\n interactionId,\n decision: message.text === \"YES\" ? \"approve\" : \"reject\",\n reference: messageIdentity(message.chat.id, message.message_id)\n };\n}\n\nfunction inboundEvent(\n update: TelegramUpdate,\n botUserId: number | undefined\n): ChannelIngressEventInput | undefined {\n const edited = update.edited_message !== undefined;\n const message = asTelegramMessage(\n edited ? update.edited_message : update.message\n );\n if (\n typeof update.update_id !== \"number\" ||\n typeof message?.message_id !== \"number\" ||\n typeof message.text !== \"string\" ||\n typeof message.chat?.id !== \"number\"\n ) {\n return undefined;\n }\n\n const eventId = updateIdentity(message.chat.id, update.update_id);\n return (\n approvalResponse(message, eventId, botUserId) ??\n normalizedMessage(message, eventId, edited, botUserId)\n );\n}\n\n/** Recover this bot's numeric user id from its BotFather token prefix. */\nfunction telegramBotUserId(botToken: string): number | undefined {\n const prefix = botToken.match(/^(\\d+):/)?.[1];\n if (!prefix) return undefined;\n const botUserId = Number(prefix);\n return Number.isSafeInteger(botUserId) && botUserId > 0\n ? botUserId\n : undefined;\n}\n\n/** Create dependency-free Telegram webhook ingress for a destination chat. */\nexport function telegramWebhook(\n options: TelegramWebhookOptions\n): ChannelIngress<TelegramUpdate> {\n if (!options.secretToken.trim()) {\n throw new Error(\n \"secretToken is required to create Telegram webhook ingress\"\n );\n }\n if (\n options.botUserId !== undefined &&\n (!Number.isSafeInteger(options.botUserId) || options.botUserId <= 0)\n ) {\n throw new Error(\"botUserId must be a positive Telegram user id\");\n }\n\n const botUserId = options.botUserId;\n const path = options.path ?? DEFAULT_TELEGRAM_WEBHOOK_PATH;\n return {\n async receive(request) {\n if (!matchesPath(request, path)) return null;\n if (request.method !== \"POST\") return emptyIngressResponse(405);\n if (\n request.headers.get(\"x-telegram-bot-api-secret-token\") !==\n options.secretToken\n ) {\n return emptyIngressResponse(401);\n }\n\n let update: unknown;\n try {\n update = await request.json();\n } catch {\n return emptyIngressResponse(400);\n }\n if (update === null || typeof update !== \"object\") {\n return emptyIngressResponse(400);\n }\n\n const raw = update as TelegramUpdate;\n const event = inboundEvent(raw, botUserId);\n return {\n events: event ? [{ event, raw }] : [],\n response: new Response(null, { status: 200 })\n };\n }\n };\n}\n\nfunction telegramSurface(\n surface: ChannelMessageSurface\n): TelegramMessageSurface | undefined {\n if (\n !isChannelMessageSurface(surface) ||\n surface.version !== 1 ||\n !isRecord(surface.address) ||\n typeof surface.address.chatId !== \"string\" ||\n surface.address.chatId.length === 0 ||\n (surface.address.botUserId !== undefined &&\n (!Number.isSafeInteger(surface.address.botUserId) ||\n Number(surface.address.botUserId) <= 0))\n ) {\n return undefined;\n }\n if (\n (surface.address.messageThreadId !== undefined &&\n !Number.isSafeInteger(surface.address.messageThreadId)) ||\n (surface.address.replyToMessageId !== undefined &&\n !Number.isSafeInteger(surface.address.replyToMessageId))\n ) {\n return undefined;\n }\n return surface as TelegramMessageSurface;\n}\n\n/**\n * Whether a chat can show an animated draft.\n *\n * `sendMessageDraft` is private chats only, and Telegram gives private chats\n * positive ids. Anywhere else the answer is simply sent once at the end.\n */\nfunction supportsDraft(chatId: string): boolean {\n const numeric = Number(chatId);\n return Number.isSafeInteger(numeric) && numeric > 0;\n}\n\n/** A non-zero draft identifier; reusing one animates between snapshots. */\nfunction newDraftId(): number {\n const [random] = crypto.getRandomValues(new Uint32Array(1));\n return ((random ?? 1) % 2_147_483_647) + 1;\n}\n\nfunction splitText(text: string, limit: number): string[] {\n const pieces: string[] = [];\n let piece = \"\";\n let length = 0;\n for (const character of text) {\n if (length === limit) {\n pieces.push(piece);\n piece = \"\";\n length = 0;\n }\n piece += character;\n length += 1;\n }\n if (piece.length > 0) pieces.push(piece);\n return pieces;\n}\n\nfunction textLength(text: string): number {\n return [...text].length;\n}\n\n/** Create a configured Telegram Bot API Channel. */\nexport function telegram(\n options: TelegramChannelOptions\n): Channel<TelegramUpdate> {\n if (!options.botToken.trim()) {\n throw new Error(\"botToken is required to create a Telegram channel\");\n }\n const fetch = options.fetch ?? globalThis.fetch;\n const apiBaseUrl = (options.apiBaseUrl ?? \"https://api.telegram.org\").replace(\n /\\/$/,\n \"\"\n );\n const maxLength = options.maxLength ?? TELEGRAM_MESSAGE_LIMIT;\n if (\n !Number.isInteger(maxLength) ||\n maxLength < 1 ||\n maxLength > TELEGRAM_MESSAGE_LIMIT\n ) {\n throw new Error(\n `maxLength must be an integer between 1 and ${TELEGRAM_MESSAGE_LIMIT}`\n );\n }\n const toText = options.toText ?? defaultText;\n const streamIntervalMs =\n options.streamIntervalMs ?? DEFAULT_STREAM_INTERVAL_MS;\n if (!Number.isInteger(streamIntervalMs) || streamIntervalMs < 0) {\n throw new Error(\"streamIntervalMs must be a non-negative integer\");\n }\n const botUserId = telegramBotUserId(options.botToken);\n const ingress = options.webhook\n ? telegramWebhook({ ...options.webhook, botUserId })\n : undefined;\n\n async function send(\n destinationValue: ChannelMessageSurface,\n text: string,\n parseMode?: TelegramChannelOptions[\"parseMode\"]\n ): Promise<DeliveryResult> {\n const destination = telegramSurface(destinationValue);\n if (\n !destination ||\n (botUserId !== undefined &&\n destination.address.botUserId !== undefined &&\n destination.address.botUserId !== botUserId)\n ) {\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_SURFACE_INVALID\",\n message: `Telegram cannot parse the address for Channel \"${destinationValue.channelKey}\"`\n }\n };\n }\n if (textLength(text) > maxLength) {\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_MESSAGE_TOO_LONG\",\n message: `Telegram message exceeds the configured ${maxLength}-character limit`\n }\n };\n }\n\n let response: Response;\n try {\n response = await fetch(\n `${apiBaseUrl}/bot${options.botToken}/sendMessage`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: destination.address.chatId,\n text,\n ...(destination.address.messageThreadId !== undefined && {\n message_thread_id: destination.address.messageThreadId\n }),\n ...(destination.address.replyToMessageId !== undefined && {\n reply_parameters: {\n message_id: destination.address.replyToMessageId\n }\n }),\n ...(parseMode && { parse_mode: parseMode })\n })\n }\n );\n } catch {\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram delivery failed with an unknown outcome\"\n );\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n }\n\n const apiResponse = asApiResponse(payload);\n return apiResponse\n ? classifyResponse(response, apiResponse)\n : uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n }\n\n /**\n * Show one ephemeral preview. Failures are swallowed on purpose: a draft is\n * a 30-second animation, and losing one must never cost the real message.\n *\n * The configured parse mode is deliberately not applied. A partial answer\n * routinely holds unbalanced markup, which Telegram rejects outright.\n */\n async function sendDraft(\n destination: TelegramMessageSurface,\n draftId: number,\n text: string\n ): Promise<boolean> {\n try {\n const response = await fetch(\n `${apiBaseUrl}/bot${options.botToken}/sendMessageDraft`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: Number(destination.address.chatId),\n draft_id: draftId,\n text,\n ...(destination.address.messageThreadId !== undefined && {\n message_thread_id: destination.address.messageThreadId\n })\n })\n }\n );\n return asApiResponse(await response.json())?.ok === true;\n } catch {\n return false;\n }\n }\n\n /** Persist the answer, splitting it when it outgrows one Telegram message. */\n async function sendComplete(\n destination: ChannelMessageSurface,\n text: string\n ): Promise<DeliveryResult> {\n const [head, ...tail] = splitText(text, maxLength);\n // Raw HTML and Markdown cannot be partitioned safely without parsing it.\n // Preserve all content literally rather than send malformed fragments.\n const parseMode = tail.length === 0 ? options.parseMode : undefined;\n const first = await send(destination, head!, parseMode);\n if (first.status !== \"delivered\") return first;\n\n for (const piece of tail) {\n const result = await send(destination, piece, parseMode);\n // A later piece failing still leaves earlier ones in the chat.\n if (result.status !== \"delivered\") {\n return uncertain(\n \"TELEGRAM_STREAM_PARTIAL\",\n \"Telegram accepted only part of a split answer\",\n first.reference\n );\n }\n }\n return first;\n }\n\n async function streamMessage(\n destinationValue: ChannelMessageSurface,\n chunks: ReadableStream<ChannelChunk>,\n streamOptions: ChannelStreamOptions\n ): Promise<DeliveryResult> {\n const destination = telegramSurface(destinationValue);\n if (\n !destination ||\n (botUserId !== undefined &&\n destination.address.botUserId !== undefined &&\n destination.address.botUserId !== botUserId)\n ) {\n await chunks.cancel().catch(() => {});\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_SURFACE_INVALID\",\n message: `Telegram cannot parse the address for Channel \"${destinationValue.channelKey}\"`\n }\n };\n }\n\n const draftId = supportsDraft(destination.address.chatId)\n ? newDraftId()\n : undefined;\n const prefix = streamOptions.title ? `${streamOptions.title}\\n\\n` : \"\";\n const shouldPreview = createPacer(streamIntervalMs);\n let answer = \"\";\n let draftsStopped = draftId === undefined;\n\n return consumeChunks(chunks, {\n async onChunk(chunk) {\n if (chunk.type !== \"text\" || chunk.text.length === 0) return;\n answer += chunk.text;\n if (draftsStopped || !shouldPreview()) return;\n const preview = splitText(`${prefix}${answer}`, maxLength)[0] ?? \"\";\n const shown = await sendDraft(destination, draftId!, preview);\n if (!shown) draftsStopped = true;\n },\n async onFinish(outcome) {\n // The draft is not the message. Without this send the reader's screen\n // goes blank in thirty seconds and nothing is kept.\n if (answer.length === 0) {\n return {\n status: \"failed\",\n retryable: false,\n error: outcome.interrupted\n ? {\n code: \"TELEGRAM_STREAM_INTERRUPTED\",\n message: \"The answer ended before producing any text to send\"\n }\n : {\n code: \"TELEGRAM_STREAM_EMPTY\",\n message: \"The stream carried no text to send\"\n }\n };\n }\n\n const result = await sendComplete(destination, `${prefix}${answer}`);\n if (!outcome.interrupted || result.status !== \"delivered\") {\n return result;\n }\n return uncertain(\n \"TELEGRAM_STREAM_INTERRUPTED\",\n \"An incomplete answer was sent because the stream ended early\",\n result.reference\n );\n }\n });\n }\n\n return {\n ...(options.route && { route: options.route }),\n ...(ingress && { ingress }),\n contactSurface(identity: ChannelIdentity) {\n if ((identity.scope ?? \"default\") !== \"default\") return null;\n const match = identity.subject.match(/^(user|chat):(-?\\d+)$/);\n if (!match) return null;\n return {\n version: 1,\n address: {\n chatId: match[2],\n ...(botUserId && { botUserId })\n },\n label: `Telegram · ${match[1]} ${match[2]}`\n };\n },\n deliver(destination, message) {\n return send(destination, toText(message), options.parseMode);\n },\n stream(destination, chunks, streamOptions) {\n return streamMessage(destination, chunks, streamOptions);\n },\n requestApproval(destination, { interactionId, request }) {\n if (interactionId.length === 0) {\n return Promise.resolve({\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_INTERACTION_ID_REQUIRED\",\n message:\n \"Telegram approval requests require a non-empty interaction id\"\n }\n });\n }\n return send(destination, approvalText(interactionId, request));\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
F as ChannelMessageSurface,
|
|
3
|
+
n as Channel,
|
|
4
|
+
u as ChannelMessage
|
|
5
|
+
} from "../channel-Bnm4S7T2.js";
|
|
6
|
+
import { c as VoiceAudioFormat, n as TTSProvider } from "../types-_Faxb570.js";
|
|
7
|
+
|
|
8
|
+
//#region src/channels/adapters/voice.d.ts
|
|
9
|
+
type BrowserVoiceSurface = ChannelMessageSurface<
|
|
10
|
+
string,
|
|
11
|
+
{
|
|
12
|
+
connection: "current";
|
|
13
|
+
}
|
|
14
|
+
>;
|
|
15
|
+
/** The subset of an Agent WebSocket connection needed for audio delivery. */
|
|
16
|
+
type BrowserVoiceConnection = {
|
|
17
|
+
id: string;
|
|
18
|
+
send(message: string | ArrayBuffer | ArrayBufferView): void;
|
|
19
|
+
};
|
|
20
|
+
/** Configuration for an output-only browser voice channel. */
|
|
21
|
+
type BrowserVoiceChannelOptions = {
|
|
22
|
+
/** Text-to-speech provider used to synthesize each outbound message. */ tts: TTSProvider /** Return the browser voice surface to use, or undefined when unavailable. */;
|
|
23
|
+
getConnection: () =>
|
|
24
|
+
| BrowserVoiceConnection
|
|
25
|
+
| undefined /** Encoded audio format returned by the TTS provider. @default "mp3" */;
|
|
26
|
+
audioFormat?: VoiceAudioFormat /** Sample rate for raw PCM audio. Ignored by encoded formats. @default 16000 */;
|
|
27
|
+
sampleRate?: number /** Project the canonical Markdown message into text suitable for speech. */;
|
|
28
|
+
toSpeechText?: (message: ChannelMessage) => string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Create an output-only channel that synthesizes one message and sends its
|
|
32
|
+
* encoded audio to one browser using the Cloudflare Voice wire protocol.
|
|
33
|
+
*
|
|
34
|
+
* The browser should connect with `VoiceClient` or `useVoiceAgent()`. No
|
|
35
|
+
* `withVoice()` mixin, microphone, or speech-to-text provider is required.
|
|
36
|
+
*/
|
|
37
|
+
declare function browserVoice(options: BrowserVoiceChannelOptions): Channel;
|
|
38
|
+
//#endregion
|
|
39
|
+
export {
|
|
40
|
+
BrowserVoiceChannelOptions,
|
|
41
|
+
BrowserVoiceConnection,
|
|
42
|
+
BrowserVoiceSurface,
|
|
43
|
+
browserVoice
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=voice.d.ts.map
|