@mono-agent/messenger-adapter 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,302 @@
1
+ # @mono-agent/messenger-adapter
2
+
3
+ Connect a mono-agent responder to Facebook Messenger users through the Meta
4
+ Graph API: a signed webhook for inbound messages and the Send API for replies.
5
+
6
+ ## Category
7
+
8
+ <!-- package-metadata:start -->
9
+ <!-- Generated by scripts/generate-package-docs.mjs. Do not edit by hand. -->
10
+
11
+ Category: `communication`
12
+ Tier: `plugin`
13
+ Catalog responsibility: Adapts Facebook Messenger webhook events to structural agent requests and delivers final-only replies through the Send API.
14
+
15
+ <!-- package-metadata:end -->
16
+
17
+ ## Responsibility
18
+
19
+ Verify Meta webhook signatures, deduplicate events, authorize page-scoped user
20
+ ids (PSIDs), turn text, postbacks, images, and documents into agent requests,
21
+ queue work per user, support `/cancel`, and deliver each completed answer as
22
+ plain-text Send API messages. Proactive cron/webhook notifications are
23
+ delivered verbatim and recorded to the conversation's history.
24
+
25
+ This is a **plugin-tier** package: it publishes to npm in the mono-agent
26
+ lockstep at the same version as the core packages, but it is not part of the
27
+ core `@mono-agent/agent-app` dependency closure. `@mono-agent/agent-app` loads it
28
+ only when a host declares it under `channels.plugins[]`.
29
+
30
+ The adapter is opt-in: plugin `config.enabled` / `MONO_AGENT_MESSENGER_ENABLED`
31
+ defaults to `false`. While disabled the loader skips secret and allowlist
32
+ validation and the channel reports `disabled` rather than `waiting_for_config`.
33
+
34
+ ## Install / Usage
35
+
36
+ ```bash
37
+ pnpm add @mono-agent/agent-app@latest @mono-agent/messenger-adapter@latest
38
+ ```
39
+
40
+ Put the three Meta credentials in the agent's `.env`:
41
+
42
+ ```bash
43
+ MONO_AGENT_MESSENGER_PAGE_ACCESS_TOKEN=... # Page access token with pages_messaging
44
+ MONO_AGENT_MESSENGER_APP_SECRET=... # App secret, verifies X-Hub-Signature-256
45
+ MONO_AGENT_MESSENGER_VERIFY_TOKEN=... # Any string you also paste into the Meta webhook setup
46
+ ```
47
+
48
+ Then declare the plugin:
49
+
50
+ ```json
51
+ {
52
+ "channels": {
53
+ "plugins": [
54
+ {
55
+ "package": "@mono-agent/messenger-adapter",
56
+ "id": "messenger",
57
+ "config": {
58
+ "enabled": true,
59
+ "allowedUserIds": ["1234567890123456"],
60
+ "host": "0.0.0.0",
61
+ "port": 8650,
62
+ "allowNonLoopback": true,
63
+ "webhookPath": "/messenger/webhook"
64
+ }
65
+ }
66
+ ]
67
+ }
68
+ }
69
+ ```
70
+
71
+ Expose the webhook over HTTPS (reverse proxy or tunnel) and register
72
+ `https://<public-host>/messenger/webhook` in the Meta app's Messenger webhook
73
+ settings with the same verify token, subscribing to the `messages` and
74
+ `messaging_postbacks` fields. `GET <path>/health` answers `{"ok":true}` for
75
+ liveness checks.
76
+
77
+ ## Architecture
78
+
79
+ ### Data flow
80
+
81
+ 1. The channel driver layers plugin JSON and environment settings, validates
82
+ secrets and the PSID allowlist, then starts the webhook server on the
83
+ configured host and port.
84
+ 2. The server answers Meta's `hub.challenge` handshake, verifies each POST's
85
+ `X-Hub-Signature-256` over the raw body, acknowledges with 200, and hands the
86
+ parsed payload to the adapter afterwards so Meta never waits on a model turn.
87
+ 3. The adapter drops echoes, receipts, and duplicate message ids, rejects
88
+ unauthorized senders with a one-line denial, normalizes text, postbacks, and
89
+ attachments (downloading images and documents from Meta's CDN), and handles
90
+ `/start`, `/help`, and `/cancel` without a model call.
91
+ 4. Allowed prompts enter the host-owned structural responder behind a per-user
92
+ queue with typing indicators. The message stream buffers deltas, flattens
93
+ Markdown, and posts the final answer as 2,000-character Send API messages.
94
+ 5. Proactive notifications (`messenger:<psid>` destinations) are posted verbatim
95
+ with the configured `messaging_type`/tag and recorded to conversation history;
96
+ process-job wakes steer into an active run or fall back to a follow-up turn.
97
+ 6. Shutdown closes the webhook server and aborts active responder work.
98
+
99
+ ### Package structure
100
+
101
+ | Source module | Responsibility |
102
+ | --- | --- |
103
+ | [`channel-driver.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/channel-driver.ts) | Config-first plugin lifecycle, proactive notify routing, and process-job delivery. |
104
+ | [`config.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/config.ts) | JSON/env layering, secret and allowlist validation, loopback guard, and redacted config metadata. |
105
+ | [`server.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/server.ts) | Meta verification handshake, signature check, body limits, and health endpoint. |
106
+ | [`adapter.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/adapter.ts) | Dedup, authorization, normalization, attachment ingest, per-user queue, commands, cancellation, responder invocation, and proactive delivery. |
107
+ | [`graph-client.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/graph-client.ts) | Send API client for text chunks, attachment URLs, and sender actions. Replay-safe sender actions retry once; message POSTs retry only a `429` and surface every other unknown outcome as an ambiguous delivery. Graph errors are scrubbed of the configured token. |
108
+ | [`message-stream.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/message-stream.ts) | Buffered final-only answer delivery with Markdown flattening. |
109
+ | [`text.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/text.ts) | Signature verification, Markdown stripping, and code-point-safe chunking. |
110
+ | [`start.ts`](https://github.com/robertsreberski/mono-agent/blob/main/extras/messenger-adapter/src/start.ts) | Composition root wiring client, adapter, and server into one start/stop handle. |
111
+
112
+ ### Configuration
113
+
114
+ | Key | Env | Default | Purpose |
115
+ | --- | --- | --- | --- |
116
+ | `enabled` | `MONO_AGENT_MESSENGER_ENABLED` | `false` | Opt-in switch. |
117
+ | — | `MONO_AGENT_MESSENGER_PAGE_ACCESS_TOKEN` | — | Secret, **env-only**. Page token used for the Send API. Rejected in JSON. |
118
+ | — | `MONO_AGENT_MESSENGER_APP_SECRET` | — | Secret, **env-only**. HMAC key for webhook signature verification. Rejected in JSON. |
119
+ | — | `MONO_AGENT_MESSENGER_VERIFY_TOKEN` | — | Secret, **env-only**. Expected `hub.verify_token` during webhook setup. Rejected in JSON. |
120
+ | `allowedUserIds` | `MONO_AGENT_MESSENGER_ALLOWED_USER_IDS` | `[]` | PSIDs allowed to talk to the agent. |
121
+ | `allowAllUsers` | `MONO_AGENT_MESSENGER_ALLOW_ALL_USERS` | `false` | Allow every user (ignores the allowlist). |
122
+ | `host` | `MONO_AGENT_MESSENGER_HOST` | `127.0.0.1` | Bind address. Non-loopback needs `allowNonLoopback`. |
123
+ | `port` | `MONO_AGENT_MESSENGER_PORT` | `8650` | Bind port. |
124
+ | `webhookPath` | `MONO_AGENT_MESSENGER_WEBHOOK_PATH` | `/messenger/webhook` | Webhook route; `/health` is appended for liveness. |
125
+ | `apiVersion` | `MONO_AGENT_MESSENGER_API_VERSION` | `v21.0` | Graph API version. |
126
+ | `allowNonLoopback` | `MONO_AGENT_MESSENGER_ALLOW_NON_LOOPBACK` | `false` | Explicit opt-in to bind a non-loopback host, enforced at load, at `startMessengerAdapter`, and immediately before `listen()`. |
127
+ | `proactiveMessagingType` | `MONO_AGENT_MESSENGER_PROACTIVE_MESSAGING_TYPE` | `RESPONSE` | `messaging_type` for cron/webhook deliveries (`RESPONSE`, `UPDATE`, `MESSAGE_TAG`). |
128
+ | `proactiveTag` | `MONO_AGENT_MESSENGER_PROACTIVE_TAG` | — | Required with `MESSAGE_TAG`, e.g. `CONFIRMED_EVENT_UPDATE`. |
129
+
130
+ Meta only delivers ordinary messages inside the 24-hour window after the
131
+ user's last message. A scheduled reminder that may fire outside that window
132
+ needs `proactiveMessagingType: "MESSAGE_TAG"` plus a policy-compliant tag.
133
+
134
+ ### Conversation ids and notifications
135
+
136
+ Conversations are keyed `messenger:<psid>`. Cron jobs and webhook endpoints can
137
+ target one with `notifyConversationId: "messenger:<psid>"`; the final answer is
138
+ posted verbatim, Markdown flattened, split into 2,000-character messages, and
139
+ recorded to that conversation's history. Destinations outside the allowlist are
140
+ refused by the adapter.
141
+
142
+ ### Behaviour notes
143
+
144
+ - Meta expects a 200 within seconds; the server acknowledges first and processes
145
+ the payload afterwards. Duplicate deliveries are dropped by message id.
146
+ - Messages from one user are handled in order. Up to four wait behind the
147
+ active turn; beyond that the user gets a short busy reply.
148
+ - `/cancel` aborts the active turn and retires every prompt already queued
149
+ behind it, so a withdrawn message never answers later; a message sent after
150
+ the cancel runs normally. `/help` and `/start` answer without a model call.
151
+ - Image and PDF/text attachments are downloaded from Meta's CDN and passed to
152
+ the agent; audio, video, and other files are described in the request text
153
+ with their URL. Downloads are bounded by an explicit host policy (HTTPS on
154
+ `fbcdn.net` / `fbsbx.com` by default, configurable via
155
+ `attachments.allowedHostSuffixes`): redirects are followed manually with
156
+ every hop re-validated, hostnames must resolve entirely to public addresses,
157
+ and the 20 MiB cap is applied while streaming rather than after buffering.
158
+ - A background wake reserves its per-user queue slot BEFORE offering live input
159
+ to the active turn, so a prompt arriving mid-offer cannot overtake it.
160
+ - Replies are plain text: Markdown is flattened before sending.
161
+ - Unauthorized senders receive a one-line denial and are logged; their text
162
+ never reaches the agent.
163
+
164
+ ## Public API
165
+
166
+ ### Start here
167
+
168
+ | API | Use it for |
169
+ | --- | --- |
170
+ | `createChannelDriver` / `createMessengerChannelDriver` | Load the adapter through a config-first `channels.plugins[]` entry. |
171
+ | `startMessengerAdapter` | Start the Graph client, adapter, and webhook server together in a custom host. |
172
+ | `MessengerAdapter` | Apply signature-verified webhook events, authorization, commands, cancellation, and responder behavior. |
173
+ | `MessengerAdapter.notify` | Deliver a proactive message verbatim or as a follow-up turn for one user. |
174
+ | `createMessengerWebhookServer` | Serve the Meta verification handshake and signed webhook POSTs. |
175
+ | `MessengerGraphClient` | Send text, attachment URLs, and sender actions through the Send API. |
176
+ | `MessengerMessageStream` | Reuse buffered plain-text final-answer delivery. |
177
+ | `loadMessengerAdapterConfig` | Validate and redact plugin/env config without starting the channel. |
178
+ | `verifyMessengerSignature` / `splitForMessenger` / `stripMarkdownForMessenger` | Reuse the webhook signature check and text shaping helpers. |
179
+
180
+ <!-- public-api-inventory:start -->
181
+ <!-- Generated by scripts/generate-public-api-docs.mjs. Do not edit by hand. -->
182
+
183
+ Every symbol exported by each public code entrypoint is listed below.
184
+
185
+ **`@mono-agent/messenger-adapter`**
186
+
187
+ ```text
188
+ AgentMessageStream
189
+ AgentRequest
190
+ AgentResponder
191
+ AgentResponse
192
+ DEFAULT_GRAPH_API_BASE_URL
193
+ DEFAULT_MESSENGER_API_VERSION
194
+ DEFAULT_MESSENGER_ATTACHMENT_HOST_SUFFIXES
195
+ DEFAULT_MESSENGER_HOST
196
+ DEFAULT_MESSENGER_PORT
197
+ DEFAULT_MESSENGER_WEBHOOK_PATH
198
+ LoadMessengerAdapterConfigInput
199
+ MESSENGER_CHANNEL_ID
200
+ MESSENGER_CONFIG_FIELDS
201
+ MESSENGER_ENV_ONLY_SECRET_KEYS
202
+ MESSENGER_MAX_MESSAGE_CHARS
203
+ MESSENGER_MESSAGING_TYPES
204
+ MessengerAdapter
205
+ MessengerAdapterConfig
206
+ MessengerAdapterConfigError
207
+ MessengerAdapterConfigErrorCode
208
+ MessengerAdapterConfigErrorDetails
209
+ MessengerAdapterLogger
210
+ MessengerAdapterMessages
211
+ MessengerAdapterOptions
212
+ MessengerAdapterStartLogger
213
+ MessengerAdapterStartResult
214
+ MessengerAmbiguousDeliveryError
215
+ MessengerAttachmentIngestOptions
216
+ MessengerChannelDriverConfig
217
+ MessengerChannelDriverOptions
218
+ MessengerEventResult
219
+ MessengerGraphClient
220
+ MessengerGraphClientLike
221
+ MessengerGraphClientLogger
222
+ MessengerGraphClientOptions
223
+ MessengerGraphError
224
+ MessengerIgnoredReason
225
+ MessengerMessageStream
226
+ MessengerMessageStreamLogger
227
+ MessengerMessageStreamOptions
228
+ MessengerMessagingType
229
+ MessengerNotifyOptions
230
+ MessengerNotifyResult
231
+ MessengerProactiveOptions
232
+ MessengerRequestMetadata
233
+ MessengerSendOptions
234
+ MessengerSendResult
235
+ MessengerSenderAction
236
+ MessengerWebhookAttachment
237
+ MessengerWebhookEvent
238
+ MessengerWebhookServer
239
+ MessengerWebhookServerLogger
240
+ MessengerWebhookServerOptions
241
+ RedactedMessengerAdapterConfig
242
+ StartMessengerAdapterOptions
243
+ assertMessengerBindAllowed
244
+ assertNoMessengerSecretsInJson
245
+ assertValidMessengerAdapterConfig
246
+ attachmentUrlPolicyRejection
247
+ createChannelDriver
248
+ createMessengerChannelDriver
249
+ createMessengerWebhookServer
250
+ isLoopbackHost
251
+ isMessengerAmbiguousDeliveryError
252
+ isPublicUnicastAddress
253
+ isSafeAttachmentUrl
254
+ loadMessengerAdapterConfig
255
+ messengerConversationId
256
+ messengerUserIdFromConversation
257
+ redactMessengerAdapterConfig
258
+ splitForMessenger
259
+ startMessengerAdapter
260
+ stripMarkdownForMessenger
261
+ verifyMessengerSignature
262
+ ```
263
+
264
+ <!-- public-api-inventory:end -->
265
+
266
+ ### Programmatic use
267
+
268
+ ```ts
269
+ import { loadMessengerAdapterConfig, startMessengerAdapter } from "@mono-agent/messenger-adapter";
270
+
271
+ const config = await loadMessengerAdapterConfig({ env: process.env, jsonPath: "./mono-agent.config.json" });
272
+ const running = await startMessengerAdapter({ config, responder });
273
+ await running.notify("1234567890123456", "Reminder text", { verbatim: true });
274
+ await running.stop();
275
+ ```
276
+
277
+ ## Dependency Boundary
278
+
279
+ Depends only on `@mono-agent/agent-contracts` and Node's built-in `http`,
280
+ `crypto`, and global `fetch`. It never imports the app host, harness, other
281
+ adapters, or operator surfaces.
282
+
283
+ ## What This Package Does Not Own
284
+
285
+ It does not own the Meta app or Page, TLS termination or the public URL for the
286
+ webhook, Meta's messaging-window policy, prompt building, model execution,
287
+ memory, or an operator UI.
288
+
289
+ ## Related Documentation
290
+
291
+ - [Messenger channel guide](https://mono-agent-docs.vercel.app/channels/messenger/)
292
+ - [Channels overview](https://mono-agent-docs.vercel.app/channels/)
293
+ - [Delivery and send tools](https://mono-agent-docs.vercel.app/channels/delivery-and-send-tools/)
294
+ - [Write your own channel adapter](https://mono-agent-docs.vercel.app/programmatic/custom-channels/)
295
+
296
+ ## Verification
297
+
298
+ ```bash
299
+ pnpm --filter @mono-agent/messenger-adapter run build
300
+ pnpm --filter @mono-agent/messenger-adapter run typecheck
301
+ pnpm --filter @mono-agent/messenger-adapter run test
302
+ ```
@@ -0,0 +1,261 @@
1
+ import { type AgentMessageStream, type AgentRequestBase, type AgentResponder as SharedAgentResponder, type AgentResponse, type NotifyDeliveryResult, type ProcessJobProjection, type ProcessJobWakeDisposition } from "@mono-agent/agent-contracts";
2
+ import type { MessengerMessagingType } from "./config.js";
3
+ import type { MessengerGraphClientLike } from "./graph-client.js";
4
+ import { type MessengerMessageStreamLogger } from "./message-stream.js";
5
+ export declare const MESSENGER_CHANNEL_ID = "messenger";
6
+ /** Conversation id for a Messenger user: `messenger:<psid>`. */
7
+ export declare function messengerConversationId(userId: string): string;
8
+ /** Parse `messenger:<psid>` back into the PSID; undefined for any other shape. */
9
+ export declare function messengerUserIdFromConversation(conversationId: string): string | undefined;
10
+ export interface MessengerWebhookAttachment {
11
+ readonly type?: string;
12
+ readonly title?: string;
13
+ readonly payload?: {
14
+ readonly url?: string;
15
+ readonly coordinates?: {
16
+ readonly lat?: number;
17
+ readonly long?: number;
18
+ };
19
+ };
20
+ }
21
+ /** One `entry[].messaging[]` event from a Messenger webhook payload. */
22
+ export interface MessengerWebhookEvent {
23
+ readonly sender?: {
24
+ readonly id?: string;
25
+ };
26
+ readonly recipient?: {
27
+ readonly id?: string;
28
+ };
29
+ readonly timestamp?: number;
30
+ readonly message?: {
31
+ readonly mid?: string;
32
+ readonly text?: string;
33
+ readonly is_echo?: boolean;
34
+ readonly attachments?: readonly MessengerWebhookAttachment[];
35
+ };
36
+ readonly postback?: {
37
+ readonly mid?: string;
38
+ readonly title?: string;
39
+ readonly payload?: string;
40
+ };
41
+ readonly delivery?: unknown;
42
+ readonly read?: unknown;
43
+ }
44
+ export interface MessengerRequestMetadata {
45
+ readonly user: {
46
+ readonly id: string;
47
+ };
48
+ readonly page?: {
49
+ readonly id: string;
50
+ };
51
+ readonly message: {
52
+ readonly id?: string;
53
+ readonly timestamp?: number;
54
+ };
55
+ readonly attachmentTypes: readonly string[];
56
+ readonly trigger: "message" | "postback" | "proactive";
57
+ }
58
+ export interface AgentRequest extends AgentRequestBase {
59
+ readonly conversationId: string;
60
+ readonly userId: string;
61
+ readonly messageId?: string;
62
+ readonly text: string;
63
+ readonly abortSignal: AbortSignal;
64
+ readonly metadata: {
65
+ readonly messenger: MessengerRequestMetadata;
66
+ readonly [key: string]: unknown;
67
+ };
68
+ }
69
+ export type { AgentResponse };
70
+ export type AgentResponder = SharedAgentResponder<AgentRequest, AgentMessageStream, AgentResponse>;
71
+ export interface MessengerAdapterMessages {
72
+ welcomeText?: string;
73
+ helpText?: string;
74
+ busyText?: string;
75
+ unauthorizedText?: string;
76
+ cancelledText?: string;
77
+ errorText?: string;
78
+ unsupportedText?: string;
79
+ }
80
+ export interface MessengerAdapterLogger extends MessengerMessageStreamLogger {
81
+ info?(message: string, metadata?: Record<string, unknown>): void;
82
+ }
83
+ export interface MessengerAttachmentIngestOptions {
84
+ readonly fetch?: typeof fetch;
85
+ readonly maxBytes?: number;
86
+ readonly timeoutMs?: number;
87
+ /**
88
+ * Explicit host policy for attachment downloads. Only hostnames equal to, or
89
+ * a subdomain of, one of these suffixes are contacted. Defaults to Meta's
90
+ * media CDNs ({@link DEFAULT_MESSENGER_ATTACHMENT_HOST_SUFFIXES}) — the only
91
+ * origin a Messenger webhook legitimately points at. Pass `[]` to disable
92
+ * attachment downloads entirely.
93
+ */
94
+ readonly allowedHostSuffixes?: readonly string[];
95
+ /** Resolves a hostname to IP addresses. Test seam; defaults to the system resolver. */
96
+ readonly resolveAddresses?: (hostname: string) => Promise<readonly string[]>;
97
+ }
98
+ export interface MessengerProactiveOptions {
99
+ readonly messagingType: MessengerMessagingType;
100
+ readonly tag?: string;
101
+ }
102
+ export interface MessengerAdapterOptions {
103
+ readonly client: MessengerGraphClientLike;
104
+ readonly responder: AgentResponder;
105
+ readonly allowedUserIds?: readonly string[];
106
+ readonly allowAllUsers?: boolean;
107
+ readonly messages?: MessengerAdapterMessages;
108
+ readonly logger?: MessengerAdapterLogger;
109
+ readonly attachments?: MessengerAttachmentIngestOptions;
110
+ readonly proactive?: MessengerProactiveOptions;
111
+ /** Inbound messages queued per user while a turn runs; beyond it the user gets `busyText`. */
112
+ readonly maxQueuedPerUser?: number;
113
+ readonly maxMessageChars?: number;
114
+ }
115
+ export interface MessengerNotifyOptions {
116
+ readonly verbatim?: boolean;
117
+ readonly deliveryKey?: string;
118
+ readonly steerActive?: boolean;
119
+ }
120
+ export interface MessengerNotifyResult extends NotifyDeliveryResult {
121
+ readonly disposition?: ProcessJobWakeDisposition;
122
+ }
123
+ export type MessengerIgnoredReason = "echo" | "receipt" | "no_sender" | "no_content" | "duplicate" | "empty_text";
124
+ export type MessengerEventResult = {
125
+ kind: "handled";
126
+ userId: string;
127
+ messageId?: string;
128
+ action: "command" | "responded";
129
+ command?: "start" | "help";
130
+ } | {
131
+ kind: "ignored";
132
+ reason: MessengerIgnoredReason;
133
+ userId?: string;
134
+ messageId?: string;
135
+ } | {
136
+ kind: "unauthorized";
137
+ userId: string;
138
+ messageId?: string;
139
+ } | {
140
+ kind: "busy";
141
+ userId: string;
142
+ messageId?: string;
143
+ } | {
144
+ kind: "cancelled";
145
+ userId: string;
146
+ messageId?: string;
147
+ } | {
148
+ kind: "error";
149
+ userId?: string;
150
+ messageId?: string;
151
+ error: unknown;
152
+ };
153
+ /**
154
+ * Meta's media CDNs. Messenger attachment payload URLs are signed links into
155
+ * these origins, so an explicit allowlist — rather than "any public-looking
156
+ * HTTPS host" — is what actually bounds this downloader.
157
+ */
158
+ export declare const DEFAULT_MESSENGER_ATTACHMENT_HOST_SUFFIXES: readonly string[];
159
+ export declare class MessengerAdapter {
160
+ private readonly client;
161
+ private readonly responder;
162
+ private readonly allowAllUsers;
163
+ private readonly allowedUserIds;
164
+ private readonly messages;
165
+ private readonly logger;
166
+ private readonly ingest;
167
+ private readonly proactive;
168
+ private readonly maxQueuedPerUser;
169
+ private readonly maxMessageChars;
170
+ private readonly dedup;
171
+ /**
172
+ * Every controller admitted for a user, registered BEFORE the work reaches
173
+ * the per-user queue. Registering eagerly is what lets `/cancel` retire a
174
+ * prompt that is still parked behind an earlier run — a controller created
175
+ * only at execution time would not exist yet.
176
+ */
177
+ private readonly pendingControllers;
178
+ private readonly queueTails;
179
+ /** Admitted work per user: at most one active turn plus `maxQueuedPerUser` waiting. */
180
+ private readonly admitted;
181
+ private stopping;
182
+ constructor(options: MessengerAdapterOptions);
183
+ /** Process one full webhook payload (`{ object: "page", entry: [...] }`). */
184
+ handleWebhookPayload(payload: unknown): Promise<MessengerEventResult[]>;
185
+ handleEvent(event: MessengerWebhookEvent): Promise<MessengerEventResult>;
186
+ /** Stop accepting work and abort every active turn. */
187
+ stop(reason?: unknown): void;
188
+ /**
189
+ * Proactive delivery. With `verbatim`, `text` is posted unchanged (no model
190
+ * call) and recorded to history; otherwise it runs as a turn for the user and
191
+ * the answer is delivered. Enforces the adapter allowlist.
192
+ */
193
+ notify(userId: string, text: string, options?: MessengerNotifyOptions): Promise<MessengerNotifyResult>;
194
+ /**
195
+ * Offer a wake to the active turn, having RESERVED this user's queue slot
196
+ * first.
197
+ *
198
+ * The `AgentLiveInputOffer` contract requires an accepted offer to stay
199
+ * represented by its reserved normal-turn slot until `settled` says whether
200
+ * that reservation runs or becomes a no-op. Offering first and enqueueing
201
+ * only afterwards lets a prompt that arrives during the offer overtake the
202
+ * wake; reserving first keeps arrival order. Every settlement path —
203
+ * accepted/applied, requeue, discard, uncertain, unavailable, and a throwing
204
+ * offer — resolves the reservation exactly once.
205
+ */
206
+ private steerOrRunReserved;
207
+ updateProcessJob(userId: string, projection: ProcessJobProjection): Promise<NotifyDeliveryResult>;
208
+ /** Create and register a controller for a user BEFORE the work is admitted. */
209
+ private registerController;
210
+ private unregisterController;
211
+ /** Abort every controller currently admitted for a user (active and parked). */
212
+ private cancelPending;
213
+ /** Append work to a user's serial queue, tracking admitted depth for the busy cap. */
214
+ private admit;
215
+ private respondToInbound;
216
+ private runProactiveTurn;
217
+ private deliverVerbatim;
218
+ private normalizeInbound;
219
+ /**
220
+ * Fetch one attachment under an explicit host policy.
221
+ *
222
+ * Redirects are followed MANUALLY so every hop is re-validated: the original
223
+ * URL being a signed Meta link says nothing about where a `Location` header
224
+ * points. Each hop must satisfy the host allowlist AND resolve entirely to
225
+ * public addresses, so neither an open redirect nor a hostname whose DNS
226
+ * answer is loopback/private/link-local can reach internal resources. The
227
+ * body is then read incrementally against the size cap, so a chunked
228
+ * response with an absent or lying `Content-Length` cannot exhaust memory.
229
+ */
230
+ private downloadAttachment;
231
+ /**
232
+ * Full per-hop admission check: the static URL policy, then DNS resolution
233
+ * with every returned address required to be public. Returns a short
234
+ * rejection reason, or `undefined` when the hop may be fetched.
235
+ */
236
+ private attachmentUrlRejection;
237
+ private createStream;
238
+ private proactiveSend;
239
+ private isAuthorized;
240
+ private finishCancelledUnlessAcknowledged;
241
+ private finishSafely;
242
+ private sendTextSafely;
243
+ }
244
+ /**
245
+ * Static URL policy for an attachment download: HTTPS, no embedded
246
+ * credentials, no literal IP host, and a hostname inside the configured
247
+ * allowlist. Returns a short rejection reason, or `undefined` when the URL
248
+ * passes. Address resolution is a separate, asynchronous check —
249
+ * see {@link MessengerAdapter.attachmentUrlRejection}.
250
+ */
251
+ export declare function attachmentUrlPolicyRejection(url: string, allowedHostSuffixes?: readonly string[]): string | undefined;
252
+ /** Only fetch https URLs on allowlisted Meta CDN hostnames. */
253
+ export declare function isSafeAttachmentUrl(url: string, allowedHostSuffixes?: readonly string[]): boolean;
254
+ /**
255
+ * True only for a globally routable unicast address. Everything else —
256
+ * loopback, private, link-local, CGNAT, multicast, and the reserved/documentation
257
+ * ranges — is rejected, so a hostname whose DNS answer points inside the
258
+ * deployment cannot be fetched.
259
+ */
260
+ export declare function isPublicUnicastAddress(address: string): boolean;
261
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAGA,OAAO,EAUL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,cAAc,IAAI,oBAAoB,EAC3C,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC/B,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,KAAK,EAAE,wBAAwB,EAAwB,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAA0B,KAAK,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAGhG,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAEhD,gEAAgE;AAChE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,kFAAkF;AAClF,wBAAgB,+BAA+B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO1F;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,WAAW,CAAC,EAAE;YAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1E,CAAC;CACH;AAED,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,CAAC;KAC9D,CAAC;IACF,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClG,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;CACxD;AAED,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,SAAS,EAAE,wBAAwB,CAAC;QAC7C,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACjC,CAAC;CACH;AAED,YAAY,EAAE,aAAa,EAAE,CAAC;AAC9B,MAAM,MAAM,cAAc,GAAG,oBAAoB,CAAC,YAAY,EAAE,kBAAkB,EAAE,aAAa,CAAC,CAAC;AAEnG,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAuB,SAAQ,4BAA4B;IAC1E,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClE;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,uFAAuF;IACvF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;CAC9E;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAC;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,gCAAgC,CAAC;IACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,yBAAyB,CAAC;IAC/C,8FAA8F;IAC9F,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,yBAAyB,CAAC;CAClD;AAED,MAAM,MAAM,sBAAsB,GAC9B,MAAM,GACN,SAAS,GACT,WAAW,GACX,YAAY,GACZ,WAAW,GACX,YAAY,CAAC;AAEjB,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,GACpH;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACxF;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAe3E;;;;GAIG;AACH,eAAO,MAAM,0CAA0C,EAAE,SAAS,MAAM,EAGvE,CAAC;AAgDF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAC5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6C;IACpE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA2C;IAC9E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAClE,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,uBAAuB;IAwB5C,6EAA6E;IACvE,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAyBvE,WAAW,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAgE9E,uDAAuD;IACvD,IAAI,CAAC,MAAM,GAAE,OAAuE,GAAG,IAAI;IAY3F;;;;OAIG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAkB5G;;;;;;;;;;;OAWG;YACW,kBAAkB;IA4E1B,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAavG,+EAA+E;IAC/E,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,oBAAoB;IAW5B,gFAAgF;IAChF,OAAO,CAAC,aAAa;IAMrB,sFAAsF;YACxE,KAAK;YAoBL,gBAAgB;YAwDhB,gBAAgB;YAsChB,eAAe;YAiDf,gBAAgB;IA+C9B;;;;;;;;;;OAUG;YACW,kBAAkB;IAmFhC;;;;OAIG;YACW,sBAAsB;IAoBpC,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,YAAY;YAIN,iCAAiC;YAQjC,YAAY;YAQZ,cAAc;CAO7B;AAkCD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,mBAAmB,GAAE,SAAS,MAAM,EAA+C,GAClF,MAAM,GAAG,SAAS,CA0BpB;AAED,+DAA+D;AAC/D,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,mBAAmB,GAAE,SAAS,MAAM,EAA+C,GAClF,OAAO,CAET;AAqFD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAS/D"}