@agentforge-io/connectors-meta 0.1.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/dist/connector.d.ts +80 -0
- package/dist/connector.js +263 -0
- package/dist/http.d.ts +89 -0
- package/dist/http.js +157 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +71 -0
- package/dist/page.d.ts +164 -0
- package/dist/page.js +139 -0
- package/dist/tools/_shared.d.ts +25 -0
- package/dist/tools/_shared.js +31 -0
- package/dist/tools/facebook-pages.d.ts +41 -0
- package/dist/tools/facebook-pages.js +362 -0
- package/dist/tools/instagram.d.ts +42 -0
- package/dist/tools/instagram.js +389 -0
- package/dist/tools/messenger.d.ts +43 -0
- package/dist/tools/messenger.js +320 -0
- package/dist/tools/whatsapp.d.ts +39 -0
- package/dist/tools/whatsapp.js +242 -0
- package/package.json +24 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Instagram Graph API tools.
|
|
4
|
+
*
|
|
5
|
+
* Six tools covering the MVP scope the agent needs to act as a brand
|
|
6
|
+
* social-media respondent:
|
|
7
|
+
*
|
|
8
|
+
* 1. ig_list_threads — inbox threads, newest first
|
|
9
|
+
* 2. ig_list_messages — messages in a thread
|
|
10
|
+
* 3. ig_send_message — send a DM (24h window applies)
|
|
11
|
+
* 4. ig_list_media — recent feed posts (id + caption + media URL)
|
|
12
|
+
* 5. ig_list_comments — comments on a specific post
|
|
13
|
+
* 6. ig_reply_comment — reply to a comment thread
|
|
14
|
+
*
|
|
15
|
+
* Token model:
|
|
16
|
+
* - `ctx.getAccessToken()` returns the PAGE access token that was
|
|
17
|
+
* persisted at connect-time (NOT the user token). All IG Graph
|
|
18
|
+
* calls accept the page token for the linked IG Business account.
|
|
19
|
+
* - `ctx.metadata.igUserId` is the linked IG Business Account id —
|
|
20
|
+
* the IG user id, not the @-handle. Set by the connect modal when
|
|
21
|
+
* the operator picks the Page.
|
|
22
|
+
*
|
|
23
|
+
* Why these endpoints (not the Basic Display API):
|
|
24
|
+
* - The Basic Display API is being deprecated and only covers PERSONAL
|
|
25
|
+
* IG accounts. The Graph API (what we use) covers Business +
|
|
26
|
+
* Creator accounts via the Page link. For an agent that needs to
|
|
27
|
+
* READ and REPLY, Graph API is the only viable choice.
|
|
28
|
+
*
|
|
29
|
+
* 24h messaging window:
|
|
30
|
+
* - Meta enforces a 24h reply window for unsolicited DMs. Sending
|
|
31
|
+
* after 24h returns error code 10. We surface the constraint in
|
|
32
|
+
* the tool description but don't pre-validate — there's no reliable
|
|
33
|
+
* way to check from the client (the "last user message" timestamp
|
|
34
|
+
* of every conversation would be N+1 calls per send), and the API
|
|
35
|
+
* error is the authoritative answer.
|
|
36
|
+
*/
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.igReplyCommentTool = exports.igListCommentsTool = exports.igListMediaTool = exports.igSendMessageTool = exports.igListMessagesTool = exports.igListThreadsTool = void 0;
|
|
39
|
+
const http_1 = require("../http");
|
|
40
|
+
const _shared_1 = require("./_shared");
|
|
41
|
+
exports.igListThreadsTool = {
|
|
42
|
+
definition: {
|
|
43
|
+
name: 'ig_list_threads',
|
|
44
|
+
description: "List the Instagram Business inbox's conversation threads, " +
|
|
45
|
+
'newest-updated first. Returns thread id, updated time, ' +
|
|
46
|
+
'participant ids/usernames, and the latest message preview. Use ' +
|
|
47
|
+
'`cursor` from a previous response to paginate.',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
limit: {
|
|
52
|
+
type: 'number',
|
|
53
|
+
description: 'Max threads (1-50, default 20).',
|
|
54
|
+
minimum: 1,
|
|
55
|
+
maximum: 50,
|
|
56
|
+
},
|
|
57
|
+
cursor: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'Pagination cursor from a previous call. Omit on the first page.',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
build: (ctx) => async (input) => {
|
|
65
|
+
const igUserId = (0, _shared_1.resolveIgUserId)(ctx);
|
|
66
|
+
const accessToken = await ctx.getAccessToken();
|
|
67
|
+
const limit = Math.min(Math.max(Number(input.limit ?? 20), 1), 50);
|
|
68
|
+
const result = await (0, http_1.metaGet)({
|
|
69
|
+
accessToken,
|
|
70
|
+
path: `/${encodeURIComponent(igUserId)}/conversations`,
|
|
71
|
+
query: {
|
|
72
|
+
platform: 'instagram',
|
|
73
|
+
fields: 'id,updated_time,participants{id,username},messages.limit(1){id,message,from{id,username},created_time}',
|
|
74
|
+
limit,
|
|
75
|
+
after: input.cursor,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return JSON.stringify({
|
|
79
|
+
threads: result.data.map((t) => ({
|
|
80
|
+
id: t.id,
|
|
81
|
+
updatedAt: t.updated_time,
|
|
82
|
+
participants: t.participants?.data?.map((p) => ({
|
|
83
|
+
id: p.id,
|
|
84
|
+
username: p.username,
|
|
85
|
+
})) ?? [],
|
|
86
|
+
latestMessage: t.messages?.data?.[0]
|
|
87
|
+
? {
|
|
88
|
+
id: t.messages.data[0].id,
|
|
89
|
+
text: t.messages.data[0].message,
|
|
90
|
+
fromId: t.messages.data[0].from?.id,
|
|
91
|
+
fromUsername: t.messages.data[0].from?.username,
|
|
92
|
+
createdAt: t.messages.data[0].created_time,
|
|
93
|
+
}
|
|
94
|
+
: null,
|
|
95
|
+
})),
|
|
96
|
+
pageInfo: {
|
|
97
|
+
nextCursor: result.paging?.cursors?.after,
|
|
98
|
+
hasNextPage: Boolean(result.paging?.next),
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
exports.igListMessagesTool = {
|
|
104
|
+
definition: {
|
|
105
|
+
name: 'ig_list_messages',
|
|
106
|
+
description: 'List the messages in an Instagram DM thread, newest first. ' +
|
|
107
|
+
'Returns message id, text, sender id/username, created time, and ' +
|
|
108
|
+
'attachment URLs (image/video/audio).',
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
threadId: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Conversation id from `ig_list_threads`.',
|
|
115
|
+
},
|
|
116
|
+
limit: {
|
|
117
|
+
type: 'number',
|
|
118
|
+
description: 'Max messages (1-100, default 25).',
|
|
119
|
+
minimum: 1,
|
|
120
|
+
maximum: 100,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
required: ['threadId'],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
build: (ctx) => async (input) => {
|
|
127
|
+
if (!input.threadId || typeof input.threadId !== 'string') {
|
|
128
|
+
throw new Error('threadId is required (from ig_list_threads).');
|
|
129
|
+
}
|
|
130
|
+
const accessToken = await ctx.getAccessToken();
|
|
131
|
+
const limit = Math.min(Math.max(Number(input.limit ?? 25), 1), 100);
|
|
132
|
+
// Conversations expose their messages via the `messages` edge. We
|
|
133
|
+
// request the inner fields in one shot instead of doing per-message
|
|
134
|
+
// GETs — it costs the same quota but saves N round-trips.
|
|
135
|
+
const result = await (0, http_1.metaGet)({
|
|
136
|
+
accessToken,
|
|
137
|
+
path: `/${encodeURIComponent(input.threadId)}`,
|
|
138
|
+
query: {
|
|
139
|
+
fields: `messages.limit(${limit}){id,message,from{id,username},to{id,username},created_time,attachments{id,mime_type,name,file_url,image_data,video_data}}`,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
return JSON.stringify({
|
|
143
|
+
messages: result.messages?.data?.map((m) => ({
|
|
144
|
+
id: m.id,
|
|
145
|
+
text: m.message,
|
|
146
|
+
fromId: m.from?.id,
|
|
147
|
+
fromUsername: m.from?.username,
|
|
148
|
+
toIds: m.to?.data?.map((t) => t.id),
|
|
149
|
+
createdAt: m.created_time,
|
|
150
|
+
attachments: m.attachments?.data?.map((a) => ({
|
|
151
|
+
id: a.id,
|
|
152
|
+
mimeType: a.mime_type,
|
|
153
|
+
name: a.name,
|
|
154
|
+
url: a.file_url ?? a.image_data?.url ?? a.video_data?.url,
|
|
155
|
+
})) ?? [],
|
|
156
|
+
})) ?? [],
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
exports.igSendMessageTool = {
|
|
161
|
+
definition: {
|
|
162
|
+
name: 'ig_send_message',
|
|
163
|
+
description: 'Send a DM to a user on Instagram. The recipient must have ' +
|
|
164
|
+
'messaged the connected IG account within the last 24 hours; ' +
|
|
165
|
+
'outside that window Meta returns error code 10 and the message ' +
|
|
166
|
+
"is not delivered. Pass either `recipientId` (the IG user id you " +
|
|
167
|
+
'got from `ig_list_threads` participants) or `threadId` ' +
|
|
168
|
+
'(conversation id) — passing both is redundant.',
|
|
169
|
+
inputSchema: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
properties: {
|
|
172
|
+
recipientId: {
|
|
173
|
+
type: 'string',
|
|
174
|
+
description: 'IG user id of the recipient (from `ig_list_threads` ' +
|
|
175
|
+
'participants[].id). Required when `threadId` is not set.',
|
|
176
|
+
},
|
|
177
|
+
threadId: {
|
|
178
|
+
type: 'string',
|
|
179
|
+
description: 'Conversation id (from `ig_list_threads`). When set, the ' +
|
|
180
|
+
'message goes to the same thread without needing the ' +
|
|
181
|
+
'recipient id.',
|
|
182
|
+
},
|
|
183
|
+
text: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'Message body, ≤1000 chars (Meta hard limit).',
|
|
186
|
+
maxLength: 1000,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
required: ['text'],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
build: (ctx) => async (input) => {
|
|
193
|
+
const igUserId = (0, _shared_1.resolveIgUserId)(ctx);
|
|
194
|
+
const accessToken = await ctx.getAccessToken();
|
|
195
|
+
const text = String(input.text ?? '').trim();
|
|
196
|
+
if (!text) {
|
|
197
|
+
throw new Error('text is required and must not be empty.');
|
|
198
|
+
}
|
|
199
|
+
const recipientId = input.recipientId;
|
|
200
|
+
const threadId = input.threadId;
|
|
201
|
+
if (!recipientId && !threadId) {
|
|
202
|
+
throw new Error('Either `recipientId` or `threadId` must be provided.');
|
|
203
|
+
}
|
|
204
|
+
// Meta accepts EITHER `recipient.id` (a user id) OR
|
|
205
|
+
// `recipient.thread_key` (a conversation id). We prefer thread_key
|
|
206
|
+
// when both are provided because it's more robust against IG users
|
|
207
|
+
// changing their ig-scoped id across re-authentications.
|
|
208
|
+
const recipient = threadId
|
|
209
|
+
? { thread_key: threadId }
|
|
210
|
+
: { id: recipientId };
|
|
211
|
+
const result = await (0, http_1.metaPost)({
|
|
212
|
+
accessToken,
|
|
213
|
+
path: `/${encodeURIComponent(igUserId)}/messages`,
|
|
214
|
+
body: {
|
|
215
|
+
recipient,
|
|
216
|
+
message: { text },
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
return JSON.stringify({
|
|
220
|
+
messageId: result.message_id,
|
|
221
|
+
recipientId: result.recipient_id,
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
exports.igListMediaTool = {
|
|
226
|
+
definition: {
|
|
227
|
+
name: 'ig_list_media',
|
|
228
|
+
description: "List the connected Instagram Business account's published " +
|
|
229
|
+
'media (feed posts, reels, carousels), newest first. Returns id, ' +
|
|
230
|
+
'caption, media type, media URL, permalink, and engagement ' +
|
|
231
|
+
'counts. Use the post id with `ig_list_comments` to read replies.',
|
|
232
|
+
inputSchema: {
|
|
233
|
+
type: 'object',
|
|
234
|
+
properties: {
|
|
235
|
+
limit: {
|
|
236
|
+
type: 'number',
|
|
237
|
+
description: 'Max items (1-50, default 20).',
|
|
238
|
+
minimum: 1,
|
|
239
|
+
maximum: 50,
|
|
240
|
+
},
|
|
241
|
+
cursor: {
|
|
242
|
+
type: 'string',
|
|
243
|
+
description: 'Pagination cursor from a previous call.',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
build: (ctx) => async (input) => {
|
|
249
|
+
const igUserId = (0, _shared_1.resolveIgUserId)(ctx);
|
|
250
|
+
const accessToken = await ctx.getAccessToken();
|
|
251
|
+
const limit = Math.min(Math.max(Number(input.limit ?? 20), 1), 50);
|
|
252
|
+
const result = await (0, http_1.metaGet)({
|
|
253
|
+
accessToken,
|
|
254
|
+
path: `/${encodeURIComponent(igUserId)}/media`,
|
|
255
|
+
query: {
|
|
256
|
+
fields: 'id,caption,media_type,media_url,permalink,thumbnail_url,timestamp,comments_count,like_count',
|
|
257
|
+
limit,
|
|
258
|
+
after: input.cursor,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
return JSON.stringify({
|
|
262
|
+
media: result.data.map((m) => ({
|
|
263
|
+
id: m.id,
|
|
264
|
+
caption: m.caption,
|
|
265
|
+
mediaType: m.media_type,
|
|
266
|
+
mediaUrl: m.media_url,
|
|
267
|
+
permalink: m.permalink,
|
|
268
|
+
thumbnailUrl: m.thumbnail_url,
|
|
269
|
+
publishedAt: m.timestamp,
|
|
270
|
+
commentsCount: m.comments_count,
|
|
271
|
+
likeCount: m.like_count,
|
|
272
|
+
})),
|
|
273
|
+
pageInfo: {
|
|
274
|
+
nextCursor: result.paging?.cursors?.after,
|
|
275
|
+
hasNextPage: Boolean(result.paging?.next),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
exports.igListCommentsTool = {
|
|
281
|
+
definition: {
|
|
282
|
+
name: 'ig_list_comments',
|
|
283
|
+
description: 'List comments on an Instagram media item (or replies under a ' +
|
|
284
|
+
'specific parent comment). Returns comment id, text, author ' +
|
|
285
|
+
'username, like count, hidden flag, and a reply-count hint.',
|
|
286
|
+
inputSchema: {
|
|
287
|
+
type: 'object',
|
|
288
|
+
properties: {
|
|
289
|
+
mediaId: {
|
|
290
|
+
type: 'string',
|
|
291
|
+
description: 'IG media id from `ig_list_media`. Required when ' +
|
|
292
|
+
'`parentCommentId` is not set.',
|
|
293
|
+
},
|
|
294
|
+
parentCommentId: {
|
|
295
|
+
type: 'string',
|
|
296
|
+
description: "Comment id to read replies under. When set, mediaId is " +
|
|
297
|
+
'ignored — the API routes through the parent comment.',
|
|
298
|
+
},
|
|
299
|
+
limit: {
|
|
300
|
+
type: 'number',
|
|
301
|
+
description: 'Max comments (1-50, default 25).',
|
|
302
|
+
minimum: 1,
|
|
303
|
+
maximum: 50,
|
|
304
|
+
},
|
|
305
|
+
cursor: {
|
|
306
|
+
type: 'string',
|
|
307
|
+
description: 'Pagination cursor from a previous call.',
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
build: (ctx) => async (input) => {
|
|
313
|
+
const accessToken = await ctx.getAccessToken();
|
|
314
|
+
const mediaId = input.mediaId;
|
|
315
|
+
const parentCommentId = input.parentCommentId;
|
|
316
|
+
if (!mediaId && !parentCommentId) {
|
|
317
|
+
throw new Error('Either `mediaId` or `parentCommentId` must be provided.');
|
|
318
|
+
}
|
|
319
|
+
const limit = Math.min(Math.max(Number(input.limit ?? 25), 1), 50);
|
|
320
|
+
const path = parentCommentId
|
|
321
|
+
? `/${encodeURIComponent(parentCommentId)}/replies`
|
|
322
|
+
: `/${encodeURIComponent(mediaId)}/comments`;
|
|
323
|
+
const result = await (0, http_1.metaGet)({
|
|
324
|
+
accessToken,
|
|
325
|
+
path,
|
|
326
|
+
query: {
|
|
327
|
+
fields: 'id,text,timestamp,username,like_count,hidden,replies.limit(0)',
|
|
328
|
+
limit,
|
|
329
|
+
after: input.cursor,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
return JSON.stringify({
|
|
333
|
+
comments: result.data.map((c) => ({
|
|
334
|
+
id: c.id,
|
|
335
|
+
text: c.text,
|
|
336
|
+
username: c.username,
|
|
337
|
+
likeCount: c.like_count,
|
|
338
|
+
hidden: c.hidden,
|
|
339
|
+
// `replies.limit(0)` returns the paging envelope without rows.
|
|
340
|
+
// We use the presence of the edge to expose "this comment has
|
|
341
|
+
// replies" without paying for them.
|
|
342
|
+
hasReplies: Boolean(c.replies?.data?.length),
|
|
343
|
+
createdAt: c.timestamp,
|
|
344
|
+
})),
|
|
345
|
+
pageInfo: {
|
|
346
|
+
nextCursor: result.paging?.cursors?.after,
|
|
347
|
+
hasNextPage: Boolean(result.paging?.next),
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
exports.igReplyCommentTool = {
|
|
353
|
+
definition: {
|
|
354
|
+
name: 'ig_reply_comment',
|
|
355
|
+
description: 'Reply to a comment on an Instagram post. The reply nests under ' +
|
|
356
|
+
'the target comment as a public child reply. Returns the new ' +
|
|
357
|
+
'comment id.',
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: 'object',
|
|
360
|
+
properties: {
|
|
361
|
+
commentId: {
|
|
362
|
+
type: 'string',
|
|
363
|
+
description: 'IG comment id to reply to (from `ig_list_comments`).',
|
|
364
|
+
},
|
|
365
|
+
message: {
|
|
366
|
+
type: 'string',
|
|
367
|
+
description: 'Reply text, ≤2200 chars (IG hard limit).',
|
|
368
|
+
maxLength: 2200,
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
required: ['commentId', 'message'],
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
build: (ctx) => async (input) => {
|
|
375
|
+
const accessToken = await ctx.getAccessToken();
|
|
376
|
+
const commentId = String(input.commentId ?? '').trim();
|
|
377
|
+
const message = String(input.message ?? '').trim();
|
|
378
|
+
if (!commentId)
|
|
379
|
+
throw new Error('commentId is required.');
|
|
380
|
+
if (!message)
|
|
381
|
+
throw new Error('message is required.');
|
|
382
|
+
const result = await (0, http_1.metaPost)({
|
|
383
|
+
accessToken,
|
|
384
|
+
path: `/${encodeURIComponent(commentId)}/replies`,
|
|
385
|
+
body: { message },
|
|
386
|
+
});
|
|
387
|
+
return JSON.stringify({ commentId: result.id });
|
|
388
|
+
},
|
|
389
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Messenger (Page DMs) tools.
|
|
3
|
+
*
|
|
4
|
+
* Four tools covering the MVP scope for a Page-bound respondent:
|
|
5
|
+
*
|
|
6
|
+
* 1. messenger_list_threads — Page inbox, newest first
|
|
7
|
+
* 2. messenger_list_messages — messages in a thread
|
|
8
|
+
* 3. messenger_send_message — send a message (24h window + tags)
|
|
9
|
+
* 4. messenger_mark_seen — sender_action: mark thread as seen
|
|
10
|
+
*
|
|
11
|
+
* Token model:
|
|
12
|
+
* - `ctx.getAccessToken()` returns the PAGE access token persisted at
|
|
13
|
+
* connect-time. Same property as Instagram — the page token
|
|
14
|
+
* authorizes both surfaces.
|
|
15
|
+
* - `ctx.metadata.pageId` is the Page id (NOT the IG user id). Used
|
|
16
|
+
* in every endpoint path here.
|
|
17
|
+
*
|
|
18
|
+
* Why so close to Instagram's shape (but a separate file):
|
|
19
|
+
* - Conversations API is shared (same fields), so the response
|
|
20
|
+
* parsing is similar. But the routing key is different (page id vs
|
|
21
|
+
* IG user id), the platform discriminator is different, and the
|
|
22
|
+
* send-message body has a Messenger-specific `messaging_type` +
|
|
23
|
+
* `tag` set that doesn't exist on Instagram. Trying to share a
|
|
24
|
+
* single send-tool implementation between them would mean a giant
|
|
25
|
+
* conditional on platform; cleaner to fork.
|
|
26
|
+
*
|
|
27
|
+
* 24h messaging window (the Messenger-specific quirk):
|
|
28
|
+
* - Inside 24h of the user's last message → `messaging_type=RESPONSE`
|
|
29
|
+
* (free-form, no tag).
|
|
30
|
+
* - Outside 24h → `messaging_type=MESSAGE_TAG` + an approved `tag`
|
|
31
|
+
* (HUMAN_AGENT, ACCOUNT_UPDATE, CONFIRMED_EVENT_UPDATE,
|
|
32
|
+
* POST_PURCHASE_UPDATE). Sending without a tag returns error
|
|
33
|
+
* code 10 ("App does not have permission for this messaging").
|
|
34
|
+
* - We expose `messagingType` + `tag` in the schema; defaults are
|
|
35
|
+
* RESPONSE/null. The agent can pick MESSAGE_TAG when it knows it's
|
|
36
|
+
* outside the window — the API is authoritative on the actual
|
|
37
|
+
* decision.
|
|
38
|
+
*/
|
|
39
|
+
import type { ConnectorToolFactory } from '@agentforge-io/core';
|
|
40
|
+
export declare const messengerListThreadsTool: ConnectorToolFactory;
|
|
41
|
+
export declare const messengerListMessagesTool: ConnectorToolFactory;
|
|
42
|
+
export declare const messengerSendMessageTool: ConnectorToolFactory;
|
|
43
|
+
export declare const messengerMarkSeenTool: ConnectorToolFactory;
|