experimental-ash 0.27.0 → 0.28.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/CHANGELOG.md +7 -0
- package/dist/docs/public/channels/README.md +50 -2
- package/dist/docs/public/channels/discord.md +15 -7
- package/dist/docs/public/channels/teams.md +121 -0
- package/dist/docs/public/channels/telegram.md +201 -0
- package/dist/docs/public/typescript-api.md +41 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/public/channels/teams/api.d.ts +140 -0
- package/dist/src/public/channels/teams/api.js +4 -0
- package/dist/src/public/channels/teams/attachments.d.ts +25 -0
- package/dist/src/public/channels/teams/attachments.js +1 -0
- package/dist/src/public/channels/teams/defaults.d.ts +24 -0
- package/dist/src/public/channels/teams/defaults.js +3 -0
- package/dist/src/public/channels/teams/hitl.d.ts +36 -0
- package/dist/src/public/channels/teams/hitl.js +1 -0
- package/dist/src/public/channels/teams/inbound.d.ts +80 -0
- package/dist/src/public/channels/teams/inbound.js +3 -0
- package/dist/src/public/channels/teams/index.d.ts +8 -0
- package/dist/src/public/channels/teams/index.js +1 -0
- package/dist/src/public/channels/teams/limits.d.ts +8 -0
- package/dist/src/public/channels/teams/limits.js +1 -0
- package/dist/src/public/channels/teams/teamsChannel.d.ts +162 -0
- package/dist/src/public/channels/teams/teamsChannel.js +1 -0
- package/dist/src/public/channels/teams/verify.d.ts +43 -0
- package/dist/src/public/channels/teams/verify.js +1 -0
- package/dist/src/public/channels/telegram/api.d.ts +107 -0
- package/dist/src/public/channels/telegram/api.js +2 -0
- package/dist/src/public/channels/telegram/attachments.d.ts +23 -0
- package/dist/src/public/channels/telegram/attachments.js +1 -0
- package/dist/src/public/channels/telegram/defaults.d.ts +9 -0
- package/dist/src/public/channels/telegram/defaults.js +3 -0
- package/dist/src/public/channels/telegram/hitl.d.ts +44 -0
- package/dist/src/public/channels/telegram/hitl.js +1 -0
- package/dist/src/public/channels/telegram/inbound.d.ts +88 -0
- package/dist/src/public/channels/telegram/inbound.js +2 -0
- package/dist/src/public/channels/telegram/index.d.ts +8 -0
- package/dist/src/public/channels/telegram/index.js +1 -0
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +126 -0
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -0
- package/dist/src/public/channels/telegram/verify.d.ts +30 -0
- package/dist/src/public/channels/telegram/verify.js +1 -0
- package/package.json +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# experimental-ash
|
|
2
2
|
|
|
3
|
+
## 0.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2e8ebcb: Add a native Microsoft Teams channel backed by the Bot Framework Activity and Connector protocols, including inbound verification, Teams conversation state, Adaptive Card HITL, proactive receive support, docs, tests, and smoke coverage.
|
|
8
|
+
- 70018bd: Add a native Telegram channel for verified Bot API webhooks, Telegram delivery, HITL interactions, attachments, and proactive sessions.
|
|
9
|
+
|
|
3
10
|
## 0.27.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Channels"
|
|
3
|
-
description: "Deliver your agent over HTTP, Slack, Discord, Twilio, and custom transports."
|
|
3
|
+
description: "Deliver your agent over HTTP, Slack, Discord, Twilio, Telegram, Microsoft Teams, and custom transports."
|
|
4
4
|
url: /channels
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ Ash ships the public HTTP protocol as channels:
|
|
|
23
23
|
- `ashChannel({ auth })` for the built-in Ash protocol channel
|
|
24
24
|
|
|
25
25
|
Ash also supports authored channels under `agent/channels/` for platform-specific integrations such
|
|
26
|
-
as Slack, Discord, Twilio, or custom webhooks.
|
|
26
|
+
as Slack, Discord, Twilio, Telegram, Microsoft Teams, or custom webhooks.
|
|
27
27
|
|
|
28
28
|
## Filesystem Shape
|
|
29
29
|
|
|
@@ -279,6 +279,53 @@ resolver when the allowed phone numbers come from dynamic state. Use `allowFrom:
|
|
|
279
279
|
|
|
280
280
|
See [Twilio channel setup](./twilio.md) for webhook URLs, environment variables, and overrides.
|
|
281
281
|
|
|
282
|
+
## Telegram Channels
|
|
283
|
+
|
|
284
|
+
Telegram channels are authored with `telegramChannel()`:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import { telegramChannel } from "experimental-ash/channels/telegram";
|
|
288
|
+
|
|
289
|
+
export default telegramChannel({
|
|
290
|
+
botUsername: "my_bot",
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The channel verifies Telegram's `X-Telegram-Bot-Api-Secret-Token` webhook header, accepts updates at
|
|
295
|
+
`/ash/v1/telegram`, dispatches private messages and addressed group messages, and resolves HITL
|
|
296
|
+
inline-keyboard callbacks and ForceReply answers back into Ash input responses. Private chats use
|
|
297
|
+
chat-wide continuation tokens. Group, supergroup, and forum-topic sessions include the chat id,
|
|
298
|
+
optional `message_thread_id`, and a conversation anchor so multiple bot conversations in the same
|
|
299
|
+
chat do not collapse.
|
|
300
|
+
|
|
301
|
+
Default delivery sends plain text through Telegram `sendMessage` with no `parse_mode`, splits text
|
|
302
|
+
at Telegram's 4096-character limit, and uses best-effort `sendChatAction("typing")` progress
|
|
303
|
+
indicators. Photos and documents are exposed as file parts and fetched through Telegram `getFile`
|
|
304
|
+
when the model needs the bytes.
|
|
305
|
+
|
|
306
|
+
See [Telegram channel setup](./telegram.md) for webhook registration, environment variables, group
|
|
307
|
+
privacy notes, HITL behavior, attachments, and proactive sessions.
|
|
308
|
+
|
|
309
|
+
## Microsoft Teams Channels
|
|
310
|
+
|
|
311
|
+
Teams channels are authored with `teamsChannel()`:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { teamsChannel } from "experimental-ash/channels/teams";
|
|
315
|
+
|
|
316
|
+
export default teamsChannel();
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
The channel verifies Bot Connector bearer JWTs, accepts Teams Bot Framework Activity POSTs at
|
|
320
|
+
`/ash/v1/teams`, dispatches personal messages and direct bot mentions, renders HITL prompts as
|
|
321
|
+
Adaptive Cards, and sends agent replies through the Bot Framework Connector REST API. Personal chats
|
|
322
|
+
resume by conversation id; channel and group-chat threads resume by conversation id plus root
|
|
323
|
+
activity id.
|
|
324
|
+
|
|
325
|
+
Proactive `receive(teams, args)` sessions require an existing conversation reference
|
|
326
|
+
(`serviceUrl` and `conversationId`). See [Microsoft Teams channel setup](./teams.md) for Azure Bot
|
|
327
|
+
setup, environment variables, proactive handoff, HITL, and file options.
|
|
328
|
+
|
|
282
329
|
## Cross-Channel Hand-off
|
|
283
330
|
|
|
284
331
|
Route handlers can start a session on a different channel via `args.receive(channel, ...)`.
|
|
@@ -472,6 +519,7 @@ See [Channel file uploads](./attachments.md) for the full guide.
|
|
|
472
519
|
## What To Read Next
|
|
473
520
|
|
|
474
521
|
- [Slack channel setup](./slack.md)
|
|
522
|
+
- [Telegram channel setup](./telegram.md)
|
|
475
523
|
- [Channel file uploads](./attachments.md)
|
|
476
524
|
- [Project Layout](../project-layout.md)
|
|
477
525
|
- [TypeScript API](../typescript-api.md)
|
|
@@ -132,19 +132,27 @@ Component and modal submissions resume the parked Ash session automatically.
|
|
|
132
132
|
|
|
133
133
|
## Proactive Sessions
|
|
134
134
|
|
|
135
|
-
Use `receive(discord, args)` from
|
|
135
|
+
Use `receive(discord, { message, args, auth })` from a schedule `run` handler, or
|
|
136
|
+
`args.receive(discord, ...)` from another channel, to start a Discord session:
|
|
136
137
|
|
|
137
138
|
```ts
|
|
138
|
-
import { defineSchedule
|
|
139
|
+
import { defineSchedule } from "experimental-ash/schedules";
|
|
139
140
|
import discord from "../channels/discord.js";
|
|
140
141
|
|
|
141
142
|
export default defineSchedule({
|
|
142
143
|
cron: "0 9 * * 1-5",
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
async run({ receive, waitUntil, appAuth }) {
|
|
145
|
+
waitUntil(
|
|
146
|
+
receive(discord, {
|
|
147
|
+
message: "Post the daily summary.",
|
|
148
|
+
args: {
|
|
149
|
+
channelId: "123456789012345678",
|
|
150
|
+
initialMessage: "Daily summary",
|
|
151
|
+
},
|
|
152
|
+
auth: appAuth,
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
},
|
|
148
156
|
});
|
|
149
157
|
```
|
|
150
158
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Microsoft Teams channel setup"
|
|
3
|
+
description: "Create a Microsoft Teams-backed Ash channel with the Bot Framework Activity protocol."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Microsoft Teams Channel Setup
|
|
7
|
+
|
|
8
|
+
The Teams channel accepts Bot Framework Activity POSTs from Microsoft Teams, verifies Bot
|
|
9
|
+
Connector bearer JWTs, dispatches message activities to Ash, renders HITL prompts as Adaptive
|
|
10
|
+
Cards, and delivers agent responses through the Bot Framework Connector REST API.
|
|
11
|
+
|
|
12
|
+
## Add The Channel
|
|
13
|
+
|
|
14
|
+
Create `agent/channels/teams.ts`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { teamsChannel } from "experimental-ash/channels/teams";
|
|
18
|
+
|
|
19
|
+
export default teamsChannel();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Set the credentials Ash needs:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
MICROSOFT_APP_ID=...
|
|
26
|
+
MICROSOFT_APP_PASSWORD=...
|
|
27
|
+
# Optional for single-tenant bots:
|
|
28
|
+
MICROSOFT_TENANT_ID=...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
By default, the channel mounts `POST /ash/v1/teams`. Configure your Azure Bot or Teams app
|
|
32
|
+
messaging endpoint to that public URL.
|
|
33
|
+
|
|
34
|
+
Use `route` when the bot endpoint needs a different path:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
export default teamsChannel({
|
|
38
|
+
route: "/api/teams/activity",
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Message Dispatch
|
|
43
|
+
|
|
44
|
+
The default `onMessage` behavior dispatches personal-chat messages and channel/group-chat messages
|
|
45
|
+
that directly mention the bot. Ambient messages delivered through Teams resource-specific consent
|
|
46
|
+
are parsed but ignored unless you override `onMessage`.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { teamsChannel, defaultTeamsAuth } from "experimental-ash/channels/teams";
|
|
50
|
+
|
|
51
|
+
export default teamsChannel({
|
|
52
|
+
onMessage(ctx, message) {
|
|
53
|
+
if (message.scope !== "personal" && !message.isBotMentioned) return null;
|
|
54
|
+
return { auth: defaultTeamsAuth(message) };
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Ash strips the bot mention from channel/group prompts, adds a compact `<teams_context>` block, and
|
|
60
|
+
keeps personal chats scoped to one Ash session per Teams conversation. Channel and group-chat
|
|
61
|
+
threads are scoped by the root activity id (`replyToId ?? id`).
|
|
62
|
+
|
|
63
|
+
## Delivery And HITL
|
|
64
|
+
|
|
65
|
+
Default delivery posts Markdown messages with `textFormat: "markdown"`, splits oversized text, and
|
|
66
|
+
sends typing indicators on turn start and action requests. `input.requested` renders Adaptive Cards:
|
|
67
|
+
buttons/options use `Action.Submit`, select requests use `Input.ChoiceSet`, and freeform requests
|
|
68
|
+
use `Input.Text`.
|
|
69
|
+
|
|
70
|
+
Adaptive Card submit activities are converted into Ash `inputResponses` automatically. Non-HITL
|
|
71
|
+
invoke activities can be handled with `onInvoke(ctx, activity)`.
|
|
72
|
+
|
|
73
|
+
## Proactive Sessions
|
|
74
|
+
|
|
75
|
+
Use `receive(teams, args)` only when you already have a Teams conversation reference. Teams v1 does
|
|
76
|
+
not create new chats by AAD user id.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { defineSchedule, receive } from "experimental-ash/schedules";
|
|
80
|
+
import teams from "../channels/teams.js";
|
|
81
|
+
|
|
82
|
+
export default defineSchedule({
|
|
83
|
+
cron: "0 9 * * 1-5",
|
|
84
|
+
markdown: "Post the daily summary.",
|
|
85
|
+
channel: receive(teams, {
|
|
86
|
+
serviceUrl: "https://smba.trafficmanager.net/teams",
|
|
87
|
+
conversationId: "19:...",
|
|
88
|
+
conversationType: "channel",
|
|
89
|
+
tenantId: "tenant-id",
|
|
90
|
+
initialMessage: "Daily summary",
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
For channel/group-chat proactive sessions without `replyToActivityId`, the first posted message
|
|
96
|
+
becomes the thread anchor and the channel re-keys the Ash continuation token to that activity id.
|
|
97
|
+
|
|
98
|
+
## Files
|
|
99
|
+
|
|
100
|
+
Text support is enabled by default. Inbound file parts are opt-in:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
export default teamsChannel({
|
|
104
|
+
files: {
|
|
105
|
+
enabled: true,
|
|
106
|
+
allowedHosts: ["contoso.sharepoint.com"],
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This covers personal-scope Teams file download attachments and simple public media URLs. Graph-based
|
|
112
|
+
channel file retrieval and outbound file-consent upload are intentionally outside v1.
|
|
113
|
+
|
|
114
|
+
## Teams References
|
|
115
|
+
|
|
116
|
+
- [Teams conversational bots](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/build-conversational-capability)
|
|
117
|
+
- [Teams channel and group chat bot conversations](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-and-group-conversations)
|
|
118
|
+
- [Bot Connector authentication](https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-authentication?view=azure-bot-service-4.0)
|
|
119
|
+
- [Bot Connector REST API reference](https://learn.microsoft.com/sr-latn-rs/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0)
|
|
120
|
+
- [Teams proactive messages](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)
|
|
121
|
+
- [Teams card actions](https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-actions)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Telegram channel setup"
|
|
3
|
+
description: "Create a Telegram-backed Ash channel for bot webhooks, replies, HITL, and attachments."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Telegram Channel Setup
|
|
7
|
+
|
|
8
|
+
The Telegram channel accepts Telegram Bot API webhooks. It verifies Telegram's
|
|
9
|
+
`X-Telegram-Bot-Api-Secret-Token` header before parsing the update, dispatches private messages and
|
|
10
|
+
addressed group messages, and sends default replies through `sendMessage`.
|
|
11
|
+
|
|
12
|
+
## Add The Channel
|
|
13
|
+
|
|
14
|
+
Create `agent/channels/telegram.ts`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { telegramChannel } from "experimental-ash/channels/telegram";
|
|
18
|
+
|
|
19
|
+
export default telegramChannel({
|
|
20
|
+
botUsername: "my_bot",
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Set the credentials Ash needs for inbound verification and outbound replies:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
TELEGRAM_BOT_TOKEN=123456:...
|
|
28
|
+
TELEGRAM_WEBHOOK_SECRET_TOKEN=...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- `TELEGRAM_BOT_TOKEN` sends replies, typing indicators, callback acknowledgements, and proactive
|
|
32
|
+
messages.
|
|
33
|
+
- `TELEGRAM_WEBHOOK_SECRET_TOKEN` must match the `secret_token` value you register with Telegram's
|
|
34
|
+
`setWebhook`.
|
|
35
|
+
|
|
36
|
+
You can also pass the same values in config:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export default telegramChannel({
|
|
40
|
+
botUsername: "my_bot",
|
|
41
|
+
credentials: {
|
|
42
|
+
botToken: process.env.TELEGRAM_BOT_TOKEN,
|
|
43
|
+
webhookSecretToken: process.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
By default, the channel mounts `POST /ash/v1/telegram`.
|
|
49
|
+
|
|
50
|
+
## Register The Webhook
|
|
51
|
+
|
|
52
|
+
Register the deployed public URL with Telegram's Bot API:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
|
|
56
|
+
-H "Content-Type: application/json" \
|
|
57
|
+
-d '{
|
|
58
|
+
"url": "https://your-app.example.com/ash/v1/telegram",
|
|
59
|
+
"secret_token": "'"$TELEGRAM_WEBHOOK_SECRET_TOKEN"'",
|
|
60
|
+
"allowed_updates": ["message", "callback_query"]
|
|
61
|
+
}'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Webhook setup is external to Ash; the runtime does not call `setWebhook` for you.
|
|
65
|
+
|
|
66
|
+
## Message Dispatch
|
|
67
|
+
|
|
68
|
+
Private chats dispatch normal text, captions, photos, and documents by default. The default
|
|
69
|
+
`onMessage` derives Telegram user auth, starts a typing indicator, and returns `{ auth }`.
|
|
70
|
+
|
|
71
|
+
Groups and supergroups are gated more narrowly:
|
|
72
|
+
|
|
73
|
+
- bot commands dispatch, including `/ask` and `/ask@my_bot`
|
|
74
|
+
- `@my_bot` mentions dispatch when `botUsername` is configured
|
|
75
|
+
- replies to bot messages dispatch
|
|
76
|
+
- unaddressed group chatter is ignored
|
|
77
|
+
|
|
78
|
+
For Telegram forum topics, the channel includes `message_thread_id` in the continuation token and
|
|
79
|
+
outbound messages so separate topics do not collapse into one session.
|
|
80
|
+
|
|
81
|
+
Override `onMessage` when you need custom auth or additional filtering:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { telegramChannel } from "experimental-ash/channels/telegram";
|
|
85
|
+
|
|
86
|
+
export default telegramChannel({
|
|
87
|
+
botUsername: "my_bot",
|
|
88
|
+
onMessage(ctx, message) {
|
|
89
|
+
if (message.chat.type !== "private" && !message.text.includes("@my_bot")) return null;
|
|
90
|
+
return {
|
|
91
|
+
auth: {
|
|
92
|
+
authenticator: "telegram",
|
|
93
|
+
principalType: "user",
|
|
94
|
+
principalId: message.from?.id ?? message.chat.id,
|
|
95
|
+
attributes: {
|
|
96
|
+
chat_id: message.chat.id,
|
|
97
|
+
chat_type: message.chat.type,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Telegram group privacy is controlled by BotFather. With privacy mode enabled, Telegram only sends
|
|
106
|
+
commands, replies, and service messages to the bot. Disable privacy mode only if your bot should see
|
|
107
|
+
all group messages; Ash still applies the group dispatch gate above.
|
|
108
|
+
|
|
109
|
+
## Delivery
|
|
110
|
+
|
|
111
|
+
The default `"message.completed"` handler sends plain text with `sendMessage`. Ash does not set
|
|
112
|
+
`parse_mode`, so generated Markdown is delivered as literal text rather than being parsed by
|
|
113
|
+
Telegram. Messages longer than Telegram's 4096-character text limit are split into multiple
|
|
114
|
+
`sendMessage` calls.
|
|
115
|
+
|
|
116
|
+
Default progress handlers call `sendChatAction` with `typing`. Typing failures are logged and
|
|
117
|
+
swallowed because the indicator is only a UX hint.
|
|
118
|
+
|
|
119
|
+
Custom event handlers can use `ctx.telegram`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
export default telegramChannel({
|
|
123
|
+
events: {
|
|
124
|
+
async "message.completed"(event, ctx) {
|
|
125
|
+
if (event.finishReason === "tool-calls" || !event.message) return;
|
|
126
|
+
await ctx.telegram.sendMessage(`Result:\n${event.message}`);
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Human Input
|
|
133
|
+
|
|
134
|
+
Pending Ash input requests render in Telegram:
|
|
135
|
+
|
|
136
|
+
- option requests render as inline keyboard buttons
|
|
137
|
+
- freeform requests render as `ForceReply` prompts
|
|
138
|
+
|
|
139
|
+
Telegram caps `callback_data` at 64 bytes, so Ash stores compact callback ids in durable channel
|
|
140
|
+
state and remaps them to the original Ash input response when the callback query arrives. Freeform
|
|
141
|
+
answers resume when the user replies to the specific prompt message.
|
|
142
|
+
|
|
143
|
+
Callback queries generated by Ash are acknowledged with `answerCallbackQuery` automatically. Other
|
|
144
|
+
callback queries are passed to `onCallbackQuery` when you provide one.
|
|
145
|
+
|
|
146
|
+
## Attachments
|
|
147
|
+
|
|
148
|
+
Inbound photos and documents become AI SDK file parts with internal `telegram-file:` URLs. When the
|
|
149
|
+
model needs the bytes, the channel calls Telegram `getFile`, downloads the file through the Bot API
|
|
150
|
+
file endpoint, and enforces the channel upload policy.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
export default telegramChannel({
|
|
154
|
+
uploadPolicy: {
|
|
155
|
+
allowedMediaTypes: ["image/*", "application/pdf"],
|
|
156
|
+
maxBytes: 10 * 1024 * 1024,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
V1 supports inbound photos and documents. Polling, inline mode, payments, business-account updates,
|
|
162
|
+
channel posts, stickers, and outbound sandbox-file sharing are not included.
|
|
163
|
+
|
|
164
|
+
## Proactive Sessions
|
|
165
|
+
|
|
166
|
+
Use `receive(telegram, { message, args, auth })` from a schedule `run` handler, or
|
|
167
|
+
`args.receive(telegram, ...)` from another channel, to start a Telegram session:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { defineSchedule } from "experimental-ash/schedules";
|
|
171
|
+
import telegram from "../channels/telegram.js";
|
|
172
|
+
|
|
173
|
+
export default defineSchedule({
|
|
174
|
+
cron: "0 9 * * 1-5",
|
|
175
|
+
async run({ receive, waitUntil, appAuth }) {
|
|
176
|
+
waitUntil(
|
|
177
|
+
receive(telegram, {
|
|
178
|
+
message: "Post the daily summary.",
|
|
179
|
+
args: {
|
|
180
|
+
chatId: "123456789",
|
|
181
|
+
initialMessage: "Daily summary",
|
|
182
|
+
},
|
|
183
|
+
auth: appAuth,
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`chatId` is required. Pass `messageThreadId` for forum topics. Pass `conversationId` to resume a
|
|
191
|
+
known group conversation, or `initialMessage` to post an anchor message first; those two options are
|
|
192
|
+
mutually exclusive.
|
|
193
|
+
|
|
194
|
+
## Telegram References
|
|
195
|
+
|
|
196
|
+
- [Telegram Bot API](https://core.telegram.org/bots/api)
|
|
197
|
+
- [setWebhook](https://core.telegram.org/bots/api#setwebhook)
|
|
198
|
+
- [sendMessage](https://core.telegram.org/bots/api#sendmessage)
|
|
199
|
+
- [sendChatAction](https://core.telegram.org/bots/api#sendchataction)
|
|
200
|
+
- [answerCallbackQuery](https://core.telegram.org/bots/api#answercallbackquery)
|
|
201
|
+
- [getFile](https://core.telegram.org/bots/api#getfile)
|
|
@@ -30,7 +30,9 @@ for `defineAgent`.
|
|
|
30
30
|
- `defineChannel(...)` - HTTP channel entrypoints in `channels/` with `routes`, `state`, `context()`, and typed event handlers (`experimental-ash/channels`)
|
|
31
31
|
- `defineHook(...)` - lifecycle and stream-event subscriber in `hooks/<slug>.ts` (`experimental-ash/hooks`)
|
|
32
32
|
- `defineSandbox(...)` - override the agent's single sandbox in `sandbox.ts` (or `sandbox/sandbox.ts` when paired with a `workspace/` folder) (`experimental-ash/sandbox`)
|
|
33
|
-
- `defineSchedule(...)` - recurring jobs in `schedules/<name>.ts` or `schedules/<name>.md`;
|
|
33
|
+
- `defineSchedule(...)` - recurring jobs in `schedules/<name>.ts` or `schedules/<name>.md`;
|
|
34
|
+
TypeScript schedules require `cron` and exactly one of `markdown` or `run`
|
|
35
|
+
(`experimental-ash/schedules`)
|
|
34
36
|
- `defineSkill(...)` - module-authored skills (`experimental-ash/skills`)
|
|
35
37
|
- `defineInstructions(...)` - module-authored instructions prompt (`experimental-ash/instructions`)
|
|
36
38
|
- `defineTool(...)` - typed executable integration in `tools/<name>.ts` (`experimental-ash/tools`)
|
|
@@ -109,6 +111,43 @@ Channel and Twilio types exported from `experimental-ash/channels/twilio`:
|
|
|
109
111
|
`confidence`, ...)
|
|
110
112
|
- `verifyTwilioRequest`, `signTwilioRequest` - Ash-owned Twilio webhook signature helpers
|
|
111
113
|
|
|
114
|
+
Channel and Telegram types exported from `experimental-ash/channels/telegram`:
|
|
115
|
+
|
|
116
|
+
- `telegramChannel` - Telegram Bot API webhook channel factory for messages, callback queries,
|
|
117
|
+
HITL, attachments, typing indicators, and proactive sessions
|
|
118
|
+
- `telegramContinuationToken` - helper for the channel-local raw token
|
|
119
|
+
(`chatId:messageThreadId:conversationId`)
|
|
120
|
+
- `TelegramChannelConfig` - config type for credentials, route, Bot API overrides, upload policy,
|
|
121
|
+
`botUsername`, hooks, and event handlers
|
|
122
|
+
- `TelegramContext` - pre-dispatch context for `onMessage` and `onCallbackQuery` (`telegram`)
|
|
123
|
+
- `TelegramEventContext` - event-handler context (`telegram`, `session`, plus mutable `state`)
|
|
124
|
+
- `TelegramHandle` - Telegram handle with `request()`, `post()`, `sendMessage()`,
|
|
125
|
+
`startTyping()`, `answerCallbackQuery()`, and `editMessageReplyMarkup()`
|
|
126
|
+
- `TelegramMessage` - parsed inbound message payload (`text`, `caption`, `chat`, `from`,
|
|
127
|
+
`attachments`, reply context, ...)
|
|
128
|
+
- `TelegramCallbackQuery` - parsed callback query payload passed to `onCallbackQuery`
|
|
129
|
+
- `TelegramAttachment` - parsed inbound photo or document metadata
|
|
130
|
+
- `TelegramReceiveArgs` - arguments accepted by proactive `receive(telegram, ...)`
|
|
131
|
+
- `verifyTelegramRequest` - Ash-owned webhook secret-token verification helper
|
|
132
|
+
|
|
133
|
+
Channel and Microsoft Teams types exported from `experimental-ash/channels/teams`:
|
|
134
|
+
|
|
135
|
+
- `teamsChannel` - Teams channel factory for Bot Framework Activity webhooks and Connector delivery
|
|
136
|
+
- `TeamsChannelConfig` - config type for credentials, route, event handlers, message/invoke hooks,
|
|
137
|
+
Adaptive Card version, and opt-in file handling
|
|
138
|
+
- `TeamsContext` - pre-dispatch context for `onMessage` and `onInvoke` (`thread`, `teams`)
|
|
139
|
+
- `TeamsEventContext` - event-handler context (`thread`, `teams`, mutable `state`)
|
|
140
|
+
- `TeamsHandle` - low-level Connector handle with `sendActivity`, `replyToActivity`,
|
|
141
|
+
`updateActivity`, `startTyping`, and `request`
|
|
142
|
+
- `TeamsThread` - conversation-scoped `post`, `update`, `startTyping`, and `mentionUser`
|
|
143
|
+
- `TeamsMessageActivity` / `TeamsInvokeActivity` - parsed inbound Activity payloads
|
|
144
|
+
- `TeamsReceiveArgs` - proactive/cross-channel handoff args requiring `serviceUrl` and
|
|
145
|
+
`conversationId`
|
|
146
|
+
- `teamsContinuationToken` - channel-local Teams continuation-token helper
|
|
147
|
+
- `defaultTeamsAuth` - default Teams actor-to-session-auth projection
|
|
148
|
+
- `verifyTeamsRequest`, `verifyTeamsJwt` - Ash-owned Bot Connector request/JWT verification helpers
|
|
149
|
+
- `sendTeamsActivity`, `replyToTeamsActivity`, `updateTeamsActivity` - Connector REST helpers
|
|
150
|
+
|
|
112
151
|
Channel types exported from `experimental-ash/channels`:
|
|
113
152
|
|
|
114
153
|
- `defineChannel` - channel primitive with `routes`, `state`, `context()`, and event handlers
|
|
@@ -138,6 +177,7 @@ import { defineChannel, POST, GET } from "experimental-ash/channels";
|
|
|
138
177
|
import { ashChannel } from "experimental-ash/channels/ash";
|
|
139
178
|
import { vercelOidc } from "experimental-ash/channels/auth";
|
|
140
179
|
import { slackChannel } from "experimental-ash/channels/slack";
|
|
180
|
+
import { telegramChannel } from "experimental-ash/channels/telegram";
|
|
141
181
|
```
|
|
142
182
|
|
|
143
183
|
Inside a route handler, the helpers object exposes:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";var cachedPackageInfo,BUNDLED_FALLBACK_PACKAGE_VERSION=`0.
|
|
1
|
+
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";var cachedPackageInfo,BUNDLED_FALLBACK_PACKAGE_VERSION=`0.28.0`,BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER=`__ASH_PACKAGE_VERSION_PLACEHOLDER__`,WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return BUNDLED_FALLBACK_PACKAGE_VERSION===BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER?`0.0.0`:BUNDLED_FALLBACK_PACKAGE_VERSION}var FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}var require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Microsoft Teams Bot Framework Connector REST wrapper.
|
|
3
|
+
*
|
|
4
|
+
* The native Teams channel talks directly to the Bot Framework Activity
|
|
5
|
+
* protocol instead of exposing BotBuilder or Teams SDK objects through
|
|
6
|
+
* Ash public APIs.
|
|
7
|
+
*/
|
|
8
|
+
import { type JsonObject } from "#shared/json.js";
|
|
9
|
+
/** Microsoft application id, materialized directly or from an async secret provider. */
|
|
10
|
+
export type TeamsAppId = string | (() => string | Promise<string>);
|
|
11
|
+
/** Microsoft application password, materialized directly or from an async secret provider. */
|
|
12
|
+
export type TeamsAppPassword = string | (() => string | Promise<string>);
|
|
13
|
+
/** Microsoft tenant id, materialized directly or from an async secret provider. */
|
|
14
|
+
export type TeamsTenantId = string | (() => string | Promise<string>);
|
|
15
|
+
/** Fetch implementation override used by tests or non-standard runtimes. */
|
|
16
|
+
export type TeamsFetch = typeof fetch;
|
|
17
|
+
/** Result shape accepted from a caller-owned Bot Connector token provider. */
|
|
18
|
+
export type TeamsAccessTokenResult = string | {
|
|
19
|
+
readonly accessToken: string;
|
|
20
|
+
readonly expiresAt?: Date | number;
|
|
21
|
+
};
|
|
22
|
+
/** Caller-owned Bot Connector token provider. */
|
|
23
|
+
export type TeamsTokenProvider = () => TeamsAccessTokenResult | Promise<TeamsAccessTokenResult>;
|
|
24
|
+
/** Credentials used by the native Teams channel. */
|
|
25
|
+
export interface TeamsCredentials {
|
|
26
|
+
readonly appId?: TeamsAppId;
|
|
27
|
+
readonly appPassword?: TeamsAppPassword;
|
|
28
|
+
readonly tenantId?: TeamsTenantId;
|
|
29
|
+
readonly tokenProvider?: TeamsTokenProvider;
|
|
30
|
+
}
|
|
31
|
+
/** Shared Teams API options. */
|
|
32
|
+
export interface TeamsApiOptions {
|
|
33
|
+
readonly credentials?: TeamsCredentials;
|
|
34
|
+
readonly fetch?: TeamsFetch;
|
|
35
|
+
readonly loginBaseUrl?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Raw Teams Connector API response body. */
|
|
38
|
+
export interface TeamsApiResponse {
|
|
39
|
+
readonly status: number;
|
|
40
|
+
readonly ok: boolean;
|
|
41
|
+
readonly body: unknown;
|
|
42
|
+
}
|
|
43
|
+
/** Minimal ResourceResponse object returned by Teams write operations. */
|
|
44
|
+
export interface TeamsPostedActivity {
|
|
45
|
+
/** Teams/Bot Framework activity id, when one was returned. */
|
|
46
|
+
readonly id: string;
|
|
47
|
+
/** Connector's raw JSON response. */
|
|
48
|
+
readonly raw: unknown;
|
|
49
|
+
}
|
|
50
|
+
/** Bot Framework channel account shape surfaced by the native Teams channel. */
|
|
51
|
+
export interface TeamsChannelAccount {
|
|
52
|
+
readonly aadObjectId?: string;
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly name?: string;
|
|
55
|
+
readonly role?: string;
|
|
56
|
+
}
|
|
57
|
+
/** Bot Framework mention entity shape used by Teams messages. */
|
|
58
|
+
export interface TeamsMention {
|
|
59
|
+
readonly mentioned: TeamsChannelAccount;
|
|
60
|
+
readonly text: string;
|
|
61
|
+
readonly type: "mention";
|
|
62
|
+
}
|
|
63
|
+
/** Bot Framework/Teams attachment shape supported by Ash-owned APIs. */
|
|
64
|
+
export interface TeamsAttachment {
|
|
65
|
+
readonly content?: JsonObject;
|
|
66
|
+
readonly contentType: string;
|
|
67
|
+
readonly contentUrl?: string;
|
|
68
|
+
readonly name?: string;
|
|
69
|
+
}
|
|
70
|
+
/** JSON body supported by Teams message endpoints used by Ash. */
|
|
71
|
+
export interface TeamsMessageBody {
|
|
72
|
+
readonly attachments?: readonly TeamsAttachment[];
|
|
73
|
+
readonly channelData?: JsonObject;
|
|
74
|
+
readonly entities?: readonly TeamsMention[];
|
|
75
|
+
readonly inputHint?: string;
|
|
76
|
+
readonly speak?: string;
|
|
77
|
+
readonly suggestedActions?: JsonObject;
|
|
78
|
+
readonly text?: string;
|
|
79
|
+
readonly textFormat?: "markdown" | "plain" | "xml";
|
|
80
|
+
}
|
|
81
|
+
/** Outbound Bot Framework activity body sent through the Connector API. */
|
|
82
|
+
export interface TeamsOutboundActivity extends TeamsMessageBody {
|
|
83
|
+
readonly conversation?: JsonObject;
|
|
84
|
+
readonly from?: TeamsChannelAccount;
|
|
85
|
+
readonly recipient?: TeamsChannelAccount;
|
|
86
|
+
readonly replyToId?: string;
|
|
87
|
+
readonly type: "message" | "typing";
|
|
88
|
+
}
|
|
89
|
+
/** Conservative text budget for one Teams bot message. */
|
|
90
|
+
export declare const TEAMS_MESSAGE_TEXT_MAX_LENGTH: number;
|
|
91
|
+
/** Builds the channel-local continuation token for one Teams conversation/thread. */
|
|
92
|
+
export declare function teamsContinuationToken(input: {
|
|
93
|
+
readonly conversationId: string;
|
|
94
|
+
readonly replyToActivityId?: string | null;
|
|
95
|
+
readonly tenantId?: string | null;
|
|
96
|
+
}): string;
|
|
97
|
+
/** Resolves a Teams app id, falling back to `MICROSOFT_APP_ID` then `TEAMS_APP_ID`. */
|
|
98
|
+
export declare function resolveTeamsAppId(appId?: TeamsAppId): Promise<string>;
|
|
99
|
+
/** Resolves a Teams app password, falling back to `MICROSOFT_APP_PASSWORD` then `TEAMS_APP_PASSWORD`. */
|
|
100
|
+
export declare function resolveTeamsAppPassword(appPassword?: TeamsAppPassword): Promise<string>;
|
|
101
|
+
/** Resolves a Teams tenant id, falling back to `MICROSOFT_TENANT_ID` then `TEAMS_TENANT_ID`. */
|
|
102
|
+
export declare function resolveTeamsTenantId(tenantId?: TeamsTenantId): Promise<string | undefined>;
|
|
103
|
+
/** Resolves a Bot Connector access token, using a custom provider or client credentials. */
|
|
104
|
+
export declare function resolveTeamsAccessToken(options?: TeamsApiOptions): Promise<string>;
|
|
105
|
+
/** Low-level Bot Framework Connector API call. */
|
|
106
|
+
export declare function callTeamsConnectorApi(input: TeamsApiOptions & {
|
|
107
|
+
readonly body?: TeamsOutboundActivity | JsonObject;
|
|
108
|
+
readonly method?: "DELETE" | "GET" | "POST" | "PUT";
|
|
109
|
+
readonly path: string;
|
|
110
|
+
readonly serviceUrl: string;
|
|
111
|
+
}): Promise<TeamsApiResponse>;
|
|
112
|
+
/** Sends a non-reply activity to one Teams conversation. */
|
|
113
|
+
export declare function sendTeamsActivity(input: TeamsApiOptions & {
|
|
114
|
+
readonly body: TeamsOutboundActivity;
|
|
115
|
+
readonly conversationId: string;
|
|
116
|
+
readonly serviceUrl: string;
|
|
117
|
+
}): Promise<TeamsPostedActivity>;
|
|
118
|
+
/** Sends a reply activity to one Teams conversation activity. */
|
|
119
|
+
export declare function replyToTeamsActivity(input: TeamsApiOptions & {
|
|
120
|
+
readonly activityId: string;
|
|
121
|
+
readonly body: TeamsOutboundActivity;
|
|
122
|
+
readonly conversationId: string;
|
|
123
|
+
readonly serviceUrl: string;
|
|
124
|
+
}): Promise<TeamsPostedActivity>;
|
|
125
|
+
/** Updates an existing Teams bot activity. */
|
|
126
|
+
export declare function updateTeamsActivity(input: TeamsApiOptions & {
|
|
127
|
+
readonly activityId: string;
|
|
128
|
+
readonly body: TeamsOutboundActivity;
|
|
129
|
+
readonly conversationId: string;
|
|
130
|
+
readonly serviceUrl: string;
|
|
131
|
+
}): Promise<TeamsPostedActivity>;
|
|
132
|
+
/** Triggers Teams' typing indicator for one conversation. */
|
|
133
|
+
export declare function triggerTeamsTypingIndicator(input: TeamsApiOptions & {
|
|
134
|
+
readonly conversationId: string;
|
|
135
|
+
readonly serviceUrl: string;
|
|
136
|
+
}): Promise<void>;
|
|
137
|
+
/** Splits text into chunks that fit Teams' conservative message-size budget. */
|
|
138
|
+
export declare function splitTeamsMessageText(content: string): readonly string[];
|
|
139
|
+
/** Normalizes a string or message body into a Teams message activity body. */
|
|
140
|
+
export declare function normalizeTeamsPostInput(message: string | TeamsMessageBody): TeamsMessageBody;
|