@ariaflowagents/messaging 0.8.1
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 +165 -0
- package/dist/adapter/createMessagingRouter.d.ts +37 -0
- package/dist/adapter/createMessagingRouter.d.ts.map +1 -0
- package/dist/adapter/createMessagingRouter.js +115 -0
- package/dist/adapter/createMessagingRouter.js.map +1 -0
- package/dist/adapter/session-resolver.d.ts +13 -0
- package/dist/adapter/session-resolver.d.ts.map +1 -0
- package/dist/adapter/session-resolver.js +18 -0
- package/dist/adapter/session-resolver.js.map +1 -0
- package/dist/adapter/stream-mapper.d.ts +33 -0
- package/dist/adapter/stream-mapper.d.ts.map +1 -0
- package/dist/adapter/stream-mapper.js +117 -0
- package/dist/adapter/stream-mapper.js.map +1 -0
- package/dist/adapter/window-tracker.d.ts +57 -0
- package/dist/adapter/window-tracker.d.ts.map +1 -0
- package/dist/adapter/window-tracker.js +95 -0
- package/dist/adapter/window-tracker.js.map +1 -0
- package/dist/errors.d.ts +69 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +101 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/shared/deduplicator.d.ts +36 -0
- package/dist/shared/deduplicator.d.ts.map +1 -0
- package/dist/shared/deduplicator.js +80 -0
- package/dist/shared/deduplicator.js.map +1 -0
- package/dist/shared/format-base.d.ts +44 -0
- package/dist/shared/format-base.d.ts.map +1 -0
- package/dist/shared/format-base.js +49 -0
- package/dist/shared/format-base.js.map +1 -0
- package/dist/shared/media-cache.d.ts +96 -0
- package/dist/shared/media-cache.d.ts.map +1 -0
- package/dist/shared/media-cache.js +111 -0
- package/dist/shared/media-cache.js.map +1 -0
- package/dist/types.d.ts +428 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# @ariaflowagents/messaging
|
|
2
|
+
|
|
3
|
+
Core interfaces and Hono adapter for connecting messaging platforms to the AriaFlow runtime.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ariaflowagents/messaging
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Hono } from 'hono';
|
|
15
|
+
import { Runtime } from '@ariaflowagents/core';
|
|
16
|
+
import { createMessagingRouter } from '@ariaflowagents/messaging';
|
|
17
|
+
import { createWhatsAppClient } from '@ariaflowagents/messaging-meta/whatsapp';
|
|
18
|
+
|
|
19
|
+
const whatsapp = createWhatsAppClient({
|
|
20
|
+
accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
|
|
21
|
+
appSecret: process.env.META_APP_SECRET!,
|
|
22
|
+
phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID!,
|
|
23
|
+
verifyToken: process.env.WHATSAPP_VERIFY_TOKEN!,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const runtime = new Runtime({ agents: [/* ... */], defaultAgentId: 'support' });
|
|
27
|
+
|
|
28
|
+
const router = createMessagingRouter({
|
|
29
|
+
runtime,
|
|
30
|
+
platforms: { whatsapp },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const app = new Hono();
|
|
34
|
+
app.route('/messaging', router);
|
|
35
|
+
// Webhook URL: POST /messaging/whatsapp/webhook
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Core Concepts
|
|
39
|
+
|
|
40
|
+
- **`PlatformClient`** -- The interface every vendor package implements. Normalizes sending, receiving, media, webhooks, and format conversion into a single contract.
|
|
41
|
+
- **`SessionResolver`** -- Maps inbound messages to AriaFlow sessions. Default: `{platform}:{threadId}`.
|
|
42
|
+
- **`ResponseMapper`** -- Controls how runtime stream output is sent to the platform. Default sends accumulated text and renders suggested questions as interactive buttons.
|
|
43
|
+
- **`StreamMapper`** -- Consumes `AsyncIterable<HarnessStreamPart>`, sends typing indicators during streaming, delegates final output to a `ResponseMapper`.
|
|
44
|
+
|
|
45
|
+
## Error Handling
|
|
46
|
+
|
|
47
|
+
All errors extend `MessagingError` with a machine-readable `code` and `platform` field.
|
|
48
|
+
|
|
49
|
+
| Class | Code | When |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `RateLimitError` | `RATE_LIMIT` | Platform rate limit exceeded. Carries `retryAfterMs`. |
|
|
52
|
+
| `WindowClosedError` | `WINDOW_CLOSED` | 24h messaging window expired. Carries `suggestedTemplates`. |
|
|
53
|
+
| `AuthenticationError` | `AUTH_FAILED` | Invalid or expired API token. |
|
|
54
|
+
| `PermissionError` | `PERMISSION_DENIED` | Token lacks required permissions. |
|
|
55
|
+
| `RecipientError` | `RECIPIENT_ERROR` | Recipient invalid, blocked, or unreachable. |
|
|
56
|
+
| `TemplateError` | `TEMPLATE_ERROR` | Template message invalid or not approved. |
|
|
57
|
+
| `MediaError` | `MEDIA_ERROR` | Media upload/download/format failure. |
|
|
58
|
+
| `WebhookVerificationError` | `WEBHOOK_VERIFICATION_FAILED` | Webhook signature check failed. |
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { RateLimitError, WindowClosedError } from '@ariaflowagents/messaging';
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await client.sendText(to, text);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
if (e instanceof RateLimitError) {
|
|
67
|
+
await sleep(e.retryAfterMs);
|
|
68
|
+
}
|
|
69
|
+
if (e instanceof WindowClosedError) {
|
|
70
|
+
// Send a template message instead
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Shared Utilities
|
|
76
|
+
|
|
77
|
+
### MessageDeduplicator
|
|
78
|
+
|
|
79
|
+
LRU cache that prevents duplicate webhook processing. Used automatically by `createMessagingRouter`.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { MessageDeduplicator } from '@ariaflowagents/messaging';
|
|
83
|
+
|
|
84
|
+
const dedup = new MessageDeduplicator(10_000, 300_000); // maxSize, ttlMs
|
|
85
|
+
if (dedup.isDuplicate(messageId)) return;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### WindowTracker
|
|
89
|
+
|
|
90
|
+
Tracks 24-hour messaging windows per thread. Updated automatically from inbound messages and status webhooks.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { WindowTracker } from '@ariaflowagents/messaging';
|
|
94
|
+
|
|
95
|
+
const tracker = new WindowTracker();
|
|
96
|
+
tracker.recordInbound(threadId, new Date());
|
|
97
|
+
tracker.isWindowOpen(threadId); // true (within 24h)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Custom Session Resolver
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { createMessagingRouter } from '@ariaflowagents/messaging';
|
|
104
|
+
import type { SessionResolver } from '@ariaflowagents/messaging';
|
|
105
|
+
|
|
106
|
+
const resolver: SessionResolver = {
|
|
107
|
+
async resolve(message) {
|
|
108
|
+
const userId = await lookupUser(message.from.phone);
|
|
109
|
+
return { sessionId: `user:${userId}`, userId };
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const router = createMessagingRouter({
|
|
114
|
+
runtime,
|
|
115
|
+
platforms: { whatsapp },
|
|
116
|
+
sessionResolver: resolver,
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Custom Response Mapper
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { createMessagingRouter } from '@ariaflowagents/messaging';
|
|
124
|
+
import type { ResponseMapper, ResponseContext } from '@ariaflowagents/messaging';
|
|
125
|
+
import type { HarnessStreamPart } from '@ariaflowagents/core';
|
|
126
|
+
|
|
127
|
+
const mapper: ResponseMapper = {
|
|
128
|
+
async mapResponse(parts: HarnessStreamPart[], ctx: ResponseContext) {
|
|
129
|
+
const text = parts
|
|
130
|
+
.filter((p) => p.type === 'text-delta')
|
|
131
|
+
.map((p) => p.text)
|
|
132
|
+
.join('');
|
|
133
|
+
|
|
134
|
+
// Split long responses into multiple messages
|
|
135
|
+
for (const chunk of splitIntoChunks(text, 4096)) {
|
|
136
|
+
await ctx.sendText(chunk);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const router = createMessagingRouter({
|
|
142
|
+
runtime,
|
|
143
|
+
platforms: { whatsapp },
|
|
144
|
+
responseMapper: mapper,
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Multi-Platform
|
|
149
|
+
|
|
150
|
+
Pass multiple clients to `platforms`. Add `onStatus`, `onError`, and `fallbackMessage` for production use:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const router = createMessagingRouter({
|
|
154
|
+
runtime,
|
|
155
|
+
platforms: { whatsapp, messenger, instagram },
|
|
156
|
+
onStatus: (status) => console.log(`${status.status}: ${status.messageId}`),
|
|
157
|
+
onError: (err, ctx) => console.error(`[${ctx.platform}]`, err.message),
|
|
158
|
+
fallbackMessage: 'Something went wrong. Please try again later.',
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Related
|
|
163
|
+
|
|
164
|
+
- [`@ariaflowagents/messaging-meta`](../ariaflow-messaging-meta) -- WhatsApp, Messenger, and Instagram clients
|
|
165
|
+
- [`@ariaflowagents/core`](../ariaflow-core) -- Runtime, agents, flows
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { MessagingRouterConfig } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create a Hono router that bridges messaging platform webhooks with the AriaFlow runtime.
|
|
5
|
+
*
|
|
6
|
+
* For each platform in the config, this function:
|
|
7
|
+
* 1. Registers message, status, and reaction handlers on the platform client
|
|
8
|
+
* 2. Mounts GET and POST webhook routes at `/{platformName}/webhook`
|
|
9
|
+
*
|
|
10
|
+
* When a message arrives:
|
|
11
|
+
* 1. The platform client verifies the webhook signature, parses the payload,
|
|
12
|
+
* and dispatches to the registered message handler
|
|
13
|
+
* 2. The handler deduplicates the message, tracks the conversation window,
|
|
14
|
+
* resolves the session, and streams the runtime response
|
|
15
|
+
* 3. The stream mapper sends the response back through the platform client
|
|
16
|
+
*
|
|
17
|
+
* @param config - Router configuration with runtime, platforms, and optional customizations.
|
|
18
|
+
* @returns A Hono app with webhook routes mounted for each platform.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { createMessagingRouter } from '@ariaflowagents/messaging';
|
|
23
|
+
*
|
|
24
|
+
* const router = createMessagingRouter({
|
|
25
|
+
* runtime,
|
|
26
|
+
* platforms: {
|
|
27
|
+
* whatsapp: whatsappClient,
|
|
28
|
+
* messenger: messengerClient,
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Mount into your main Hono app
|
|
33
|
+
* app.route('/messaging', router);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMessagingRouter(config: MessagingRouterConfig): Hono;
|
|
37
|
+
//# sourceMappingURL=createMessagingRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMessagingRouter.d.ts","sourceRoot":"","sources":["../../src/adapter/createMessagingRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,qBAAqB,EAAgB,MAAM,aAAa,CAAC;AAMvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAyFzE"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { MessageDeduplicator } from '../shared/deduplicator.js';
|
|
3
|
+
import { WindowTracker } from './window-tracker.js';
|
|
4
|
+
import { defaultSessionResolver } from './session-resolver.js';
|
|
5
|
+
import { StreamMapper } from './stream-mapper.js';
|
|
6
|
+
/**
|
|
7
|
+
* Create a Hono router that bridges messaging platform webhooks with the AriaFlow runtime.
|
|
8
|
+
*
|
|
9
|
+
* For each platform in the config, this function:
|
|
10
|
+
* 1. Registers message, status, and reaction handlers on the platform client
|
|
11
|
+
* 2. Mounts GET and POST webhook routes at `/{platformName}/webhook`
|
|
12
|
+
*
|
|
13
|
+
* When a message arrives:
|
|
14
|
+
* 1. The platform client verifies the webhook signature, parses the payload,
|
|
15
|
+
* and dispatches to the registered message handler
|
|
16
|
+
* 2. The handler deduplicates the message, tracks the conversation window,
|
|
17
|
+
* resolves the session, and streams the runtime response
|
|
18
|
+
* 3. The stream mapper sends the response back through the platform client
|
|
19
|
+
*
|
|
20
|
+
* @param config - Router configuration with runtime, platforms, and optional customizations.
|
|
21
|
+
* @returns A Hono app with webhook routes mounted for each platform.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { createMessagingRouter } from '@ariaflowagents/messaging';
|
|
26
|
+
*
|
|
27
|
+
* const router = createMessagingRouter({
|
|
28
|
+
* runtime,
|
|
29
|
+
* platforms: {
|
|
30
|
+
* whatsapp: whatsappClient,
|
|
31
|
+
* messenger: messengerClient,
|
|
32
|
+
* },
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Mount into your main Hono app
|
|
36
|
+
* app.route('/messaging', router);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function createMessagingRouter(config) {
|
|
40
|
+
const app = new Hono();
|
|
41
|
+
const deduplicator = new MessageDeduplicator();
|
|
42
|
+
const windowTracker = new WindowTracker();
|
|
43
|
+
const sessionResolver = config.sessionResolver ?? defaultSessionResolver;
|
|
44
|
+
const streamMapper = new StreamMapper();
|
|
45
|
+
const fallbackMessage = config.fallbackMessage ?? "Sorry, I'm having trouble right now. Please try again.";
|
|
46
|
+
for (const [name, platform] of Object.entries(config.platforms)) {
|
|
47
|
+
// -------------------------------------------------------
|
|
48
|
+
// Register message handler
|
|
49
|
+
// -------------------------------------------------------
|
|
50
|
+
platform.onMessage(async (message) => {
|
|
51
|
+
// Deduplicate — webhooks can be retried by the platform
|
|
52
|
+
if (deduplicator.isDuplicate(message.id))
|
|
53
|
+
return;
|
|
54
|
+
// Track the messaging window
|
|
55
|
+
windowTracker.recordInbound(message.threadId, message.timestamp);
|
|
56
|
+
// Resolve AriaFlow session
|
|
57
|
+
const { sessionId, userId } = await sessionResolver.resolve(message);
|
|
58
|
+
// Extract text input — fall back to a type indicator for non-text messages
|
|
59
|
+
const input = message.text ?? `[${message.type}]`;
|
|
60
|
+
try {
|
|
61
|
+
// Stream from the AriaFlow runtime
|
|
62
|
+
const stream = config.runtime.stream({
|
|
63
|
+
input,
|
|
64
|
+
sessionId,
|
|
65
|
+
userId,
|
|
66
|
+
});
|
|
67
|
+
// Map the stream output to platform messages
|
|
68
|
+
await streamMapper.mapStream(stream, platform, message.threadId, {
|
|
69
|
+
responseMapper: config.responseMapper,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
// Attempt to send a fallback message
|
|
74
|
+
try {
|
|
75
|
+
await platform.sendText(message.threadId, fallbackMessage);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Cannot even send fallback — nothing more we can do
|
|
79
|
+
}
|
|
80
|
+
const errorContext = {
|
|
81
|
+
message,
|
|
82
|
+
platform: name,
|
|
83
|
+
error: error,
|
|
84
|
+
};
|
|
85
|
+
config.onError?.(error, errorContext);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// -------------------------------------------------------
|
|
89
|
+
// Register status handler
|
|
90
|
+
// -------------------------------------------------------
|
|
91
|
+
platform.onStatus(async (status) => {
|
|
92
|
+
// Track window expiry from platform-reported conversation info.
|
|
93
|
+
// Use status.threadId (set by platform clients in the same format as
|
|
94
|
+
// inbound messages) so the window tracker key matches recordInbound().
|
|
95
|
+
if (status.conversation?.expirationTimestamp && status.threadId) {
|
|
96
|
+
windowTracker.recordExpiry(status.threadId, status.conversation.expirationTimestamp);
|
|
97
|
+
}
|
|
98
|
+
// Forward to user-provided status handler
|
|
99
|
+
config.onStatus?.(status);
|
|
100
|
+
});
|
|
101
|
+
// -------------------------------------------------------
|
|
102
|
+
// Mount webhook routes
|
|
103
|
+
// -------------------------------------------------------
|
|
104
|
+
// GET — webhook verification (e.g. Meta's hub.verify_token challenge)
|
|
105
|
+
app.get(`/${name}/webhook`, async (c) => {
|
|
106
|
+
return platform.handleWebhook(c.req.raw);
|
|
107
|
+
});
|
|
108
|
+
// POST — incoming events (messages, statuses, reactions)
|
|
109
|
+
app.post(`/${name}/webhook`, async (c) => {
|
|
110
|
+
return platform.handleWebhook(c.req.raw);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return app;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=createMessagingRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMessagingRouter.js","sourceRoot":"","sources":["../../src/adapter/createMessagingRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SessionResolver } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default session resolver that maps inbound messages to AriaFlow sessions.
|
|
4
|
+
*
|
|
5
|
+
* Session ID format: `{platform}:{threadId}` — ensures uniqueness across
|
|
6
|
+
* platforms even when thread IDs collide (e.g. phone numbers used as both
|
|
7
|
+
* WhatsApp and SMS thread IDs).
|
|
8
|
+
*
|
|
9
|
+
* User ID is taken from the message sender's contact ID.
|
|
10
|
+
*/
|
|
11
|
+
export declare const defaultSessionResolver: SessionResolver;
|
|
12
|
+
export type { SessionResolver } from '../types.js';
|
|
13
|
+
//# sourceMappingURL=session-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-resolver.d.ts","sourceRoot":"","sources":["../../src/adapter/session-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAOpC,CAAC;AAGF,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default session resolver that maps inbound messages to AriaFlow sessions.
|
|
3
|
+
*
|
|
4
|
+
* Session ID format: `{platform}:{threadId}` — ensures uniqueness across
|
|
5
|
+
* platforms even when thread IDs collide (e.g. phone numbers used as both
|
|
6
|
+
* WhatsApp and SMS thread IDs).
|
|
7
|
+
*
|
|
8
|
+
* User ID is taken from the message sender's contact ID.
|
|
9
|
+
*/
|
|
10
|
+
export const defaultSessionResolver = {
|
|
11
|
+
async resolve(message) {
|
|
12
|
+
return {
|
|
13
|
+
sessionId: `${message.platform}:${message.threadId}`,
|
|
14
|
+
userId: message.from.id,
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=session-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-resolver.js","sourceRoot":"","sources":["../../src/adapter/session-resolver.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAoB;IACrD,KAAK,CAAC,OAAO,CAAC,OAAuB;QACnC,OAAO;YACL,SAAS,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpD,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;SACxB,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HarnessStreamPart } from '@ariaflowagents/core';
|
|
2
|
+
import type { PlatformClient, StreamMapperOptions } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Maps an AriaFlow runtime stream to platform messages.
|
|
5
|
+
*
|
|
6
|
+
* The mapper:
|
|
7
|
+
* 1. Starts a typing indicator interval on the platform
|
|
8
|
+
* 2. Buffers all `text-delta` events into a complete response string
|
|
9
|
+
* 3. Collects metadata events (suggested-questions, handoff, flow-end, done)
|
|
10
|
+
* 4. When the stream completes, either delegates to a custom `ResponseMapper`
|
|
11
|
+
* or uses the default behavior:
|
|
12
|
+
* - Sends the accumulated text via `platform.sendText()`
|
|
13
|
+
* - If suggested questions exist, sends them as interactive buttons
|
|
14
|
+
* 5. Clears the typing indicator
|
|
15
|
+
* 6. Returns all collected stream parts
|
|
16
|
+
*/
|
|
17
|
+
export declare class StreamMapper {
|
|
18
|
+
/**
|
|
19
|
+
* Consume an AriaFlow runtime stream and send the response to a platform.
|
|
20
|
+
*
|
|
21
|
+
* @param stream - The async iterable from `runtime.stream()`.
|
|
22
|
+
* @param platform - The platform client to send messages through.
|
|
23
|
+
* @param threadId - The thread to send responses to.
|
|
24
|
+
* @param options - Optional configuration for response mapping and typing interval.
|
|
25
|
+
* @returns All collected stream parts for inspection or logging.
|
|
26
|
+
*/
|
|
27
|
+
mapStream(stream: AsyncIterable<HarnessStreamPart>, platform: PlatformClient, threadId: string, options?: StreamMapperOptions): Promise<HarnessStreamPart[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Default response mapping: send accumulated text and optional suggested questions.
|
|
30
|
+
*/
|
|
31
|
+
private defaultMapResponse;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=stream-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-mapper.d.ts","sourceRoot":"","sources":["../../src/adapter/stream-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EACV,cAAc,EAEd,mBAAmB,EAEpB,MAAM,aAAa,CAAC;AAKrB;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY;IACvB;;;;;;;;OAQG;IACG,SAAS,CACb,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,EACxC,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA6D/B;;OAEG;YACW,kBAAkB;CAuCjC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** Default interval for sending typing indicators during streaming (ms). */
|
|
2
|
+
const DEFAULT_TYPING_INTERVAL_MS = 5_000;
|
|
3
|
+
/**
|
|
4
|
+
* Maps an AriaFlow runtime stream to platform messages.
|
|
5
|
+
*
|
|
6
|
+
* The mapper:
|
|
7
|
+
* 1. Starts a typing indicator interval on the platform
|
|
8
|
+
* 2. Buffers all `text-delta` events into a complete response string
|
|
9
|
+
* 3. Collects metadata events (suggested-questions, handoff, flow-end, done)
|
|
10
|
+
* 4. When the stream completes, either delegates to a custom `ResponseMapper`
|
|
11
|
+
* or uses the default behavior:
|
|
12
|
+
* - Sends the accumulated text via `platform.sendText()`
|
|
13
|
+
* - If suggested questions exist, sends them as interactive buttons
|
|
14
|
+
* 5. Clears the typing indicator
|
|
15
|
+
* 6. Returns all collected stream parts
|
|
16
|
+
*/
|
|
17
|
+
export class StreamMapper {
|
|
18
|
+
/**
|
|
19
|
+
* Consume an AriaFlow runtime stream and send the response to a platform.
|
|
20
|
+
*
|
|
21
|
+
* @param stream - The async iterable from `runtime.stream()`.
|
|
22
|
+
* @param platform - The platform client to send messages through.
|
|
23
|
+
* @param threadId - The thread to send responses to.
|
|
24
|
+
* @param options - Optional configuration for response mapping and typing interval.
|
|
25
|
+
* @returns All collected stream parts for inspection or logging.
|
|
26
|
+
*/
|
|
27
|
+
async mapStream(stream, platform, threadId, options) {
|
|
28
|
+
const parts = [];
|
|
29
|
+
let textBuffer = '';
|
|
30
|
+
const typingIntervalMs = options?.typingIntervalMs ?? DEFAULT_TYPING_INTERVAL_MS;
|
|
31
|
+
// Start typing indicator loop
|
|
32
|
+
let typingActive = true;
|
|
33
|
+
const typingInterval = setInterval(async () => {
|
|
34
|
+
if (!typingActive)
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
await platform.sendTypingIndicator(threadId);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Typing indicator failures are non-critical — silently ignore
|
|
41
|
+
}
|
|
42
|
+
}, typingIntervalMs);
|
|
43
|
+
// Send an initial typing indicator immediately
|
|
44
|
+
try {
|
|
45
|
+
await platform.sendTypingIndicator(threadId);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Non-critical
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
for await (const part of stream) {
|
|
52
|
+
parts.push(part);
|
|
53
|
+
switch (part.type) {
|
|
54
|
+
case 'text-delta':
|
|
55
|
+
textBuffer += part.text;
|
|
56
|
+
break;
|
|
57
|
+
// All other event types are collected for the response mapper
|
|
58
|
+
// but don't require special handling during streaming
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Stop typing indicator
|
|
62
|
+
typingActive = false;
|
|
63
|
+
clearInterval(typingInterval);
|
|
64
|
+
// Delegate to custom response mapper or use default behavior
|
|
65
|
+
if (options?.responseMapper) {
|
|
66
|
+
await options.responseMapper.mapResponse(parts, {
|
|
67
|
+
threadId,
|
|
68
|
+
platform: platform.platform,
|
|
69
|
+
sendText: (text) => platform.sendText(threadId, text),
|
|
70
|
+
sendInteractive: (msg) => platform.sendInteractive(threadId, msg),
|
|
71
|
+
sendMedia: (media) => platform.sendMedia(threadId, media),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
await this.defaultMapResponse(platform, threadId, textBuffer, parts);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
// Ensure typing indicator is always cleaned up
|
|
80
|
+
typingActive = false;
|
|
81
|
+
clearInterval(typingInterval);
|
|
82
|
+
}
|
|
83
|
+
return parts;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Default response mapping: send accumulated text and optional suggested questions.
|
|
87
|
+
*/
|
|
88
|
+
async defaultMapResponse(platform, threadId, text, parts) {
|
|
89
|
+
// Send the accumulated text response
|
|
90
|
+
if (text.trim().length > 0) {
|
|
91
|
+
const formatted = platform.formatConverter.toPlatformFormat(text);
|
|
92
|
+
await platform.sendText(threadId, formatted);
|
|
93
|
+
}
|
|
94
|
+
// Check for suggested questions
|
|
95
|
+
const suggestedPart = parts.find((p) => p.type === 'suggested-questions');
|
|
96
|
+
if (suggestedPart && suggestedPart.suggestions.length > 0) {
|
|
97
|
+
// Limit to 3 buttons (most platforms cap at 3)
|
|
98
|
+
const buttons = suggestedPart.suggestions.slice(0, 3).map((suggestion, i) => ({
|
|
99
|
+
id: `suggestion_${i}`,
|
|
100
|
+
title: suggestion.length > 20 ? suggestion.substring(0, 17) + '...' : suggestion,
|
|
101
|
+
}));
|
|
102
|
+
const interactive = {
|
|
103
|
+
type: 'buttons',
|
|
104
|
+
body: 'You might also want to ask:',
|
|
105
|
+
action: { type: 'buttons', buttons },
|
|
106
|
+
};
|
|
107
|
+
try {
|
|
108
|
+
await platform.sendInteractive(threadId, interactive);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// If interactive messages aren't supported, silently skip
|
|
112
|
+
// The text response was already sent
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=stream-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-mapper.js","sourceRoot":"","sources":["../../src/adapter/stream-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the 24-hour messaging window for each conversation thread.
|
|
3
|
+
*
|
|
4
|
+
* Many messaging platforms (notably WhatsApp) only allow free-form messages
|
|
5
|
+
* within a window that starts when the user last messaged. After the window
|
|
6
|
+
* closes, only template messages can be sent.
|
|
7
|
+
*
|
|
8
|
+
* The tracker records:
|
|
9
|
+
* - Inbound message timestamps (sets window to `timestamp + 24h`)
|
|
10
|
+
* - Platform-reported expiry times (from status webhooks, more accurate)
|
|
11
|
+
*
|
|
12
|
+
* Stale entries are periodically cleaned up to prevent unbounded memory growth.
|
|
13
|
+
*/
|
|
14
|
+
export declare class WindowTracker {
|
|
15
|
+
private readonly windows;
|
|
16
|
+
private lastCleanup;
|
|
17
|
+
/**
|
|
18
|
+
* Record an inbound message, opening or extending the messaging window.
|
|
19
|
+
* The window is set to 24 hours from the message timestamp.
|
|
20
|
+
*
|
|
21
|
+
* @param threadId - The conversation thread identifier.
|
|
22
|
+
* @param timestamp - When the user's message was sent.
|
|
23
|
+
*/
|
|
24
|
+
recordInbound(threadId: string, timestamp: Date): void;
|
|
25
|
+
/**
|
|
26
|
+
* Record a platform-reported window expiry (e.g. from a WhatsApp status webhook).
|
|
27
|
+
* Platform-reported values are more accurate than computed ones.
|
|
28
|
+
*
|
|
29
|
+
* @param threadId - The conversation thread identifier.
|
|
30
|
+
* @param expiresAt - The platform-reported expiration timestamp.
|
|
31
|
+
*/
|
|
32
|
+
recordExpiry(threadId: string, expiresAt: Date): void;
|
|
33
|
+
/**
|
|
34
|
+
* Check whether the messaging window is currently open for a thread.
|
|
35
|
+
*
|
|
36
|
+
* @param threadId - The conversation thread identifier.
|
|
37
|
+
* @returns `true` if the window is open and free-form messages can be sent.
|
|
38
|
+
*/
|
|
39
|
+
isWindowOpen(threadId: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get the window expiry timestamp for a thread.
|
|
42
|
+
*
|
|
43
|
+
* @param threadId - The conversation thread identifier.
|
|
44
|
+
* @returns The expiry date, or `null` if no window is tracked.
|
|
45
|
+
*/
|
|
46
|
+
getExpiry(threadId: string): Date | null;
|
|
47
|
+
/**
|
|
48
|
+
* Run cleanup if enough time has passed since the last cleanup.
|
|
49
|
+
* Removes all expired entries to prevent unbounded memory growth.
|
|
50
|
+
*/
|
|
51
|
+
private maybeCleanup;
|
|
52
|
+
/** Return the number of tracked windows. */
|
|
53
|
+
get size(): number;
|
|
54
|
+
/** Clear all tracked windows. */
|
|
55
|
+
clear(): void;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=window-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window-tracker.d.ts","sourceRoot":"","sources":["../../src/adapter/window-tracker.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,WAAW,CAAsB;IAEzC;;;;;;OAMG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI;IAWtD;;;;;;OAMG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI;IAKrD;;;;;OAKG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAMvC;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKxC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAapB,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,iCAAiC;IACjC,KAAK,IAAI,IAAI;CAGd"}
|