@effect-uai/core 0.14.0 → 0.15.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/MessengerError-CeK6VAcu.d.mts +64 -0
- package/dist/MessengerError-CeK6VAcu.d.mts.map +1 -0
- package/dist/inbox/Inbox.d.mts +16 -0
- package/dist/inbox/Inbox.d.mts.map +1 -0
- package/dist/inbox/Inbox.mjs +28 -0
- package/dist/inbox/Inbox.mjs.map +1 -0
- package/dist/inbox/Inbox.test.d.mts +1 -0
- package/dist/inbox/Inbox.test.mjs +42 -0
- package/dist/inbox/Inbox.test.mjs.map +1 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.mjs +5 -1
- package/dist/messenger/Messenger.d.mts +472 -0
- package/dist/messenger/Messenger.d.mts.map +1 -0
- package/dist/messenger/Messenger.mjs +66 -0
- package/dist/messenger/Messenger.mjs.map +1 -0
- package/dist/messenger/MessengerAdapter.d.mts +35 -0
- package/dist/messenger/MessengerAdapter.d.mts.map +1 -0
- package/dist/messenger/MessengerAdapter.mjs +102 -0
- package/dist/messenger/MessengerAdapter.mjs.map +1 -0
- package/dist/messenger/MessengerAdapter.test.d.mts +1 -0
- package/dist/messenger/MessengerAdapter.test.mjs +138 -0
- package/dist/messenger/MessengerAdapter.test.mjs.map +1 -0
- package/dist/messenger/MessengerError.d.mts +2 -0
- package/dist/messenger/MessengerError.mjs +39 -0
- package/dist/messenger/MessengerError.mjs.map +1 -0
- package/dist/testing/MockMessenger.d.mts +192 -0
- package/dist/testing/MockMessenger.d.mts.map +1 -0
- package/dist/testing/MockMessenger.mjs +76 -0
- package/dist/testing/MockMessenger.mjs.map +1 -0
- package/package.json +21 -1
- package/src/inbox/Inbox.test.ts +52 -0
- package/src/inbox/Inbox.ts +33 -0
- package/src/index.ts +4 -0
- package/src/messenger/Messenger.ts +293 -0
- package/src/messenger/MessengerAdapter.test.ts +179 -0
- package/src/messenger/MessengerAdapter.ts +189 -0
- package/src/messenger/MessengerError.ts +67 -0
- package/src/testing/MockMessenger.ts +147 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { a as MediaSource } from "../Media-BbX7BAaO.mjs";
|
|
2
|
+
import { n as MessengerError } from "../MessengerError-CeK6VAcu.mjs";
|
|
3
|
+
import { Brand, Context, Data, Effect, Option, Scope, Stream } from "effect";
|
|
4
|
+
declare namespace Messenger_d_exports {
|
|
5
|
+
export { ChannelId, ConversationRef, CurrentConversation, InboundEvent, MessageId, MessageRef, Messenger, MessengerLimits, MessengerService, Outbound, OutboundBody, OutboundOptions, StreamOptions, ThreadId, UserId, conversationKey, edit, events, inConversation, media, post, raw, react, stream, text, typing };
|
|
6
|
+
}
|
|
7
|
+
/** Where messages go. Providers mint these per their own scheme; core treats them opaquely. */
|
|
8
|
+
type ChannelId = Brand.Branded<string, "ChannelId">;
|
|
9
|
+
declare const ChannelId: Brand.Constructor<ChannelId>;
|
|
10
|
+
/** One message inside a channel. Only a named message can be edited or reacted to. */
|
|
11
|
+
type MessageId = Brand.Branded<string, "MessageId">;
|
|
12
|
+
declare const MessageId: Brand.Constructor<MessageId>;
|
|
13
|
+
/** The human (or bot) an event came from. */
|
|
14
|
+
type UserId = Brand.Branded<string, "UserId">;
|
|
15
|
+
declare const UserId: Brand.Constructor<UserId>;
|
|
16
|
+
/** A sub-conversation inside a channel, where the platform has one. Opaque, provider-minted. */
|
|
17
|
+
type ThreadId = Brand.Branded<string, "ThreadId">;
|
|
18
|
+
declare const ThreadId: Brand.Constructor<ThreadId>;
|
|
19
|
+
/**
|
|
20
|
+
* Where a conversation happens. `thread` is provider-interpreted: a Slack
|
|
21
|
+
* `thread_ts`, a Telegram forum topic, unset on Discord (where a thread is
|
|
22
|
+
* its own channel).
|
|
23
|
+
*/
|
|
24
|
+
type ConversationRef = {
|
|
25
|
+
readonly channel: ChannelId;
|
|
26
|
+
readonly thread?: ThreadId;
|
|
27
|
+
};
|
|
28
|
+
type MessageRef = {
|
|
29
|
+
readonly conversation: ConversationRef;
|
|
30
|
+
readonly id: MessageId;
|
|
31
|
+
};
|
|
32
|
+
/** Stable key for a conversation. Index per-conversation state with it. */
|
|
33
|
+
declare const conversationKey: (ref: ConversationRef) => string;
|
|
34
|
+
/**
|
|
35
|
+
* What the world says to the agent. `raw` is the untouched platform payload,
|
|
36
|
+
* for everything the shared shape drops.
|
|
37
|
+
*
|
|
38
|
+
* `addressed` is the rule recipes branch on: a DM, a mention of the bot, or
|
|
39
|
+
* a reply to one of its messages. Each adapter owns that decision (and hides
|
|
40
|
+
* Telegram privacy mode / Discord intents behind it), and delivers `text`
|
|
41
|
+
* with the bot's own mention stripped.
|
|
42
|
+
*
|
|
43
|
+
* `Reaction.emoji` is the platform's own spelling, the same one `react`
|
|
44
|
+
* accepts: unicode where the platform speaks unicode, a shortcode where it
|
|
45
|
+
* speaks shortcodes.
|
|
46
|
+
*/
|
|
47
|
+
type InboundEvent = Data.TaggedEnum<{
|
|
48
|
+
Message: {
|
|
49
|
+
readonly conversation: ConversationRef;
|
|
50
|
+
readonly id: MessageId;
|
|
51
|
+
readonly author: UserId;
|
|
52
|
+
readonly text: string;
|
|
53
|
+
readonly addressed: boolean;
|
|
54
|
+
readonly replyTo?: MessageId;
|
|
55
|
+
readonly raw: unknown;
|
|
56
|
+
};
|
|
57
|
+
Reaction: {
|
|
58
|
+
readonly conversation: ConversationRef;
|
|
59
|
+
readonly message: MessageId;
|
|
60
|
+
readonly emoji: string;
|
|
61
|
+
readonly author: UserId;
|
|
62
|
+
readonly raw: unknown;
|
|
63
|
+
};
|
|
64
|
+
Command: {
|
|
65
|
+
readonly conversation: ConversationRef;
|
|
66
|
+
readonly name: string;
|
|
67
|
+
readonly args: string;
|
|
68
|
+
readonly author: UserId;
|
|
69
|
+
readonly raw: unknown;
|
|
70
|
+
};
|
|
71
|
+
Action: {
|
|
72
|
+
readonly conversation: ConversationRef;
|
|
73
|
+
readonly actionId: string;
|
|
74
|
+
readonly value?: string;
|
|
75
|
+
readonly author: UserId;
|
|
76
|
+
readonly raw: unknown;
|
|
77
|
+
};
|
|
78
|
+
}>;
|
|
79
|
+
declare const InboundEvent: {
|
|
80
|
+
readonly $is: <Tag extends "Action" | "Command" | "Message" | "Reaction">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
81
|
+
readonly _tag: "Action";
|
|
82
|
+
readonly conversation: ConversationRef;
|
|
83
|
+
readonly actionId: string;
|
|
84
|
+
readonly value?: string;
|
|
85
|
+
readonly author: UserId;
|
|
86
|
+
readonly raw: unknown;
|
|
87
|
+
}, {
|
|
88
|
+
readonly _tag: Tag;
|
|
89
|
+
}> | Extract<{
|
|
90
|
+
readonly _tag: "Command";
|
|
91
|
+
readonly conversation: ConversationRef;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
readonly args: string;
|
|
94
|
+
readonly author: UserId;
|
|
95
|
+
readonly raw: unknown;
|
|
96
|
+
}, {
|
|
97
|
+
readonly _tag: Tag;
|
|
98
|
+
}> | Extract<{
|
|
99
|
+
readonly _tag: "Message";
|
|
100
|
+
readonly conversation: ConversationRef;
|
|
101
|
+
readonly id: MessageId;
|
|
102
|
+
readonly author: UserId;
|
|
103
|
+
readonly text: string;
|
|
104
|
+
readonly addressed: boolean;
|
|
105
|
+
readonly replyTo?: MessageId;
|
|
106
|
+
readonly raw: unknown;
|
|
107
|
+
}, {
|
|
108
|
+
readonly _tag: Tag;
|
|
109
|
+
}> | Extract<{
|
|
110
|
+
readonly _tag: "Reaction";
|
|
111
|
+
readonly conversation: ConversationRef;
|
|
112
|
+
readonly message: MessageId;
|
|
113
|
+
readonly emoji: string;
|
|
114
|
+
readonly author: UserId;
|
|
115
|
+
readonly raw: unknown;
|
|
116
|
+
}, {
|
|
117
|
+
readonly _tag: Tag;
|
|
118
|
+
}>;
|
|
119
|
+
readonly $match: {
|
|
120
|
+
<Cases extends { readonly [Tag in "Action" | "Command" | "Message" | "Reaction"]: (args: Extract<{
|
|
121
|
+
readonly _tag: "Action";
|
|
122
|
+
readonly conversation: ConversationRef;
|
|
123
|
+
readonly actionId: string;
|
|
124
|
+
readonly value?: string;
|
|
125
|
+
readonly author: UserId;
|
|
126
|
+
readonly raw: unknown;
|
|
127
|
+
} | {
|
|
128
|
+
readonly _tag: "Command";
|
|
129
|
+
readonly conversation: ConversationRef;
|
|
130
|
+
readonly name: string;
|
|
131
|
+
readonly args: string;
|
|
132
|
+
readonly author: UserId;
|
|
133
|
+
readonly raw: unknown;
|
|
134
|
+
} | {
|
|
135
|
+
readonly _tag: "Message";
|
|
136
|
+
readonly conversation: ConversationRef;
|
|
137
|
+
readonly id: MessageId;
|
|
138
|
+
readonly author: UserId;
|
|
139
|
+
readonly text: string;
|
|
140
|
+
readonly addressed: boolean;
|
|
141
|
+
readonly replyTo?: MessageId;
|
|
142
|
+
readonly raw: unknown;
|
|
143
|
+
} | {
|
|
144
|
+
readonly _tag: "Reaction";
|
|
145
|
+
readonly conversation: ConversationRef;
|
|
146
|
+
readonly message: MessageId;
|
|
147
|
+
readonly emoji: string;
|
|
148
|
+
readonly author: UserId;
|
|
149
|
+
readonly raw: unknown;
|
|
150
|
+
}, {
|
|
151
|
+
readonly _tag: Tag;
|
|
152
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
153
|
+
readonly _tag: "Action";
|
|
154
|
+
readonly conversation: ConversationRef;
|
|
155
|
+
readonly actionId: string;
|
|
156
|
+
readonly value?: string;
|
|
157
|
+
readonly author: UserId;
|
|
158
|
+
readonly raw: unknown;
|
|
159
|
+
} | {
|
|
160
|
+
readonly _tag: "Command";
|
|
161
|
+
readonly conversation: ConversationRef;
|
|
162
|
+
readonly name: string;
|
|
163
|
+
readonly args: string;
|
|
164
|
+
readonly author: UserId;
|
|
165
|
+
readonly raw: unknown;
|
|
166
|
+
} | {
|
|
167
|
+
readonly _tag: "Message";
|
|
168
|
+
readonly conversation: ConversationRef;
|
|
169
|
+
readonly id: MessageId;
|
|
170
|
+
readonly author: UserId;
|
|
171
|
+
readonly text: string;
|
|
172
|
+
readonly addressed: boolean;
|
|
173
|
+
readonly replyTo?: MessageId;
|
|
174
|
+
readonly raw: unknown;
|
|
175
|
+
} | {
|
|
176
|
+
readonly _tag: "Reaction";
|
|
177
|
+
readonly conversation: ConversationRef;
|
|
178
|
+
readonly message: MessageId;
|
|
179
|
+
readonly emoji: string;
|
|
180
|
+
readonly author: UserId;
|
|
181
|
+
readonly raw: unknown;
|
|
182
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Action" | "Command" | "Message" | "Reaction"]>>;
|
|
183
|
+
<Cases extends { readonly [Tag in "Action" | "Command" | "Message" | "Reaction"]: (args: Extract<{
|
|
184
|
+
readonly _tag: "Action";
|
|
185
|
+
readonly conversation: ConversationRef;
|
|
186
|
+
readonly actionId: string;
|
|
187
|
+
readonly value?: string;
|
|
188
|
+
readonly author: UserId;
|
|
189
|
+
readonly raw: unknown;
|
|
190
|
+
} | {
|
|
191
|
+
readonly _tag: "Command";
|
|
192
|
+
readonly conversation: ConversationRef;
|
|
193
|
+
readonly name: string;
|
|
194
|
+
readonly args: string;
|
|
195
|
+
readonly author: UserId;
|
|
196
|
+
readonly raw: unknown;
|
|
197
|
+
} | {
|
|
198
|
+
readonly _tag: "Message";
|
|
199
|
+
readonly conversation: ConversationRef;
|
|
200
|
+
readonly id: MessageId;
|
|
201
|
+
readonly author: UserId;
|
|
202
|
+
readonly text: string;
|
|
203
|
+
readonly addressed: boolean;
|
|
204
|
+
readonly replyTo?: MessageId;
|
|
205
|
+
readonly raw: unknown;
|
|
206
|
+
} | {
|
|
207
|
+
readonly _tag: "Reaction";
|
|
208
|
+
readonly conversation: ConversationRef;
|
|
209
|
+
readonly message: MessageId;
|
|
210
|
+
readonly emoji: string;
|
|
211
|
+
readonly author: UserId;
|
|
212
|
+
readonly raw: unknown;
|
|
213
|
+
}, {
|
|
214
|
+
readonly _tag: Tag;
|
|
215
|
+
}>) => any; }>(value: {
|
|
216
|
+
readonly _tag: "Action";
|
|
217
|
+
readonly conversation: ConversationRef;
|
|
218
|
+
readonly actionId: string;
|
|
219
|
+
readonly value?: string;
|
|
220
|
+
readonly author: UserId;
|
|
221
|
+
readonly raw: unknown;
|
|
222
|
+
} | {
|
|
223
|
+
readonly _tag: "Command";
|
|
224
|
+
readonly conversation: ConversationRef;
|
|
225
|
+
readonly name: string;
|
|
226
|
+
readonly args: string;
|
|
227
|
+
readonly author: UserId;
|
|
228
|
+
readonly raw: unknown;
|
|
229
|
+
} | {
|
|
230
|
+
readonly _tag: "Message";
|
|
231
|
+
readonly conversation: ConversationRef;
|
|
232
|
+
readonly id: MessageId;
|
|
233
|
+
readonly author: UserId;
|
|
234
|
+
readonly text: string;
|
|
235
|
+
readonly addressed: boolean;
|
|
236
|
+
readonly replyTo?: MessageId;
|
|
237
|
+
readonly raw: unknown;
|
|
238
|
+
} | {
|
|
239
|
+
readonly _tag: "Reaction";
|
|
240
|
+
readonly conversation: ConversationRef;
|
|
241
|
+
readonly message: MessageId;
|
|
242
|
+
readonly emoji: string;
|
|
243
|
+
readonly author: UserId;
|
|
244
|
+
readonly raw: unknown;
|
|
245
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Action" | "Command" | "Message" | "Reaction"]>>;
|
|
246
|
+
};
|
|
247
|
+
readonly Action: Data.TaggedEnum.ConstructorFrom<{
|
|
248
|
+
readonly _tag: "Action";
|
|
249
|
+
readonly conversation: ConversationRef;
|
|
250
|
+
readonly actionId: string;
|
|
251
|
+
readonly value?: string;
|
|
252
|
+
readonly author: UserId;
|
|
253
|
+
readonly raw: unknown;
|
|
254
|
+
}, "_tag">;
|
|
255
|
+
readonly Command: Data.TaggedEnum.ConstructorFrom<{
|
|
256
|
+
readonly _tag: "Command";
|
|
257
|
+
readonly conversation: ConversationRef;
|
|
258
|
+
readonly name: string;
|
|
259
|
+
readonly args: string;
|
|
260
|
+
readonly author: UserId;
|
|
261
|
+
readonly raw: unknown;
|
|
262
|
+
}, "_tag">;
|
|
263
|
+
readonly Message: Data.TaggedEnum.ConstructorFrom<{
|
|
264
|
+
readonly _tag: "Message";
|
|
265
|
+
readonly conversation: ConversationRef;
|
|
266
|
+
readonly id: MessageId;
|
|
267
|
+
readonly author: UserId;
|
|
268
|
+
readonly text: string;
|
|
269
|
+
readonly addressed: boolean;
|
|
270
|
+
readonly replyTo?: MessageId;
|
|
271
|
+
readonly raw: unknown;
|
|
272
|
+
}, "_tag">;
|
|
273
|
+
readonly Reaction: Data.TaggedEnum.ConstructorFrom<{
|
|
274
|
+
readonly _tag: "Reaction";
|
|
275
|
+
readonly conversation: ConversationRef;
|
|
276
|
+
readonly message: MessageId;
|
|
277
|
+
readonly emoji: string;
|
|
278
|
+
readonly author: UserId;
|
|
279
|
+
readonly raw: unknown;
|
|
280
|
+
}, "_tag">;
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* What the agent says. `Text` is sent verbatim: each provider layer says
|
|
284
|
+
* which markup it expects (Telegram HTML, Slack markdown), and the prompt is
|
|
285
|
+
* where that gets decided. Nothing here converts. `Media` reuses the core
|
|
286
|
+
* {@link MediaSource}, so a URL, base64 or raw bytes all work; the adapter
|
|
287
|
+
* routes on `mimeType` (`image/*` to a photo endpoint, `audio/*` to an audio
|
|
288
|
+
* one, everything else to a file upload), which is why a URL source without
|
|
289
|
+
* a `mimeType` goes as a plain file. `Raw` bypasses the shape entirely and
|
|
290
|
+
* reaches the platform as-is: the escape hatch for buttons, cards and
|
|
291
|
+
* everything the verbs do not unify.
|
|
292
|
+
*/
|
|
293
|
+
type OutboundBody = Data.TaggedEnum<{
|
|
294
|
+
Text: {
|
|
295
|
+
readonly text: string;
|
|
296
|
+
};
|
|
297
|
+
Media: {
|
|
298
|
+
readonly media: MediaSource;
|
|
299
|
+
readonly caption?: string;
|
|
300
|
+
readonly filename?: string;
|
|
301
|
+
};
|
|
302
|
+
Raw: {
|
|
303
|
+
readonly payload: unknown;
|
|
304
|
+
};
|
|
305
|
+
}>;
|
|
306
|
+
declare const OutboundBody: {
|
|
307
|
+
readonly $is: <Tag extends "Media" | "Raw" | "Text">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
308
|
+
readonly _tag: "Media";
|
|
309
|
+
readonly media: MediaSource;
|
|
310
|
+
readonly caption?: string;
|
|
311
|
+
readonly filename?: string;
|
|
312
|
+
}, {
|
|
313
|
+
readonly _tag: Tag;
|
|
314
|
+
}> | Extract<{
|
|
315
|
+
readonly _tag: "Raw";
|
|
316
|
+
readonly payload: unknown;
|
|
317
|
+
}, {
|
|
318
|
+
readonly _tag: Tag;
|
|
319
|
+
}> | Extract<{
|
|
320
|
+
readonly _tag: "Text";
|
|
321
|
+
readonly text: string;
|
|
322
|
+
}, {
|
|
323
|
+
readonly _tag: Tag;
|
|
324
|
+
}>;
|
|
325
|
+
readonly $match: {
|
|
326
|
+
<Cases extends { readonly [Tag in "Media" | "Raw" | "Text"]: (args: Extract<{
|
|
327
|
+
readonly _tag: "Media";
|
|
328
|
+
readonly media: MediaSource;
|
|
329
|
+
readonly caption?: string;
|
|
330
|
+
readonly filename?: string;
|
|
331
|
+
} | {
|
|
332
|
+
readonly _tag: "Raw";
|
|
333
|
+
readonly payload: unknown;
|
|
334
|
+
} | {
|
|
335
|
+
readonly _tag: "Text";
|
|
336
|
+
readonly text: string;
|
|
337
|
+
}, {
|
|
338
|
+
readonly _tag: Tag;
|
|
339
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
340
|
+
readonly _tag: "Media";
|
|
341
|
+
readonly media: MediaSource;
|
|
342
|
+
readonly caption?: string;
|
|
343
|
+
readonly filename?: string;
|
|
344
|
+
} | {
|
|
345
|
+
readonly _tag: "Raw";
|
|
346
|
+
readonly payload: unknown;
|
|
347
|
+
} | {
|
|
348
|
+
readonly _tag: "Text";
|
|
349
|
+
readonly text: string;
|
|
350
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Media" | "Raw" | "Text"]>>;
|
|
351
|
+
<Cases extends { readonly [Tag in "Media" | "Raw" | "Text"]: (args: Extract<{
|
|
352
|
+
readonly _tag: "Media";
|
|
353
|
+
readonly media: MediaSource;
|
|
354
|
+
readonly caption?: string;
|
|
355
|
+
readonly filename?: string;
|
|
356
|
+
} | {
|
|
357
|
+
readonly _tag: "Raw";
|
|
358
|
+
readonly payload: unknown;
|
|
359
|
+
} | {
|
|
360
|
+
readonly _tag: "Text";
|
|
361
|
+
readonly text: string;
|
|
362
|
+
}, {
|
|
363
|
+
readonly _tag: Tag;
|
|
364
|
+
}>) => any; }>(value: {
|
|
365
|
+
readonly _tag: "Media";
|
|
366
|
+
readonly media: MediaSource;
|
|
367
|
+
readonly caption?: string;
|
|
368
|
+
readonly filename?: string;
|
|
369
|
+
} | {
|
|
370
|
+
readonly _tag: "Raw";
|
|
371
|
+
readonly payload: unknown;
|
|
372
|
+
} | {
|
|
373
|
+
readonly _tag: "Text";
|
|
374
|
+
readonly text: string;
|
|
375
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Media" | "Raw" | "Text"]>>;
|
|
376
|
+
};
|
|
377
|
+
readonly Media: Data.TaggedEnum.ConstructorFrom<{
|
|
378
|
+
readonly _tag: "Media";
|
|
379
|
+
readonly media: MediaSource;
|
|
380
|
+
readonly caption?: string;
|
|
381
|
+
readonly filename?: string;
|
|
382
|
+
}, "_tag">;
|
|
383
|
+
readonly Raw: Data.TaggedEnum.ConstructorFrom<{
|
|
384
|
+
readonly _tag: "Raw";
|
|
385
|
+
readonly payload: unknown;
|
|
386
|
+
}, "_tag">;
|
|
387
|
+
readonly Text: Data.TaggedEnum.ConstructorFrom<{
|
|
388
|
+
readonly _tag: "Text";
|
|
389
|
+
readonly text: string;
|
|
390
|
+
}, "_tag">;
|
|
391
|
+
};
|
|
392
|
+
/**
|
|
393
|
+
* A body plus the envelope fields true of any message. `replyTo` is ignored
|
|
394
|
+
* for a `Raw` body, which names its own wire fields.
|
|
395
|
+
*/
|
|
396
|
+
type Outbound = {
|
|
397
|
+
readonly body: OutboundBody;
|
|
398
|
+
readonly replyTo?: MessageId;
|
|
399
|
+
};
|
|
400
|
+
type OutboundOptions = {
|
|
401
|
+
readonly replyTo?: MessageId;
|
|
402
|
+
};
|
|
403
|
+
/** Text message. The overwhelmingly common case: `post(text("hi"))`. */
|
|
404
|
+
declare const text: (body: string, options?: OutboundOptions) => Outbound;
|
|
405
|
+
/** Media message. `caption` and `filename` describe the media, not the envelope. */
|
|
406
|
+
declare const media: (source: MediaSource, options?: OutboundOptions & {
|
|
407
|
+
readonly caption?: string;
|
|
408
|
+
readonly filename?: string;
|
|
409
|
+
}) => Outbound;
|
|
410
|
+
/** Platform payload, passed through untouched. */
|
|
411
|
+
declare const raw: (payload: unknown) => Outbound;
|
|
412
|
+
declare const CurrentConversation_base: Context.ServiceClass<CurrentConversation, "@betalyra/effect-uai/Messenger/CurrentConversation", ConversationRef>;
|
|
413
|
+
/**
|
|
414
|
+
* The conversation the outbound verbs target. A tag with no default, so
|
|
415
|
+
* posting outside an established conversation is a compile error. Deep code
|
|
416
|
+
* (a tool posting progress, an approval resolver) inherits it rather than
|
|
417
|
+
* threading a ref down.
|
|
418
|
+
*/
|
|
419
|
+
declare class CurrentConversation extends CurrentConversation_base {}
|
|
420
|
+
/** Run `effect` against `ref`. Wrap a conversation fiber once; every nested post lands. */
|
|
421
|
+
declare const inConversation: (ref: ConversationRef) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, CurrentConversation>>;
|
|
422
|
+
/** Envelope of a streamed reply. `replyTo` lands on the first message it posts. */
|
|
423
|
+
type StreamOptions = OutboundOptions;
|
|
424
|
+
/** Platform ceilings, so recipes can see them before composing a message. */
|
|
425
|
+
type MessengerLimits = {
|
|
426
|
+
/** Longest single text message. */
|
|
427
|
+
readonly maxText: number;
|
|
428
|
+
/** Longest caption on a media message. */
|
|
429
|
+
readonly maxCaption: number;
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Cross-platform messaging: one inbound stream, five outbound verbs.
|
|
433
|
+
*
|
|
434
|
+
* `events` is single-consumer and provider-owned. The connection opens when
|
|
435
|
+
* the layer is built and closes with its scope, so there is no connect /
|
|
436
|
+
* disconnect here, and reconnects, acks and callback answering all happen
|
|
437
|
+
* below this line.
|
|
438
|
+
*
|
|
439
|
+
* `post`, `typing` and `stream` target the ambient
|
|
440
|
+
* {@link CurrentConversation}; `edit` and `react` name a message outright.
|
|
441
|
+
*/
|
|
442
|
+
type MessengerService = {
|
|
443
|
+
readonly events: Stream.Stream<InboundEvent, MessengerError>;
|
|
444
|
+
/** Text past `limits.maxText` goes out as several messages; the id is the last. */
|
|
445
|
+
readonly post: (msg: Outbound) => Effect.Effect<MessageId, MessengerError, CurrentConversation>;
|
|
446
|
+
readonly edit: (msg: MessageRef, next: Outbound) => Effect.Effect<void, MessengerError>;
|
|
447
|
+
/**
|
|
448
|
+
* `emoji` in the platform's own spelling, as its `Reaction` events deliver
|
|
449
|
+
* it. Nothing is translated; one the platform rejects fails
|
|
450
|
+
* `MessengerUnsupported`.
|
|
451
|
+
*/
|
|
452
|
+
readonly react: (msg: MessageRef, emoji: string) => Effect.Effect<void, MessengerError>;
|
|
453
|
+
/** Activity indicator, kept alive by the adapter until the scope closes. */
|
|
454
|
+
readonly typing: Effect.Effect<void, MessengerError, CurrentConversation | Scope.Scope>;
|
|
455
|
+
/**
|
|
456
|
+
* Progressive delivery of a text stream; the mechanism is the adapter's.
|
|
457
|
+
* The id is the last message posted, `None` when the stream had no text.
|
|
458
|
+
*/
|
|
459
|
+
readonly stream: <E, R>(deltas: Stream.Stream<string, E, R>, options?: StreamOptions) => Effect.Effect<Option.Option<MessageId>, MessengerError | E, R | CurrentConversation>;
|
|
460
|
+
readonly limits: MessengerLimits;
|
|
461
|
+
};
|
|
462
|
+
declare const Messenger_base: Context.ServiceClass<Messenger, "@betalyra/effect-uai/Messenger", MessengerService>;
|
|
463
|
+
declare class Messenger extends Messenger_base {}
|
|
464
|
+
declare const events: Stream.Stream<InboundEvent, MessengerError, Messenger>;
|
|
465
|
+
declare const post: (msg: Outbound) => Effect.Effect<MessageId, MessengerError, Messenger | CurrentConversation>;
|
|
466
|
+
declare const edit: (msg: MessageRef, next: Outbound) => Effect.Effect<void, MessengerError, Messenger>;
|
|
467
|
+
declare const react: (msg: MessageRef, emoji: string) => Effect.Effect<void, MessengerError, Messenger>;
|
|
468
|
+
declare const typing: Effect.Effect<void, MessengerError, Messenger | CurrentConversation | Scope.Scope>;
|
|
469
|
+
declare const stream: <E, R>(deltas: Stream.Stream<string, E, R>, options?: StreamOptions) => Effect.Effect<Option.Option<MessageId>, MessengerError | E, R | Messenger | CurrentConversation>;
|
|
470
|
+
//#endregion
|
|
471
|
+
export { ChannelId, ConversationRef, CurrentConversation, InboundEvent, MessageId, MessageRef, Messenger, MessengerLimits, MessengerService, Outbound, OutboundBody, OutboundOptions, StreamOptions, ThreadId, UserId, conversationKey, edit, events, inConversation, media, post, raw, react, stream, Messenger_d_exports as t, text, typing };
|
|
472
|
+
//# sourceMappingURL=Messenger.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Messenger.d.mts","names":[],"sources":["../../src/messenger/Messenger.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
|
|
2
|
+
import { Brand, Context, Data, Effect, Stream } from "effect";
|
|
3
|
+
//#region src/messenger/Messenger.ts
|
|
4
|
+
var Messenger_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
ChannelId: () => ChannelId,
|
|
6
|
+
CurrentConversation: () => CurrentConversation,
|
|
7
|
+
InboundEvent: () => InboundEvent,
|
|
8
|
+
MessageId: () => MessageId,
|
|
9
|
+
Messenger: () => Messenger,
|
|
10
|
+
OutboundBody: () => OutboundBody,
|
|
11
|
+
ThreadId: () => ThreadId,
|
|
12
|
+
UserId: () => UserId,
|
|
13
|
+
conversationKey: () => conversationKey,
|
|
14
|
+
edit: () => edit,
|
|
15
|
+
events: () => events,
|
|
16
|
+
inConversation: () => inConversation,
|
|
17
|
+
media: () => media,
|
|
18
|
+
post: () => post,
|
|
19
|
+
raw: () => raw,
|
|
20
|
+
react: () => react,
|
|
21
|
+
stream: () => stream,
|
|
22
|
+
text: () => text,
|
|
23
|
+
typing: () => typing
|
|
24
|
+
});
|
|
25
|
+
const ChannelId = Brand.nominal();
|
|
26
|
+
const MessageId = Brand.nominal();
|
|
27
|
+
const UserId = Brand.nominal();
|
|
28
|
+
const ThreadId = Brand.nominal();
|
|
29
|
+
/** Stable key for a conversation. Index per-conversation state with it. */
|
|
30
|
+
const conversationKey = (ref) => ref.thread === void 0 ? ref.channel : `${ref.channel}/${ref.thread}`;
|
|
31
|
+
const InboundEvent = Data.taggedEnum();
|
|
32
|
+
const OutboundBody = Data.taggedEnum();
|
|
33
|
+
const envelope = (body, options) => ({
|
|
34
|
+
body,
|
|
35
|
+
...options?.replyTo !== void 0 && { replyTo: options.replyTo }
|
|
36
|
+
});
|
|
37
|
+
/** Text message. The overwhelmingly common case: `post(text("hi"))`. */
|
|
38
|
+
const text = (body, options) => envelope(OutboundBody.Text({ text: body }), options);
|
|
39
|
+
/** Media message. `caption` and `filename` describe the media, not the envelope. */
|
|
40
|
+
const media = (source, options) => envelope(OutboundBody.Media({
|
|
41
|
+
media: source,
|
|
42
|
+
...options?.caption !== void 0 && { caption: options.caption },
|
|
43
|
+
...options?.filename !== void 0 && { filename: options.filename }
|
|
44
|
+
}), options);
|
|
45
|
+
/** Platform payload, passed through untouched. */
|
|
46
|
+
const raw = (payload) => ({ body: OutboundBody.Raw({ payload }) });
|
|
47
|
+
/**
|
|
48
|
+
* The conversation the outbound verbs target. A tag with no default, so
|
|
49
|
+
* posting outside an established conversation is a compile error. Deep code
|
|
50
|
+
* (a tool posting progress, an approval resolver) inherits it rather than
|
|
51
|
+
* threading a ref down.
|
|
52
|
+
*/
|
|
53
|
+
var CurrentConversation = class extends Context.Service()("@betalyra/effect-uai/Messenger/CurrentConversation") {};
|
|
54
|
+
/** Run `effect` against `ref`. Wrap a conversation fiber once; every nested post lands. */
|
|
55
|
+
const inConversation = (ref) => (effect) => Effect.provideService(effect, CurrentConversation, ref);
|
|
56
|
+
var Messenger = class extends Context.Service()("@betalyra/effect-uai/Messenger") {};
|
|
57
|
+
const events = Stream.unwrap(Effect.map(Messenger, (m) => m.events));
|
|
58
|
+
const post = (msg) => Effect.flatMap(Messenger, (m) => m.post(msg));
|
|
59
|
+
const edit = (msg, next) => Effect.flatMap(Messenger, (m) => m.edit(msg, next));
|
|
60
|
+
const react = (msg, emoji) => Effect.flatMap(Messenger, (m) => m.react(msg, emoji));
|
|
61
|
+
const typing = Effect.flatMap(Messenger, (m) => m.typing);
|
|
62
|
+
const stream = (deltas, options) => Effect.flatMap(Messenger, (m) => m.stream(deltas, options));
|
|
63
|
+
//#endregion
|
|
64
|
+
export { ChannelId, CurrentConversation, InboundEvent, MessageId, Messenger, OutboundBody, ThreadId, UserId, conversationKey, edit, events, inConversation, media, post, raw, react, stream, Messenger_exports as t, text, typing };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=Messenger.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Messenger.mjs","names":[],"sources":["../../src/messenger/Messenger.ts"],"sourcesContent":["import { Brand, Context, Data, Effect, type Option, type Scope, Stream } from \"effect\"\nimport type { MediaSource } from \"../domain/Media.js\"\nimport type * as MessengerError from \"./MessengerError.js\"\n\n// ---------------------------------------------------------------------------\n// Identity\n// ---------------------------------------------------------------------------\n\n/** Where messages go. Providers mint these per their own scheme; core treats them opaquely. */\nexport type ChannelId = Brand.Branded<string, \"ChannelId\">\nexport const ChannelId = Brand.nominal<ChannelId>()\n\n/** One message inside a channel. Only a named message can be edited or reacted to. */\nexport type MessageId = Brand.Branded<string, \"MessageId\">\nexport const MessageId = Brand.nominal<MessageId>()\n\n/** The human (or bot) an event came from. */\nexport type UserId = Brand.Branded<string, \"UserId\">\nexport const UserId = Brand.nominal<UserId>()\n\n/** A sub-conversation inside a channel, where the platform has one. Opaque, provider-minted. */\nexport type ThreadId = Brand.Branded<string, \"ThreadId\">\nexport const ThreadId = Brand.nominal<ThreadId>()\n\n/**\n * Where a conversation happens. `thread` is provider-interpreted: a Slack\n * `thread_ts`, a Telegram forum topic, unset on Discord (where a thread is\n * its own channel).\n */\nexport type ConversationRef = {\n readonly channel: ChannelId\n readonly thread?: ThreadId\n}\n\nexport type MessageRef = {\n readonly conversation: ConversationRef\n readonly id: MessageId\n}\n\n/** Stable key for a conversation. Index per-conversation state with it. */\nexport const conversationKey = (ref: ConversationRef): string =>\n ref.thread === undefined ? ref.channel : `${ref.channel}/${ref.thread}`\n\n// ---------------------------------------------------------------------------\n// Inbound\n// ---------------------------------------------------------------------------\n\n/**\n * What the world says to the agent. `raw` is the untouched platform payload,\n * for everything the shared shape drops.\n *\n * `addressed` is the rule recipes branch on: a DM, a mention of the bot, or\n * a reply to one of its messages. Each adapter owns that decision (and hides\n * Telegram privacy mode / Discord intents behind it), and delivers `text`\n * with the bot's own mention stripped.\n *\n * `Reaction.emoji` is the platform's own spelling, the same one `react`\n * accepts: unicode where the platform speaks unicode, a shortcode where it\n * speaks shortcodes.\n */\nexport type InboundEvent = Data.TaggedEnum<{\n Message: {\n readonly conversation: ConversationRef\n readonly id: MessageId\n readonly author: UserId\n readonly text: string\n readonly addressed: boolean\n readonly replyTo?: MessageId\n readonly raw: unknown\n }\n Reaction: {\n readonly conversation: ConversationRef\n readonly message: MessageId\n readonly emoji: string\n readonly author: UserId\n readonly raw: unknown\n }\n Command: {\n readonly conversation: ConversationRef\n readonly name: string\n readonly args: string\n readonly author: UserId\n readonly raw: unknown\n }\n Action: {\n readonly conversation: ConversationRef\n readonly actionId: string\n readonly value?: string\n readonly author: UserId\n readonly raw: unknown\n }\n}>\n\nexport const InboundEvent = Data.taggedEnum<InboundEvent>()\n\n// ---------------------------------------------------------------------------\n// Outbound\n// ---------------------------------------------------------------------------\n\n/**\n * What the agent says. `Text` is sent verbatim: each provider layer says\n * which markup it expects (Telegram HTML, Slack markdown), and the prompt is\n * where that gets decided. Nothing here converts. `Media` reuses the core\n * {@link MediaSource}, so a URL, base64 or raw bytes all work; the adapter\n * routes on `mimeType` (`image/*` to a photo endpoint, `audio/*` to an audio\n * one, everything else to a file upload), which is why a URL source without\n * a `mimeType` goes as a plain file. `Raw` bypasses the shape entirely and\n * reaches the platform as-is: the escape hatch for buttons, cards and\n * everything the verbs do not unify.\n */\nexport type OutboundBody = Data.TaggedEnum<{\n Text: { readonly text: string }\n Media: {\n readonly media: MediaSource\n readonly caption?: string\n readonly filename?: string\n }\n Raw: { readonly payload: unknown }\n}>\n\nexport const OutboundBody = Data.taggedEnum<OutboundBody>()\n\n/**\n * A body plus the envelope fields true of any message. `replyTo` is ignored\n * for a `Raw` body, which names its own wire fields.\n */\nexport type Outbound = {\n readonly body: OutboundBody\n readonly replyTo?: MessageId\n}\n\nexport type OutboundOptions = { readonly replyTo?: MessageId }\n\nconst envelope = (body: OutboundBody, options?: OutboundOptions): Outbound => ({\n body,\n ...(options?.replyTo !== undefined && { replyTo: options.replyTo }),\n})\n\n/** Text message. The overwhelmingly common case: `post(text(\"hi\"))`. */\nexport const text = (body: string, options?: OutboundOptions): Outbound =>\n envelope(OutboundBody.Text({ text: body }), options)\n\n/** Media message. `caption` and `filename` describe the media, not the envelope. */\nexport const media = (\n source: MediaSource,\n options?: OutboundOptions & { readonly caption?: string; readonly filename?: string },\n): Outbound =>\n envelope(\n OutboundBody.Media({\n media: source,\n ...(options?.caption !== undefined && { caption: options.caption }),\n ...(options?.filename !== undefined && { filename: options.filename }),\n }),\n options,\n )\n\n/** Platform payload, passed through untouched. */\nexport const raw = (payload: unknown): Outbound => ({ body: OutboundBody.Raw({ payload }) })\n\n// ---------------------------------------------------------------------------\n// Ambient conversation targeting\n// ---------------------------------------------------------------------------\n\n/**\n * The conversation the outbound verbs target. A tag with no default, so\n * posting outside an established conversation is a compile error. Deep code\n * (a tool posting progress, an approval resolver) inherits it rather than\n * threading a ref down.\n */\nexport class CurrentConversation extends Context.Service<CurrentConversation, ConversationRef>()(\n \"@betalyra/effect-uai/Messenger/CurrentConversation\",\n) {}\n\n/** Run `effect` against `ref`. Wrap a conversation fiber once; every nested post lands. */\nexport const inConversation =\n (ref: ConversationRef) =>\n <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, Exclude<R, CurrentConversation>> =>\n Effect.provideService(effect, CurrentConversation, ref)\n\n// ---------------------------------------------------------------------------\n// Service definition\n// ---------------------------------------------------------------------------\n\n/** Envelope of a streamed reply. `replyTo` lands on the first message it posts. */\nexport type StreamOptions = OutboundOptions\n\n/** Platform ceilings, so recipes can see them before composing a message. */\nexport type MessengerLimits = {\n /** Longest single text message. */\n readonly maxText: number\n /** Longest caption on a media message. */\n readonly maxCaption: number\n}\n\n/**\n * Cross-platform messaging: one inbound stream, five outbound verbs.\n *\n * `events` is single-consumer and provider-owned. The connection opens when\n * the layer is built and closes with its scope, so there is no connect /\n * disconnect here, and reconnects, acks and callback answering all happen\n * below this line.\n *\n * `post`, `typing` and `stream` target the ambient\n * {@link CurrentConversation}; `edit` and `react` name a message outright.\n */\nexport type MessengerService = {\n readonly events: Stream.Stream<InboundEvent, MessengerError.MessengerError>\n\n /** Text past `limits.maxText` goes out as several messages; the id is the last. */\n readonly post: (\n msg: Outbound,\n ) => Effect.Effect<MessageId, MessengerError.MessengerError, CurrentConversation>\n\n readonly edit: (\n msg: MessageRef,\n next: Outbound,\n ) => Effect.Effect<void, MessengerError.MessengerError>\n\n /**\n * `emoji` in the platform's own spelling, as its `Reaction` events deliver\n * it. Nothing is translated; one the platform rejects fails\n * `MessengerUnsupported`.\n */\n readonly react: (\n msg: MessageRef,\n emoji: string,\n ) => Effect.Effect<void, MessengerError.MessengerError>\n\n /** Activity indicator, kept alive by the adapter until the scope closes. */\n readonly typing: Effect.Effect<\n void,\n MessengerError.MessengerError,\n CurrentConversation | Scope.Scope\n >\n\n /**\n * Progressive delivery of a text stream; the mechanism is the adapter's.\n * The id is the last message posted, `None` when the stream had no text.\n */\n readonly stream: <E, R>(\n deltas: Stream.Stream<string, E, R>,\n options?: StreamOptions,\n ) => Effect.Effect<\n Option.Option<MessageId>,\n MessengerError.MessengerError | E,\n R | CurrentConversation\n >\n\n readonly limits: MessengerLimits\n}\n\nexport class Messenger extends Context.Service<Messenger, MessengerService>()(\n \"@betalyra/effect-uai/Messenger\",\n) {}\n\n// ---------------------------------------------------------------------------\n// Free helpers\n// ---------------------------------------------------------------------------\n\nexport const events: Stream.Stream<InboundEvent, MessengerError.MessengerError, Messenger> =\n Stream.unwrap(Effect.map(Messenger, (m) => m.events))\n\nexport const post = (\n msg: Outbound,\n): Effect.Effect<MessageId, MessengerError.MessengerError, Messenger | CurrentConversation> =>\n Effect.flatMap(Messenger, (m) => m.post(msg))\n\nexport const edit = (\n msg: MessageRef,\n next: Outbound,\n): Effect.Effect<void, MessengerError.MessengerError, Messenger> =>\n Effect.flatMap(Messenger, (m) => m.edit(msg, next))\n\nexport const react = (\n msg: MessageRef,\n emoji: string,\n): Effect.Effect<void, MessengerError.MessengerError, Messenger> =>\n Effect.flatMap(Messenger, (m) => m.react(msg, emoji))\n\nexport const typing: Effect.Effect<\n void,\n MessengerError.MessengerError,\n Messenger | CurrentConversation | Scope.Scope\n> = Effect.flatMap(Messenger, (m) => m.typing)\n\nexport const stream = <E, R>(\n deltas: Stream.Stream<string, E, R>,\n options?: StreamOptions,\n): Effect.Effect<\n Option.Option<MessageId>,\n MessengerError.MessengerError | E,\n R | Messenger | CurrentConversation\n> => Effect.flatMap(Messenger, (m) => m.stream(deltas, options))\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAUA,MAAa,YAAY,MAAM,QAAmB;AAIlD,MAAa,YAAY,MAAM,QAAmB;AAIlD,MAAa,SAAS,MAAM,QAAgB;AAI5C,MAAa,WAAW,MAAM,QAAkB;;AAkBhD,MAAa,mBAAmB,QAC9B,IAAI,WAAW,KAAA,IAAY,IAAI,UAAU,GAAG,IAAI,QAAQ,GAAG,IAAI;AAoDjE,MAAa,eAAe,KAAK,WAAyB;AA2B1D,MAAa,eAAe,KAAK,WAAyB;AAa1D,MAAM,YAAY,MAAoB,aAAyC;CAC7E;CACA,GAAI,SAAS,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,QAAQ;AACnE;;AAGA,MAAa,QAAQ,MAAc,YACjC,SAAS,aAAa,KAAK,EAAE,MAAM,KAAK,CAAC,GAAG,OAAO;;AAGrD,MAAa,SACX,QACA,YAEA,SACE,aAAa,MAAM;CACjB,OAAO;CACP,GAAI,SAAS,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,QAAQ;CACjE,GAAI,SAAS,aAAa,KAAA,KAAa,EAAE,UAAU,QAAQ,SAAS;AACtE,CAAC,GACD,OACF;;AAGF,MAAa,OAAO,aAAgC,EAAE,MAAM,aAAa,IAAI,EAAE,QAAQ,CAAC,EAAE;;;;;;;AAY1F,IAAa,sBAAb,cAAyC,QAAQ,QAA8C,CAAC,CAC9F,oDACF,CAAC,CAAC,CAAC;;AAGH,MAAa,kBACV,SACS,WACR,OAAO,eAAe,QAAQ,qBAAqB,GAAG;AA0E1D,IAAa,YAAb,cAA+B,QAAQ,QAAqC,CAAC,CAC3E,gCACF,CAAC,CAAC,CAAC;AAMH,MAAa,SACX,OAAO,OAAO,OAAO,IAAI,YAAY,MAAM,EAAE,MAAM,CAAC;AAEtD,MAAa,QACX,QAEA,OAAO,QAAQ,YAAY,MAAM,EAAE,KAAK,GAAG,CAAC;AAE9C,MAAa,QACX,KACA,SAEA,OAAO,QAAQ,YAAY,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;AAEpD,MAAa,SACX,KACA,UAEA,OAAO,QAAQ,YAAY,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;AAEtD,MAAa,SAIT,OAAO,QAAQ,YAAY,MAAM,EAAE,MAAM;AAE7C,MAAa,UACX,QACA,YAKG,OAAO,QAAQ,YAAY,MAAM,EAAE,OAAO,QAAQ,OAAO,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { MessengerService } from "./Messenger.mjs";
|
|
2
|
+
import { Array, Duration } from "effect";
|
|
3
|
+
declare namespace MessengerAdapter_d_exports {
|
|
4
|
+
export { EditableVerbs, StreamViaEditsOptions, splitForLimit, streamViaEdits };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Chunk `body` into pieces no longer than `limit`, breaking on paragraph,
|
|
8
|
+
* line, then word boundaries. Always yields at least one chunk, so callers
|
|
9
|
+
* have something to send even for empty input.
|
|
10
|
+
*/
|
|
11
|
+
declare const splitForLimit: (body: string, limit: number) => Array.NonEmptyReadonlyArray<string>;
|
|
12
|
+
/** The slice of a messenger that {@link streamViaEdits} drives. */
|
|
13
|
+
type EditableVerbs = Pick<MessengerService, "post" | "edit" | "limits">;
|
|
14
|
+
type StreamViaEditsOptions = {
|
|
15
|
+
/** Minimum wall time between edits. Default 1 second. */
|
|
16
|
+
readonly every?: Duration.Input;
|
|
17
|
+
/** Minimum growth since the last edit before spending another. Default 40. */
|
|
18
|
+
readonly minChars?: number;
|
|
19
|
+
/** How often to honour a `MessengerRateLimited` before giving up. Default 3. */
|
|
20
|
+
readonly rateLimitRetries?: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Progressive delivery as post-then-edit, for adapters with no native
|
|
24
|
+
* streaming API. Coalesces deltas by time and growth, never resends
|
|
25
|
+
* unchanged text, waits out `MessengerRateLimited`, rolls over to a fresh
|
|
26
|
+
* message past `limits.maxText`, and always flushes the tail.
|
|
27
|
+
*
|
|
28
|
+
* The first chunk goes out as soon as any text arrives, so the message shows
|
|
29
|
+
* up immediately and fills in from there. A stream that produced no text
|
|
30
|
+
* posts nothing and yields `None`.
|
|
31
|
+
*/
|
|
32
|
+
declare const streamViaEdits: (verbs: EditableVerbs, options?: StreamViaEditsOptions) => MessengerService["stream"];
|
|
33
|
+
//#endregion
|
|
34
|
+
export { EditableVerbs, StreamViaEditsOptions, splitForLimit, streamViaEdits, MessengerAdapter_d_exports as t };
|
|
35
|
+
//# sourceMappingURL=MessengerAdapter.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessengerAdapter.d.mts","names":[],"sources":["../../src/messenger/MessengerAdapter.ts"],"mappings":";;;;;;;;;;cAsCa,gBAAa,cAAgB,kBAAkB,MAAI;;KAUpD,gBAAgB,KAAK;KAErB;;WAED,QAAQ,SAAS;;WAEjB;;WAEA;;;;;;;;;;;;cAsDE,iBAAc,OACjB,eAAa,UAAY,0BAAwB"}
|