@adonis-agora/agent 0.62.0 → 0.63.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 +29 -0
- package/dist/providers/agent_provider.d.ts.map +1 -1
- package/dist/providers/agent_provider.js +1 -12
- package/dist/providers/agent_provider.js.map +1 -1
- package/dist/src/action-proposal-service.d.ts +3 -2
- package/dist/src/action-proposal-service.d.ts.map +1 -1
- package/dist/src/action-proposal-service.js +1 -0
- package/dist/src/action-proposal-service.js.map +1 -1
- package/dist/src/actor-resolver.d.ts +12 -4
- package/dist/src/actor-resolver.d.ts.map +1 -1
- package/dist/src/actor-resolver.js +24 -7
- package/dist/src/actor-resolver.js.map +1 -1
- package/dist/src/ag-ui/adapter.d.ts.map +1 -1
- package/dist/src/ag-ui/adapter.js +32 -6
- package/dist/src/ag-ui/adapter.js.map +1 -1
- package/dist/src/ag-ui/core/encoder.d.ts +2 -1
- package/dist/src/ag-ui/core/encoder.d.ts.map +1 -1
- package/dist/src/ag-ui/core/encoder.js +16 -5
- package/dist/src/ag-ui/core/encoder.js.map +1 -1
- package/dist/src/ag-ui/core/index.d.ts +2 -2
- package/dist/src/ag-ui/core/index.d.ts.map +1 -1
- package/dist/src/ag-ui/core/index.js +2 -2
- package/dist/src/ag-ui/core/index.js.map +1 -1
- package/dist/src/ag-ui/core/input.d.ts +21 -3
- package/dist/src/ag-ui/core/input.d.ts.map +1 -1
- package/dist/src/ag-ui/core/input.js +24 -6
- package/dist/src/ag-ui/core/input.js.map +1 -1
- package/dist/src/ag-ui/core/stream.d.ts +24 -2
- package/dist/src/ag-ui/core/stream.d.ts.map +1 -1
- package/dist/src/ag-ui/core/stream.js +25 -3
- package/dist/src/ag-ui/core/stream.js.map +1 -1
- package/dist/src/ag-ui/core/types.d.ts +2 -2
- package/dist/src/ag-ui/core/types.d.ts.map +1 -1
- package/dist/src/ag-ui/core/types.js +2 -2
- package/dist/src/ag-ui/core/types.js.map +1 -1
- package/dist/src/ag-ui/frames.d.ts +5 -1
- package/dist/src/ag-ui/frames.d.ts.map +1 -1
- package/dist/src/ag-ui/frames.js +13 -3
- package/dist/src/ag-ui/frames.js.map +1 -1
- package/dist/src/ag-ui/index.d.ts +1 -1
- package/dist/src/ag-ui/index.d.ts.map +1 -1
- package/dist/src/ag-ui/index.js +1 -1
- package/dist/src/ag-ui/index.js.map +1 -1
- package/dist/src/agent-service.d.ts +36 -3
- package/dist/src/agent-service.d.ts.map +1 -1
- package/dist/src/agent-service.js +65 -1
- package/dist/src/agent-service.js.map +1 -1
- package/dist/src/attachment-limits.d.ts +10 -0
- package/dist/src/attachment-limits.d.ts.map +1 -0
- package/dist/src/attachment-limits.js +13 -0
- package/dist/src/attachment-limits.js.map +1 -0
- package/dist/src/channels/adapters/evolution_api.d.ts +49 -0
- package/dist/src/channels/adapters/evolution_api.d.ts.map +1 -0
- package/dist/src/channels/adapters/evolution_api.js +241 -0
- package/dist/src/channels/adapters/evolution_api.js.map +1 -0
- package/dist/src/channels/adapters/telegram.d.ts +34 -0
- package/dist/src/channels/adapters/telegram.d.ts.map +1 -0
- package/dist/src/channels/adapters/telegram.js +196 -0
- package/dist/src/channels/adapters/telegram.js.map +1 -0
- package/dist/src/channels/adapters/whatsapp_cloud.d.ts +31 -0
- package/dist/src/channels/adapters/whatsapp_cloud.d.ts.map +1 -0
- package/dist/src/channels/adapters/whatsapp_cloud.js +177 -0
- package/dist/src/channels/adapters/whatsapp_cloud.js.map +1 -0
- package/dist/src/channels/handler.d.ts +167 -0
- package/dist/src/channels/handler.d.ts.map +1 -0
- package/dist/src/channels/handler.js +569 -0
- package/dist/src/channels/handler.js.map +1 -0
- package/dist/src/channels/http.d.ts +45 -0
- package/dist/src/channels/http.d.ts.map +1 -0
- package/dist/src/channels/http.js +147 -0
- package/dist/src/channels/http.js.map +1 -0
- package/dist/src/channels/index.d.ts +16 -0
- package/dist/src/channels/index.d.ts.map +1 -0
- package/dist/src/channels/index.js +15 -0
- package/dist/src/channels/index.js.map +1 -0
- package/dist/src/channels/markdown.d.ts +12 -0
- package/dist/src/channels/markdown.d.ts.map +1 -0
- package/dist/src/channels/markdown.js +120 -0
- package/dist/src/channels/markdown.js.map +1 -0
- package/dist/src/channels/questions.d.ts +43 -0
- package/dist/src/channels/questions.d.ts.map +1 -0
- package/dist/src/channels/questions.js +90 -0
- package/dist/src/channels/questions.js.map +1 -0
- package/dist/src/channels/split.d.ts +8 -0
- package/dist/src/channels/split.d.ts.map +1 -0
- package/dist/src/channels/split.js +45 -0
- package/dist/src/channels/split.js.map +1 -0
- package/dist/src/channels/store.d.ts +78 -0
- package/dist/src/channels/store.d.ts.map +1 -0
- package/dist/src/channels/store.js +151 -0
- package/dist/src/channels/store.js.map +1 -0
- package/dist/src/channels/types.d.ts +138 -0
- package/dist/src/channels/types.d.ts.map +1 -0
- package/dist/src/channels/types.js +7 -0
- package/dist/src/channels/types.js.map +1 -0
- package/dist/src/genui/builtins.d.ts.map +1 -1
- package/dist/src/genui/builtins.js +65 -6
- package/dist/src/genui/builtins.js.map +1 -1
- package/dist/src/genui/catalog.d.ts +11 -2
- package/dist/src/genui/catalog.d.ts.map +1 -1
- package/dist/src/genui/catalog.js +8 -0
- package/dist/src/genui/catalog.js.map +1 -1
- package/dist/src/genui/text.d.ts +4 -3
- package/dist/src/genui/text.d.ts.map +1 -1
- package/dist/src/genui/text.js +25 -24
- package/dist/src/genui/text.js.map +1 -1
- package/dist/src/genui/tools.d.ts +2 -1
- package/dist/src/genui/tools.d.ts.map +1 -1
- package/dist/src/genui/tools.js +6 -7
- package/dist/src/genui/tools.js.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/opencode/turns.d.ts +4 -2
- package/dist/src/opencode/turns.d.ts.map +1 -1
- package/dist/src/opencode/turns.js.map +1 -1
- package/dist/src/react/core/ag-ui-backend.d.ts +38 -14
- package/dist/src/react/core/ag-ui-backend.d.ts.map +1 -1
- package/dist/src/react/core/ag-ui-backend.js +182 -17
- package/dist/src/react/core/ag-ui-backend.js.map +1 -1
- package/dist/src/react/core/agent-chat-transport.d.ts.map +1 -1
- package/dist/src/react/core/agent-chat-transport.js +22 -1
- package/dist/src/react/core/agent-chat-transport.js.map +1 -1
- package/dist/src/react/core/backend.d.ts +11 -0
- package/dist/src/react/core/backend.d.ts.map +1 -1
- package/dist/src/react/core/backend.js.map +1 -1
- package/dist/src/react/core/index.d.ts +1 -1
- package/dist/src/react/core/index.d.ts.map +1 -1
- package/dist/src/react/core/index.js.map +1 -1
- package/dist/src/stores/lucid-schema.d.ts +24 -6
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +47 -6
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +2 -0
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +4 -0
- package/dist/src/stores/lucid.js.map +1 -1
- package/package.json +6 -1
- package/skills/agent-setup/SKILL.md +39 -3
- package/skills/agent-tools/SKILL.md +3 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ChannelDeliveryError, ChannelMediaTooLargeError, fetchBytes, postJson, record, safeEqual, sizeOf, str, } from '../http.js';
|
|
2
|
+
import { unescapeTelegramMarkdown } from '../markdown.js';
|
|
3
|
+
/** Telegram's limits: message text, buttons we put in one keyboard. */
|
|
4
|
+
const MAX_TEXT = 4096;
|
|
5
|
+
const MAX_BUTTONS = 8;
|
|
6
|
+
/** The file a message carries: the largest photo size, a document, a voice note, audio, video, a sticker. */
|
|
7
|
+
function mediaOf(message) {
|
|
8
|
+
const photo = Array.isArray(message.photo) ? record(message.photo.at(-1)) : undefined;
|
|
9
|
+
const candidates = [
|
|
10
|
+
['image', photo, 'image/jpeg'],
|
|
11
|
+
['document', record(message.document), undefined],
|
|
12
|
+
['audio', record(message.voice), 'audio/ogg'],
|
|
13
|
+
['audio', record(message.audio), undefined],
|
|
14
|
+
['video', record(message.video), 'video/mp4'],
|
|
15
|
+
['video', record(message.video_note), 'video/mp4'],
|
|
16
|
+
[
|
|
17
|
+
'sticker',
|
|
18
|
+
record(message.sticker),
|
|
19
|
+
record(message.sticker)?.is_animated === true
|
|
20
|
+
? 'application/x-tgsticker'
|
|
21
|
+
: record(message.sticker)?.is_video === true
|
|
22
|
+
? 'video/webm'
|
|
23
|
+
: 'image/webp',
|
|
24
|
+
],
|
|
25
|
+
];
|
|
26
|
+
for (const [kind, body, implied] of candidates) {
|
|
27
|
+
const fileId = str(body?.file_id);
|
|
28
|
+
if (!body || fileId === undefined)
|
|
29
|
+
continue;
|
|
30
|
+
const contentType = str(body.mime_type) ?? implied;
|
|
31
|
+
const filename = str(body.file_name);
|
|
32
|
+
const sizeBytes = sizeOf(body.file_size);
|
|
33
|
+
return {
|
|
34
|
+
kind,
|
|
35
|
+
ref: fileId,
|
|
36
|
+
...(contentType !== undefined ? { contentType } : {}),
|
|
37
|
+
...(filename !== undefined ? { filename } : {}),
|
|
38
|
+
...(sizeBytes !== undefined ? { sizeBytes } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
/** The label of the inline button with `data` on the message it was pressed on. */
|
|
44
|
+
function pressedLabel(message, data) {
|
|
45
|
+
const rows = record(message?.reply_markup)?.inline_keyboard;
|
|
46
|
+
if (!Array.isArray(rows))
|
|
47
|
+
return undefined;
|
|
48
|
+
for (const row of rows) {
|
|
49
|
+
if (!Array.isArray(row))
|
|
50
|
+
continue;
|
|
51
|
+
for (const button of row) {
|
|
52
|
+
if (record(button)?.callback_data === data)
|
|
53
|
+
return str(record(button)?.text);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A Telegram bot over the [Bot API](https://core.telegram.org/bots/api) webhook. Register it with
|
|
60
|
+
* `setWebhook({ url, secret_token, allowed_updates: ['message', 'callback_query'] })`.
|
|
61
|
+
*
|
|
62
|
+
* Reads text messages, media (photos, documents, voice notes, audio, video, stickers — downloaded
|
|
63
|
+
* with `getFile`, which serves up to 20 MB) and inline-keyboard presses (`callback_query`, answered with
|
|
64
|
+
* `answerCallbackQuery` and the keyboard removed so it cannot be pressed twice); ignores bots and
|
|
65
|
+
* (unless `groups`) group chats. Sends `sendMessage` in MarkdownV2 — when Telegram refuses the
|
|
66
|
+
* formatting, the same text goes again as plain text.
|
|
67
|
+
*/
|
|
68
|
+
export function telegram(options) {
|
|
69
|
+
const name = options.name ?? 'telegram';
|
|
70
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
71
|
+
const timeoutMs = options.timeoutMs ?? 20_000;
|
|
72
|
+
const markdown = options.markdown !== false;
|
|
73
|
+
const origin = (options.apiUrl ?? 'https://api.telegram.org').replace(/\/+$/, '');
|
|
74
|
+
const api = `${origin}/bot${options.botToken}`;
|
|
75
|
+
const call = (method, body) => postJson(name, fetcher, `${api}/${method}`, {}, body, timeoutMs);
|
|
76
|
+
const privateOnly = (chat) => options.groups === true || chat?.type === 'private';
|
|
77
|
+
return {
|
|
78
|
+
name,
|
|
79
|
+
capabilities: {
|
|
80
|
+
buttons: MAX_BUTTONS,
|
|
81
|
+
markdown: markdown ? 'telegram' : 'none',
|
|
82
|
+
maxLength: MAX_TEXT,
|
|
83
|
+
},
|
|
84
|
+
verify(request) {
|
|
85
|
+
return safeEqual(request.header('x-telegram-bot-api-secret-token'), options.secretToken);
|
|
86
|
+
},
|
|
87
|
+
parse(body) {
|
|
88
|
+
const update = record(body);
|
|
89
|
+
const updateId = update?.update_id;
|
|
90
|
+
if (typeof updateId !== 'number' && typeof updateId !== 'string')
|
|
91
|
+
return null;
|
|
92
|
+
const id = String(updateId);
|
|
93
|
+
const message = record(update?.message);
|
|
94
|
+
if (message) {
|
|
95
|
+
const chat = record(message.chat);
|
|
96
|
+
const from = record(message.from);
|
|
97
|
+
const media = mediaOf(message);
|
|
98
|
+
const text = (str(message.text) ?? str(message.caption) ?? '').trim();
|
|
99
|
+
if (!chat || !from || from.is_bot === true || !privateOnly(chat))
|
|
100
|
+
return null;
|
|
101
|
+
if (text === '' && !media)
|
|
102
|
+
return null;
|
|
103
|
+
return {
|
|
104
|
+
id,
|
|
105
|
+
from: String(from.id),
|
|
106
|
+
conversation: String(chat.id),
|
|
107
|
+
text,
|
|
108
|
+
...(media ? { media: [media] } : {}),
|
|
109
|
+
raw: update,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const query = record(update?.callback_query);
|
|
113
|
+
if (query) {
|
|
114
|
+
const from = record(query.from);
|
|
115
|
+
const pressedOn = record(query.message);
|
|
116
|
+
const chat = record(pressedOn?.chat);
|
|
117
|
+
const data = str(query.data);
|
|
118
|
+
if (!from || !chat || !data || from.is_bot === true || !privateOnly(chat))
|
|
119
|
+
return null;
|
|
120
|
+
return {
|
|
121
|
+
id,
|
|
122
|
+
from: String(from.id),
|
|
123
|
+
conversation: String(chat.id),
|
|
124
|
+
text: pressedLabel(pressedOn, data) ?? data,
|
|
125
|
+
buttonId: data,
|
|
126
|
+
raw: update,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
},
|
|
131
|
+
async acknowledge(message) {
|
|
132
|
+
const query = record(record(message.raw)?.callback_query);
|
|
133
|
+
const queryId = str(query?.id);
|
|
134
|
+
if (queryId === undefined)
|
|
135
|
+
return;
|
|
136
|
+
await call('answerCallbackQuery', { callback_query_id: queryId }).catch(() => { });
|
|
137
|
+
const pressedOn = record(query?.message);
|
|
138
|
+
if (pressedOn?.message_id !== undefined) {
|
|
139
|
+
await call('editMessageReplyMarkup', {
|
|
140
|
+
chat_id: message.conversation,
|
|
141
|
+
message_id: pressedOn.message_id,
|
|
142
|
+
reply_markup: { inline_keyboard: [] },
|
|
143
|
+
}).catch(() => { });
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
async download(media, { maxBytes }) {
|
|
147
|
+
if (media.sizeBytes !== undefined && media.sizeBytes > maxBytes) {
|
|
148
|
+
throw new ChannelMediaTooLargeError(maxBytes);
|
|
149
|
+
}
|
|
150
|
+
const answer = record(await call('getFile', { file_id: media.ref }));
|
|
151
|
+
const path = str(record(answer?.result)?.file_path);
|
|
152
|
+
if (path === undefined) {
|
|
153
|
+
// The Bot API serves files up to 20 MB; a larger one has no path.
|
|
154
|
+
throw new ChannelDeliveryError(name, null, `${name}: the file could not be downloaded`);
|
|
155
|
+
}
|
|
156
|
+
const file = await fetchBytes(name, fetcher, `${origin}/file/bot${options.botToken}/${path}`, {}, timeoutMs, maxBytes);
|
|
157
|
+
return {
|
|
158
|
+
data: file.data,
|
|
159
|
+
contentType: media.contentType ?? file.contentType ?? 'application/octet-stream',
|
|
160
|
+
...(media.filename !== undefined ? { filename: media.filename } : {}),
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
async send(conversation, message) {
|
|
164
|
+
const body = {
|
|
165
|
+
chat_id: conversation,
|
|
166
|
+
text: message.text,
|
|
167
|
+
link_preview_options: { is_disabled: true },
|
|
168
|
+
...(message.buttons !== undefined && message.buttons.length > 0
|
|
169
|
+
? {
|
|
170
|
+
reply_markup: {
|
|
171
|
+
inline_keyboard: [
|
|
172
|
+
message.buttons
|
|
173
|
+
.slice(0, MAX_BUTTONS)
|
|
174
|
+
.map((button) => ({ text: button.label, callback_data: button.id })),
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
: {}),
|
|
179
|
+
};
|
|
180
|
+
if (!markdown) {
|
|
181
|
+
await call('sendMessage', body);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
await call('sendMessage', { ...body, parse_mode: 'MarkdownV2' });
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
// "can't parse entities": the same words, unformatted, beat no answer.
|
|
189
|
+
if (!(error instanceof ChannelDeliveryError) || error.status !== 400)
|
|
190
|
+
throw error;
|
|
191
|
+
await call('sendMessage', { ...body, text: unescapeTelegramMarkdown(message.text) });
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=telegram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../../src/channels/adapters/telegram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ChannelFetch } from '../http.js';
|
|
2
|
+
import type { ChannelAdapter } from '../types.js';
|
|
3
|
+
export interface WhatsappCloudOptions {
|
|
4
|
+
/** The business phone number's id (WhatsApp Manager → API setup) — not the number itself. */
|
|
5
|
+
phoneNumberId: string;
|
|
6
|
+
/** A system-user (or temporary) access token with `whatsapp_business_messaging`. */
|
|
7
|
+
accessToken: string;
|
|
8
|
+
/** The Meta app's secret: `X-Hub-Signature-256` is an HMAC of the raw body under it. */
|
|
9
|
+
appSecret: string;
|
|
10
|
+
/** The "Verify token" typed into the app's webhook settings, echoed by the subscription check. */
|
|
11
|
+
verifyToken: string;
|
|
12
|
+
/** Graph API version. Default `'v23.0'`. */
|
|
13
|
+
graphVersion?: string;
|
|
14
|
+
/** The adapter's name — the dedupe prefix and the decision's `via`. Default `'whatsapp'`. */
|
|
15
|
+
name?: string;
|
|
16
|
+
/** Per-request timeout. Default 20 s. */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
fetch?: ChannelFetch;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* WhatsApp through Meta's [Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api).
|
|
22
|
+
* Route both `GET` (the subscription check) and `POST` (messages) to the same handler, and subscribe
|
|
23
|
+
* the app to the `messages` field.
|
|
24
|
+
*
|
|
25
|
+
* Verifies `X-Hub-Signature-256` over the raw body; reads text, interactive button/list replies,
|
|
26
|
+
* template quick replies and media (image, audio, video, document, sticker — downloaded through the
|
|
27
|
+
* Graph API media endpoint) addressed to `phoneNumberId` (status updates are ignored); sends text and
|
|
28
|
+
* interactive reply buttons (at most 3, 20-character titles).
|
|
29
|
+
*/
|
|
30
|
+
export declare function whatsappCloud(options: WhatsappCloudOptions): ChannelAdapter;
|
|
31
|
+
//# sourceMappingURL=whatsapp_cloud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp_cloud.d.ts","sourceRoot":"","sources":["../../../../src/channels/adapters/whatsapp_cloud.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,YAAY,EASlB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,oBAAoB;IACnC,6FAA6F;IAC7F,aAAa,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,SAAS,EAAE,MAAM,CAAC;IAClB,kGAAkG;IAClG,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6FAA6F;IAC7F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAwDD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAmI3E"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { createHmac } from 'node:crypto';
|
|
2
|
+
import { ChannelDeliveryError, ChannelMediaTooLargeError, fetchBytes, postJson, queryParam, record, safeEqual, sizeOf, str, } from '../http.js';
|
|
3
|
+
/** Cloud API limits: text body, interactive body, button title. */
|
|
4
|
+
const MAX_TEXT = 4096;
|
|
5
|
+
const MAX_INTERACTIVE_BODY = 1024;
|
|
6
|
+
const MAX_BUTTON_TITLE = 20;
|
|
7
|
+
const MAX_BUTTONS = 3;
|
|
8
|
+
const MEDIA_TYPES = ['image', 'audio', 'video', 'document', 'sticker'];
|
|
9
|
+
/** The file a Cloud API message carries (`{ id, mime_type, caption?, filename? }`), and its caption. */
|
|
10
|
+
function mediaOf(message) {
|
|
11
|
+
const kind = MEDIA_TYPES.find((type) => type === message.type);
|
|
12
|
+
const body = kind === undefined ? undefined : record(message[kind]);
|
|
13
|
+
const mediaId = str(body?.id);
|
|
14
|
+
if (kind === undefined || !body || mediaId === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
const contentType = str(body.mime_type);
|
|
17
|
+
const filename = str(body.filename);
|
|
18
|
+
return {
|
|
19
|
+
media: {
|
|
20
|
+
kind,
|
|
21
|
+
ref: mediaId,
|
|
22
|
+
...(contentType !== undefined ? { contentType } : {}),
|
|
23
|
+
...(filename !== undefined ? { filename } : {}),
|
|
24
|
+
},
|
|
25
|
+
caption: str(body.caption),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function parseMessage(message) {
|
|
29
|
+
const id = str(message.id);
|
|
30
|
+
const from = str(message.from);
|
|
31
|
+
if (!id || !from)
|
|
32
|
+
return null;
|
|
33
|
+
const interactive = record(message.interactive);
|
|
34
|
+
const reply = record(interactive?.button_reply) ?? record(interactive?.list_reply);
|
|
35
|
+
// A template's quick-reply button.
|
|
36
|
+
const button = record(message.button);
|
|
37
|
+
const buttonId = str(reply?.id) ?? str(button?.payload);
|
|
38
|
+
const file = mediaOf(message);
|
|
39
|
+
const text = file
|
|
40
|
+
? (file.caption ?? '')
|
|
41
|
+
: (str(record(message.text)?.body) ?? str(reply?.title) ?? str(button?.text) ?? buttonId);
|
|
42
|
+
if (text === undefined || (text.trim() === '' && !file))
|
|
43
|
+
return null;
|
|
44
|
+
return {
|
|
45
|
+
id,
|
|
46
|
+
from,
|
|
47
|
+
conversation: from,
|
|
48
|
+
text: text.trim(),
|
|
49
|
+
...(buttonId !== undefined ? { buttonId } : {}),
|
|
50
|
+
...(file ? { media: [file.media] } : {}),
|
|
51
|
+
raw: message,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* WhatsApp through Meta's [Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api).
|
|
56
|
+
* Route both `GET` (the subscription check) and `POST` (messages) to the same handler, and subscribe
|
|
57
|
+
* the app to the `messages` field.
|
|
58
|
+
*
|
|
59
|
+
* Verifies `X-Hub-Signature-256` over the raw body; reads text, interactive button/list replies,
|
|
60
|
+
* template quick replies and media (image, audio, video, document, sticker — downloaded through the
|
|
61
|
+
* Graph API media endpoint) addressed to `phoneNumberId` (status updates are ignored); sends text and
|
|
62
|
+
* interactive reply buttons (at most 3, 20-character titles).
|
|
63
|
+
*/
|
|
64
|
+
export function whatsappCloud(options) {
|
|
65
|
+
const name = options.name ?? 'whatsapp';
|
|
66
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
67
|
+
const timeoutMs = options.timeoutMs ?? 20_000;
|
|
68
|
+
const graphVersion = options.graphVersion ?? 'v23.0';
|
|
69
|
+
const endpoint = `https://graph.facebook.com/${graphVersion}/${encodeURIComponent(options.phoneNumberId)}/messages`;
|
|
70
|
+
const post = (body) => postJson(name, fetcher, endpoint, { authorization: `Bearer ${options.accessToken}` }, { messaging_product: 'whatsapp', recipient_type: 'individual', ...body }, timeoutMs);
|
|
71
|
+
const sendText = (to, body) => post({ to, type: 'text', text: { body, preview_url: false } });
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
capabilities: { buttons: MAX_BUTTONS, markdown: 'whatsapp', maxLength: MAX_TEXT },
|
|
75
|
+
challenge(request) {
|
|
76
|
+
if (request.method !== 'GET')
|
|
77
|
+
return null;
|
|
78
|
+
const mode = queryParam(request.url, 'hub.mode');
|
|
79
|
+
const token = queryParam(request.url, 'hub.verify_token');
|
|
80
|
+
const challenge = queryParam(request.url, 'hub.challenge');
|
|
81
|
+
if (mode === 'subscribe' && challenge !== undefined && safeEqual(token, options.verifyToken))
|
|
82
|
+
return { status: 200, body: challenge, contentType: 'text/plain' };
|
|
83
|
+
return { status: 403, body: 'Forbidden', contentType: 'text/plain' };
|
|
84
|
+
},
|
|
85
|
+
verify(request) {
|
|
86
|
+
const signature = request.header('x-hub-signature-256');
|
|
87
|
+
if (request.rawBody === null || signature === undefined)
|
|
88
|
+
return false;
|
|
89
|
+
const expected = `sha256=${createHmac('sha256', options.appSecret).update(request.rawBody, 'utf8').digest('hex')}`;
|
|
90
|
+
return safeEqual(signature, expected);
|
|
91
|
+
},
|
|
92
|
+
parse(body) {
|
|
93
|
+
const envelope = record(body);
|
|
94
|
+
if (envelope?.object !== 'whatsapp_business_account' || !Array.isArray(envelope.entry))
|
|
95
|
+
return null;
|
|
96
|
+
const messages = [];
|
|
97
|
+
for (const entry of envelope.entry) {
|
|
98
|
+
const changes = record(entry)?.changes;
|
|
99
|
+
if (!Array.isArray(changes))
|
|
100
|
+
continue;
|
|
101
|
+
for (const change of changes) {
|
|
102
|
+
const value = record(record(change)?.value);
|
|
103
|
+
if (record(change)?.field !== 'messages' || !value)
|
|
104
|
+
continue;
|
|
105
|
+
if (str(record(value.metadata)?.phone_number_id) !== options.phoneNumberId)
|
|
106
|
+
continue;
|
|
107
|
+
if (!Array.isArray(value.messages))
|
|
108
|
+
continue;
|
|
109
|
+
for (const message of value.messages) {
|
|
110
|
+
const parsed = record(message) ? parseMessage(record(message)) : null;
|
|
111
|
+
if (parsed)
|
|
112
|
+
messages.push(parsed);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return messages;
|
|
117
|
+
},
|
|
118
|
+
async download(media, { maxBytes }) {
|
|
119
|
+
const auth = { authorization: `Bearer ${options.accessToken}` };
|
|
120
|
+
// The media id resolves to a short-lived url, which wants the same token.
|
|
121
|
+
const lookup = await fetchBytes(name, fetcher, `https://graph.facebook.com/${graphVersion}/${encodeURIComponent(String(media.ref))}`, auth, timeoutMs, 64 * 1024);
|
|
122
|
+
let info;
|
|
123
|
+
try {
|
|
124
|
+
info = record(JSON.parse(lookup.data.toString('utf8')));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
info = undefined;
|
|
128
|
+
}
|
|
129
|
+
const url = str(info?.url);
|
|
130
|
+
if (url === undefined) {
|
|
131
|
+
throw new ChannelDeliveryError(name, null, `${name}: the media could not be resolved`);
|
|
132
|
+
}
|
|
133
|
+
const declared = sizeOf(info?.file_size);
|
|
134
|
+
if (declared !== undefined && declared > maxBytes)
|
|
135
|
+
throw new ChannelMediaTooLargeError(maxBytes);
|
|
136
|
+
const file = await fetchBytes(name, fetcher, url, auth, timeoutMs, maxBytes);
|
|
137
|
+
return {
|
|
138
|
+
data: file.data,
|
|
139
|
+
contentType: media.contentType ??
|
|
140
|
+
str(info?.mime_type) ??
|
|
141
|
+
file.contentType ??
|
|
142
|
+
'application/octet-stream',
|
|
143
|
+
...(media.filename !== undefined ? { filename: media.filename } : {}),
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
async send(conversation, message) {
|
|
147
|
+
if (message.buttons === undefined || message.buttons.length === 0) {
|
|
148
|
+
await sendText(conversation, message.buttons === undefined ? message.text : message.fallbackText);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
let body = message.text;
|
|
152
|
+
if (body.length > MAX_INTERACTIVE_BODY) {
|
|
153
|
+
// The interactive body is short: the text goes first, the buttons under its last paragraph.
|
|
154
|
+
const cut = body.lastIndexOf('\n\n', body.length - 1);
|
|
155
|
+
const tail = cut > 0 ? body.slice(cut + 2) : '';
|
|
156
|
+
const head = tail.length > 0 && tail.length <= MAX_INTERACTIVE_BODY ? body.slice(0, cut) : body;
|
|
157
|
+
await sendText(conversation, head);
|
|
158
|
+
body = head === body ? '…' : tail;
|
|
159
|
+
}
|
|
160
|
+
await post({
|
|
161
|
+
to: conversation,
|
|
162
|
+
type: 'interactive',
|
|
163
|
+
interactive: {
|
|
164
|
+
type: 'button',
|
|
165
|
+
body: { text: body },
|
|
166
|
+
action: {
|
|
167
|
+
buttons: message.buttons.slice(0, MAX_BUTTONS).map((button) => ({
|
|
168
|
+
type: 'reply',
|
|
169
|
+
reply: { id: button.id, title: button.label.slice(0, MAX_BUTTON_TITLE) },
|
|
170
|
+
})),
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=whatsapp_cloud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp_cloud.js","sourceRoot":"","sources":["../../../../src/channels/adapters/whatsapp_cloud.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import { AgentService } from '../agent-service.js';
|
|
3
|
+
import type { AttachmentLimits } from '../attachment-limits.js';
|
|
4
|
+
import type { ActionProposal } from '../spi/action-proposal-store.js';
|
|
5
|
+
import type { ToolConfirmation } from '../tool-presentation.js';
|
|
6
|
+
import type { Actor, PageContext } from '../types.js';
|
|
7
|
+
import { type ChannelQuestionTexts } from './questions.js';
|
|
8
|
+
import { type ChannelStore } from './store.js';
|
|
9
|
+
import type { ChannelAdapter, InboundMedia, InboundMessage } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* What a channel turn needs from the agent — `AgentService` is one. The optional methods turn
|
|
12
|
+
* features on: proposals (buttons, outcomes), questions (`answer`), media (`stageAttachment`), and
|
|
13
|
+
* the Lucid default store (`lucidDatabase`).
|
|
14
|
+
*/
|
|
15
|
+
export interface ChannelTurnService extends Pick<AgentService, 'send' | 'subscribe' | 'skip' | 'cancel' | 'actionProposalReply'>, Partial<Pick<AgentService, 'decideActionProposal' | 'listActionProposals' | 'actionProposalVocabulary' | 'actionApprovalMode' | 'answer' | 'attachmentLimits' | 'stageAttachment' | 'lucidDatabase'>> {
|
|
16
|
+
}
|
|
17
|
+
/** A proposal the turn left pending, as the channel puts it to the person. */
|
|
18
|
+
export interface ChannelProposal {
|
|
19
|
+
id: string;
|
|
20
|
+
toolName: string;
|
|
21
|
+
confirmation?: ToolConfirmation;
|
|
22
|
+
}
|
|
23
|
+
/** Why a media message could not be attached. */
|
|
24
|
+
export type ChannelMediaRefusal = 'disabled' | 'type' | 'size' | 'failed';
|
|
25
|
+
/** Everything the channel says on its own (not the model). English defaults; override any. */
|
|
26
|
+
export interface ChannelTexts {
|
|
27
|
+
/** The Confirm button's label. */
|
|
28
|
+
approve: string;
|
|
29
|
+
/** The Cancel button's label. */
|
|
30
|
+
reject: string;
|
|
31
|
+
/** What a proposal says above its buttons — default: its confirmation's bold title and detail. */
|
|
32
|
+
proposal(proposal: ChannelProposal): string;
|
|
33
|
+
/**
|
|
34
|
+
* How to answer a proposal by text, when there are no buttons. `approve`/`reject` are the reply
|
|
35
|
+
* commands, built from the configured `actionProposalText.vocabulary` — `#ID` included when more
|
|
36
|
+
* than one proposal is waiting.
|
|
37
|
+
*/
|
|
38
|
+
instruction(commands: {
|
|
39
|
+
approve: string;
|
|
40
|
+
reject: string;
|
|
41
|
+
}): string;
|
|
42
|
+
/** An approval in blocking mode, which a text channel cannot settle. */
|
|
43
|
+
blockingApproval: string;
|
|
44
|
+
/** The turn failed. */
|
|
45
|
+
failed: string;
|
|
46
|
+
/** An approved action ran, and presented nothing the channel could show. */
|
|
47
|
+
actionSucceeded: string;
|
|
48
|
+
/** An approved action's execution failed. */
|
|
49
|
+
actionFailed: string;
|
|
50
|
+
/** A file that could not be attached — no attachment store, a type or size it refuses, a failed download. */
|
|
51
|
+
mediaRefused(reason: ChannelMediaRefusal, media: InboundMedia, limits: AttachmentLimits | null): string;
|
|
52
|
+
/** How questions (the `ask` tool, intakes) are worded. */
|
|
53
|
+
questions: ChannelQuestionTexts;
|
|
54
|
+
/** Answer a sender `actor()` maps to nobody. Omitted → say nothing. */
|
|
55
|
+
unknownSender?: string;
|
|
56
|
+
}
|
|
57
|
+
export declare const DEFAULT_CHANNEL_TEXTS: ChannelTexts;
|
|
58
|
+
/** `texts` as `channels.handle` takes it: any part, `questions` too. */
|
|
59
|
+
export type ChannelTextsOverrides = Partial<Omit<ChannelTexts, 'questions'>> & {
|
|
60
|
+
questions?: Partial<ChannelQuestionTexts>;
|
|
61
|
+
};
|
|
62
|
+
export interface ChannelHandleOptions {
|
|
63
|
+
/**
|
|
64
|
+
* The account the sender is — from `message.from` (a phone number, a Telegram id). `null` → the
|
|
65
|
+
* message is not answered (or answered with `texts.unknownSender`). This IS the authentication of
|
|
66
|
+
* every message, so map only senders the channel itself vouches for.
|
|
67
|
+
*/
|
|
68
|
+
actor(message: InboundMessage): Actor | null | Promise<Actor | null>;
|
|
69
|
+
/**
|
|
70
|
+
* The thread this conversation continues; `null`/`undefined` → a new one, reported to
|
|
71
|
+
* {@link onThreadCreated} so you can store it.
|
|
72
|
+
*/
|
|
73
|
+
thread(actor: Actor, message: InboundMessage): string | null | undefined | Promise<string | null | undefined>;
|
|
74
|
+
/** A new thread was created for the conversation — remember it for {@link thread}. */
|
|
75
|
+
onThreadCreated?(threadId: string, actor: Actor, message: InboundMessage): void | Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* The turn's page context. Default `{ kind: adapter.name }`. The handler adds `channel: { name,
|
|
78
|
+
* conversation }` to it — what a proposal's outcome is routed back by.
|
|
79
|
+
*/
|
|
80
|
+
pageContext?: PageContext | ((message: InboundMessage) => PageContext);
|
|
81
|
+
/** The agent that answers. Default: the thread's, else the configured default. */
|
|
82
|
+
agentName?: string;
|
|
83
|
+
/** The agent service. Default: `AgentService` from the container. */
|
|
84
|
+
service?: ChannelTurnService;
|
|
85
|
+
/**
|
|
86
|
+
* Where the channel's short-lived state lives: message ids already taken, questions waiting for an
|
|
87
|
+
* answer, outcomes already relayed. Default: `lucidChannelStore()` over the agent store's database
|
|
88
|
+
* when the agent store is Lucid, else this process's memory.
|
|
89
|
+
*/
|
|
90
|
+
store?: ChannelStore;
|
|
91
|
+
/** How long a message id is remembered. Default 24 h. */
|
|
92
|
+
dedupeTtlMs?: number;
|
|
93
|
+
/** Stop waiting for a turn after this long (and cancel it). Default 5 minutes. */
|
|
94
|
+
timeoutMs?: number;
|
|
95
|
+
/**
|
|
96
|
+
* After a proposal is approved, wait this long for it to execute and relay its outcome. `0` → do
|
|
97
|
+
* not wait (the decision reply is all the person gets; `channels.onSettled` can still relay it).
|
|
98
|
+
* Default 60 s.
|
|
99
|
+
*/
|
|
100
|
+
outcomeTimeoutMs?: number;
|
|
101
|
+
/** How long a question waits for its answer before the agent proceeds without it. Default 30 min. */
|
|
102
|
+
questionTimeoutMs?: number;
|
|
103
|
+
texts?: ChannelTextsOverrides;
|
|
104
|
+
/** A message whose handling failed. Default: `console.error`. */
|
|
105
|
+
onError?(error: unknown, message: InboundMessage): void;
|
|
106
|
+
}
|
|
107
|
+
/** The route handler `channels.handle()` returns. */
|
|
108
|
+
export interface ChannelRouteHandler {
|
|
109
|
+
(ctx: HttpContext): Promise<void>;
|
|
110
|
+
/** Resolves once every turn this handler started has been answered — for tests and shutdown. */
|
|
111
|
+
drain(): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
/** The button ids for a proposal: what {@link ChannelRouteHandler} maps back to its decision. */
|
|
114
|
+
export declare function proposalButtonIds(proposalId: string): {
|
|
115
|
+
approve: string;
|
|
116
|
+
reject: string;
|
|
117
|
+
};
|
|
118
|
+
/** Where a turn's output goes back to — `pageContext.channel`, recorded with every proposal. */
|
|
119
|
+
export interface ChannelAddress {
|
|
120
|
+
name: string;
|
|
121
|
+
conversation: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The webhook route for one channel. Each request is verified (`401` when it is not the provider's),
|
|
125
|
+
* parsed, deduplicated by the provider's message id, and acknowledged with `200` at once; the turn
|
|
126
|
+
* runs after the response, so a slow model never makes the provider retry. Then:
|
|
127
|
+
*
|
|
128
|
+
* - media are downloaded and attached (held to the attachment store's limits), or refused with a text;
|
|
129
|
+
* - the message is sent with text-only capabilities (components arrive as their `fallbackText`);
|
|
130
|
+
* a text decision ("yes", "confirm #ID") is answered with its reply instead of starting a turn;
|
|
131
|
+
* - a press on a proposal's button decides that proposal (`via` = the adapter's name);
|
|
132
|
+
* - a question the turn asks is sent as text, one at a time, and the next messages answer it;
|
|
133
|
+
* - the reply is converted to the channel's markdown, split at its length limit, and sent;
|
|
134
|
+
* - a proposal the turn left pending is sent with Confirm/Cancel buttons — or, on a channel without
|
|
135
|
+
* them, with a text instruction in the configured `actionProposalText` vocabulary;
|
|
136
|
+
* - an approved proposal's outcome is relayed once it executed (see `outcomeTimeoutMs`, and
|
|
137
|
+
* `channels.onSettled` for one that runs later).
|
|
138
|
+
*
|
|
139
|
+
* Wants `actionApprovalMode: 'independent'`: a blocking approval holds the turn open until someone
|
|
140
|
+
* decides it in the app, so the channel sends what it has and `texts.blockingApproval`.
|
|
141
|
+
*
|
|
142
|
+
* Registers the adapter under its `name` for `channels.onSettled` — names must be unique.
|
|
143
|
+
*/
|
|
144
|
+
export declare function handleChannel(adapter: ChannelAdapter, options: ChannelHandleOptions): ChannelRouteHandler;
|
|
145
|
+
/**
|
|
146
|
+
* `actionProposalWorker.onSettled` for text channels: relays an executed proposal's outcome to the
|
|
147
|
+
* conversation it was proposed in, through the adapter registered under the recorded channel name.
|
|
148
|
+
* Proposals from other surfaces are ignored. Safe next to the handler's own wait: an outcome is
|
|
149
|
+
* relayed once.
|
|
150
|
+
*
|
|
151
|
+
* ```ts
|
|
152
|
+
* // config/agent.ts
|
|
153
|
+
* actionProposalWorker: { onSettled: channels.onSettled }
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare function relayChannelOutcome(proposal: ActionProposal): Promise<void>;
|
|
157
|
+
export declare const channels: {
|
|
158
|
+
/** See {@link handleChannel}. */
|
|
159
|
+
handle: typeof handleChannel;
|
|
160
|
+
/** See {@link relayChannelOutcome}. */
|
|
161
|
+
onSettled: typeof relayChannelOutcome;
|
|
162
|
+
/** Relay one proposal's outcome now; `true` when it was sent. */
|
|
163
|
+
deliverOutcome: (proposal: ActionProposal, service?: ChannelTurnService) => Promise<boolean>;
|
|
164
|
+
/** The adapter registered under `name` (by `channels.handle`), if any. */
|
|
165
|
+
adapter: (name: string) => ChannelAdapter | undefined;
|
|
166
|
+
};
|
|
167
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/channels/handler.ts"],"names":[],"mappings":"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"}
|