@intentface/latch-channels 0.9.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/dist/ask.d.ts +69 -0
  4. package/dist/ask.d.ts.map +1 -0
  5. package/dist/ask.js +94 -0
  6. package/dist/ask.js.map +1 -0
  7. package/dist/commands.d.ts +41 -0
  8. package/dist/commands.d.ts.map +1 -0
  9. package/dist/commands.js +54 -0
  10. package/dist/commands.js.map +1 -0
  11. package/dist/index.d.ts +19 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +19 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/kernel.d.ts +207 -0
  16. package/dist/kernel.d.ts.map +1 -0
  17. package/dist/kernel.js +883 -0
  18. package/dist/kernel.js.map +1 -0
  19. package/dist/render.d.ts +43 -0
  20. package/dist/render.d.ts.map +1 -0
  21. package/dist/render.js +192 -0
  22. package/dist/render.js.map +1 -0
  23. package/dist/slack/api.d.ts +207 -0
  24. package/dist/slack/api.d.ts.map +1 -0
  25. package/dist/slack/api.js +249 -0
  26. package/dist/slack/api.js.map +1 -0
  27. package/dist/slack/blocks.d.ts +36 -0
  28. package/dist/slack/blocks.d.ts.map +1 -0
  29. package/dist/slack/blocks.js +161 -0
  30. package/dist/slack/blocks.js.map +1 -0
  31. package/dist/slack/ids.d.ts +40 -0
  32. package/dist/slack/ids.d.ts.map +1 -0
  33. package/dist/slack/ids.js +47 -0
  34. package/dist/slack/ids.js.map +1 -0
  35. package/dist/slack/index.d.ts +24 -0
  36. package/dist/slack/index.d.ts.map +1 -0
  37. package/dist/slack/index.js +18 -0
  38. package/dist/slack/index.js.map +1 -0
  39. package/dist/slack/manifest.d.ts +23 -0
  40. package/dist/slack/manifest.d.ts.map +1 -0
  41. package/dist/slack/manifest.js +102 -0
  42. package/dist/slack/manifest.js.map +1 -0
  43. package/dist/slack/normalize.d.ts +35 -0
  44. package/dist/slack/normalize.d.ts.map +1 -0
  45. package/dist/slack/normalize.js +63 -0
  46. package/dist/slack/normalize.js.map +1 -0
  47. package/dist/slack/payloads.d.ts +36 -0
  48. package/dist/slack/payloads.d.ts.map +1 -0
  49. package/dist/slack/payloads.js +80 -0
  50. package/dist/slack/payloads.js.map +1 -0
  51. package/dist/slack/slackChannel.d.ts +152 -0
  52. package/dist/slack/slackChannel.d.ts.map +1 -0
  53. package/dist/slack/slackChannel.js +650 -0
  54. package/dist/slack/slackChannel.js.map +1 -0
  55. package/dist/slack/stream.d.ts +88 -0
  56. package/dist/slack/stream.d.ts.map +1 -0
  57. package/dist/slack/stream.js +418 -0
  58. package/dist/slack/stream.js.map +1 -0
  59. package/dist/slack/verify.d.ts +20 -0
  60. package/dist/slack/verify.d.ts.map +1 -0
  61. package/dist/slack/verify.js +32 -0
  62. package/dist/slack/verify.js.map +1 -0
  63. package/dist/telegram/api.d.ts +110 -0
  64. package/dist/telegram/api.d.ts.map +1 -0
  65. package/dist/telegram/api.js +170 -0
  66. package/dist/telegram/api.js.map +1 -0
  67. package/dist/telegram/format.d.ts +26 -0
  68. package/dist/telegram/format.d.ts.map +1 -0
  69. package/dist/telegram/format.js +56 -0
  70. package/dist/telegram/format.js.map +1 -0
  71. package/dist/telegram/html.d.ts +11 -0
  72. package/dist/telegram/html.d.ts.map +1 -0
  73. package/dist/telegram/html.js +35 -0
  74. package/dist/telegram/html.js.map +1 -0
  75. package/dist/telegram/index.d.ts +15 -0
  76. package/dist/telegram/index.d.ts.map +1 -0
  77. package/dist/telegram/index.js +15 -0
  78. package/dist/telegram/index.js.map +1 -0
  79. package/dist/telegram/stream.d.ts +59 -0
  80. package/dist/telegram/stream.d.ts.map +1 -0
  81. package/dist/telegram/stream.js +185 -0
  82. package/dist/telegram/stream.js.map +1 -0
  83. package/dist/telegram/telegramChannel.d.ts +136 -0
  84. package/dist/telegram/telegramChannel.d.ts.map +1 -0
  85. package/dist/telegram/telegramChannel.js +277 -0
  86. package/dist/telegram/telegramChannel.js.map +1 -0
  87. package/dist/telegram/verify.d.ts +7 -0
  88. package/dist/telegram/verify.d.ts.map +1 -0
  89. package/dist/telegram/verify.js +14 -0
  90. package/dist/telegram/verify.js.map +1 -0
  91. package/dist/types.d.ts +240 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +12 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/whatsapp/api.d.ts +59 -0
  96. package/dist/whatsapp/api.d.ts.map +1 -0
  97. package/dist/whatsapp/api.js +154 -0
  98. package/dist/whatsapp/api.js.map +1 -0
  99. package/dist/whatsapp/index.d.ts +13 -0
  100. package/dist/whatsapp/index.d.ts.map +1 -0
  101. package/dist/whatsapp/index.js +13 -0
  102. package/dist/whatsapp/index.js.map +1 -0
  103. package/dist/whatsapp/verify.d.ts +17 -0
  104. package/dist/whatsapp/verify.d.ts.map +1 -0
  105. package/dist/whatsapp/verify.js +32 -0
  106. package/dist/whatsapp/verify.js.map +1 -0
  107. package/dist/whatsapp/whatsappChannel.d.ts +104 -0
  108. package/dist/whatsapp/whatsappChannel.d.ts.map +1 -0
  109. package/dist/whatsapp/whatsappChannel.js +217 -0
  110. package/dist/whatsapp/whatsappChannel.js.map +1 -0
  111. package/package.json +68 -0
@@ -0,0 +1,104 @@
1
+ import type { AskStateStore, AuthResult, ChannelRunOps, ChannelThreadStore } from "../types.js";
2
+ import { decodeApprovalData, decodeQuestionData, encodeApprovalData, type InboundMedia, type Surface } from "../kernel.js";
3
+ import { type LazyString, type WhatsappApiOptions, type WhatsappClient } from "./api.js";
4
+ export interface WhatsappUser {
5
+ waId: string;
6
+ profileName?: string;
7
+ }
8
+ export interface WhatsappChannelConfig<P> {
9
+ /** The agent this number serves. */
10
+ agent: string;
11
+ /** Stable host-side bot id — used in chat ids (`whatsapp_<botRef>_<waId>`) and delivery keys. */
12
+ botRef: string;
13
+ /** Meta credentials, all lazily resolvable from the host's vault. */
14
+ accessToken: LazyString;
15
+ phoneNumberId: LazyString;
16
+ appSecret: LazyString;
17
+ verifyToken: LazyString;
18
+ runtime: ChannelRunOps<P>;
19
+ /** Resolve the inbound WhatsApp user (`wa_id`) to a platform principal. */
20
+ authorizeUser: (user: WhatsappUser) => Promise<AuthResult<P>>;
21
+ /** Persistent dedupe on `<botRef>:<waId>:<wamid>` (Meta re-delivers webhooks). */
22
+ seen?: (deliveryKey: string) => Promise<boolean>;
23
+ /** Host-backed one-turn-per-chat lease (see the kernel). */
24
+ claimTurn?: (chatId: string) => Promise<string | null>;
25
+ releaseTurn?: (chatId: string, token: string) => Promise<void>;
26
+ /** Stash the message that triggered a link prompt, to replay after linking. */
27
+ stashPending?: (input: WhatsappUser & {
28
+ text: string;
29
+ media?: InboundMedia<WaMediaRef>[];
30
+ }) => Promise<void>;
31
+ /** Build the platform authorize URL for a `connect_<name>` OAuth approval. */
32
+ authorizeUrl?: (connectionName: string, ctx: {
33
+ chatId: string;
34
+ waId: string;
35
+ approvalId: string;
36
+ }) => string;
37
+ /** Host storage for in-flight `askUser` questionnaires (see the kernel). */
38
+ askState?: AskStateStore;
39
+ /** Host storage for the thread epoch — what makes `/new` possible. */
40
+ thread?: ChannelThreadStore;
41
+ waitUntil?: (p: Promise<unknown>) => void;
42
+ /** Test seam: base URL / fetch for the Graph API client. */
43
+ apiOptions?: WhatsappApiOptions;
44
+ }
45
+ export interface WaMediaRef {
46
+ id: string;
47
+ mime_type?: string;
48
+ filename?: string;
49
+ caption?: string;
50
+ }
51
+ /** Interactive reply ids use the kernel's shared `lta:` / `ltq:` formats (256-char budget here). */
52
+ export declare const encodeWhatsappApprovalId: typeof encodeApprovalData;
53
+ export declare const decodeWhatsappApprovalId: typeof decodeApprovalData;
54
+ export declare const decodeWhatsappQuestionId: typeof decodeQuestionData;
55
+ /** Build the chat id for a (bot, WhatsApp user) at a thread epoch — same shape as Telegram's. */
56
+ export declare function whatsappChatId(botRef: string, waId: string, epoch?: number): string;
57
+ /** The inverse of `whatsappChatId`, or null when `id` isn't one of ours. */
58
+ export declare function parseWhatsappChatId(id: string): {
59
+ botRef: string;
60
+ waId: string;
61
+ epoch: number;
62
+ } | null;
63
+ /** Kept for hosts that split their own text; the kernel splits answers itself. */
64
+ export declare const splitWhatsappText: (text: string, max?: number) => string[];
65
+ export declare function whatsappSurface(api: WhatsappClient, botRef: string): Surface<WhatsappUser, WaMediaRef>;
66
+ export declare function whatsappChannel<P>(config: WhatsappChannelConfig<P>): {
67
+ handleRequest: (request: Request) => Promise<Response>;
68
+ /** Run one already-gated, already-leased message (scheduled delivery). */
69
+ runMessage: (ctx: {
70
+ waId: string;
71
+ text: string;
72
+ media?: InboundMedia<WaMediaRef>[];
73
+ principal: P;
74
+ } & {
75
+ chatId?: string;
76
+ }) => Promise<void>;
77
+ /** Replay a stashed message under the turn lease (the post-link resume). */
78
+ replayMessage: (ctx: {
79
+ waId: string;
80
+ text: string;
81
+ media?: InboundMedia<WaMediaRef>[];
82
+ principal: P;
83
+ }) => Promise<void>;
84
+ api: {
85
+ sendText(to: string, body: string): Promise<void>;
86
+ sendReplyButtons(to: string, body: string, buttons: import("./api.js").WhatsappReplyButton[]): Promise<void>;
87
+ sendList(to: string, body: string, buttonLabel: string, rows: {
88
+ id: string;
89
+ title: string;
90
+ description?: string;
91
+ }[]): Promise<void>;
92
+ getMediaInfo(mediaId: string): Promise<{
93
+ url?: string;
94
+ mimeType?: string;
95
+ fileSize?: number;
96
+ }>;
97
+ downloadMedia(url: string): Promise<Uint8Array>;
98
+ checkCredentials(): Promise<{
99
+ displayPhoneNumber?: string;
100
+ verifiedName?: string;
101
+ }>;
102
+ };
103
+ };
104
+ //# sourceMappingURL=whatsappChannel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsappChannel.d.ts","sourceRoot":"","sources":["../../src/whatsapp/whatsappChannel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,217 @@
1
+ import { createTurnKernel, decodeApprovalData, decodeQuestionData, encodeApprovalData, splitText, } from "../kernel.js";
2
+ import { whatsappApi, MAX_LIST_ROWS, MAX_REPLY_BUTTONS, } from "./api.js";
3
+ import { verifyWhatsappSignature, whatsappVerifyChallenge } from "./verify.js";
4
+ /**
5
+ * One agent's WhatsApp business number (Meta Cloud API). The host wires
6
+ * `handleRequest` to the bot's per-bot webhook route; we handle Meta's GET
7
+ * verify handshake, check the POST signature, ACK fast, and hand the parsed
8
+ * message to the turn kernel in the background. WhatsApp messages cannot be
9
+ * edited, so the surface never edits: answers post once (split past the
10
+ * 4096-char cap), receipts are separate messages.
11
+ */
12
+ const MAX_TEXT_CHARS = 4096;
13
+ /** Interactive reply ids use the kernel's shared `lta:` / `ltq:` formats (256-char budget here). */
14
+ export const encodeWhatsappApprovalId = encodeApprovalData;
15
+ export const decodeWhatsappApprovalId = decodeApprovalData;
16
+ export const decodeWhatsappQuestionId = decodeQuestionData;
17
+ /** Build the chat id for a (bot, WhatsApp user) at a thread epoch — same shape as Telegram's. */
18
+ export function whatsappChatId(botRef, waId, epoch = 0) {
19
+ const base = `whatsapp_${botRef}_${waId}`;
20
+ return epoch > 0 ? `${base}_t${epoch}` : base;
21
+ }
22
+ /** The inverse of `whatsappChatId`, or null when `id` isn't one of ours. */
23
+ export function parseWhatsappChatId(id) {
24
+ const m = /^whatsapp_(.+)_(\d+)(?:_t(\d+))?$/.exec(id);
25
+ if (!m)
26
+ return null;
27
+ return { botRef: m[1], waId: m[2], epoch: m[3] ? Number(m[3]) : 0 };
28
+ }
29
+ /** Kept for hosts that split their own text; the kernel splits answers itself. */
30
+ export const splitWhatsappText = (text, max = MAX_TEXT_CHARS) => splitText(text, max);
31
+ const ok = () => new Response(null, { status: 200 });
32
+ function mediaRefs(msg) {
33
+ const out = [];
34
+ if (msg.image)
35
+ out.push({ ref: msg.image, mediaType: msg.image.mime_type ?? "image/jpeg", name: "photo.jpg" });
36
+ if (msg.document)
37
+ out.push({
38
+ ref: msg.document,
39
+ mediaType: msg.document.mime_type ?? "application/octet-stream",
40
+ name: msg.document.filename ?? "document",
41
+ });
42
+ if (msg.audio)
43
+ out.push({ ref: msg.audio, mediaType: msg.audio.mime_type ?? "audio/ogg", name: "audio.ogg" });
44
+ if (msg.video)
45
+ out.push({ ref: msg.video, mediaType: msg.video.mime_type ?? "video/mp4", name: "video.mp4" });
46
+ return out;
47
+ }
48
+ /** Question body text: the question plus, when `numbered`, its options (typed "2" answers it). */
49
+ function questionBody(q, index, total, numbered) {
50
+ const head = total > 1 ? `*Question ${index + 1} of ${total}*\n` : "";
51
+ const title = q.header ? `_${q.header}_\n${q.question}` : q.question;
52
+ if (!numbered)
53
+ return `${head}${title}`;
54
+ const opts = q.options.map((o, i) => `${i + 1}. ${o.label}`).join("\n");
55
+ const how = q.multiSelect
56
+ ? "\n\nReply with the numbers you want, e.g. 1,3"
57
+ : "\n\nReply with a number, or just tell me in your own words.";
58
+ return `${head}${title}\n\n${opts}${how}`;
59
+ }
60
+ // ── The surface ─────────────────────────────────────────────────────────────
61
+ export function whatsappSurface(api, botRef) {
62
+ return {
63
+ kind: "whatsapp",
64
+ maxTextChars: MAX_TEXT_CHARS,
65
+ chatId: (u, epoch) => whatsappChatId(botRef, u.waId, epoch),
66
+ sendText: async (u, text) => {
67
+ await api.sendText(u.waId, text);
68
+ return undefined;
69
+ },
70
+ sendLink: (u, body, label, url) => api.sendText(u.waId, `${body}\n\n${label}: ${url}`),
71
+ sendApproval: async (u, pa, data) => {
72
+ let json;
73
+ try {
74
+ json = JSON.stringify(pa.input, null, 2) ?? String(pa.input);
75
+ }
76
+ catch {
77
+ json = String(pa.input);
78
+ }
79
+ if (json.length > 800)
80
+ json = `${json.slice(0, 800)}\n… (truncated)`;
81
+ await api.sendReplyButtons(u.waId, `🔒 Approve ${pa.toolName}?\n\n${json}`, [
82
+ { id: data.approve, title: "✅ Approve" },
83
+ { id: data.deny, title: "🚫 Deny" },
84
+ ]);
85
+ return undefined;
86
+ },
87
+ /**
88
+ * Post one question with the best affordance Meta allows: reply buttons up
89
+ * to 3 options, a tappable list up to 10, otherwise a numbered text prompt.
90
+ * Multi-select always falls back to numbers: messages can't be edited, so
91
+ * there is no way to tick options in place.
92
+ */
93
+ sendQuestion: async (u, session, data) => {
94
+ const q = session.questions[session.cursor];
95
+ if (!q)
96
+ return undefined;
97
+ const total = session.questions.length;
98
+ if (data && !q.multiSelect && q.options.length <= MAX_REPLY_BUTTONS) {
99
+ await api.sendReplyButtons(u.waId, questionBody(q, session.cursor, total, false), q.options.map((o, i) => ({ id: data.pick(i), title: o.label })));
100
+ }
101
+ else if (data && !q.multiSelect && q.options.length <= MAX_LIST_ROWS) {
102
+ await api.sendList(u.waId, questionBody(q, session.cursor, total, false), "Choose", q.options.map((o, i) => ({ id: data.pick(i), title: o.label, description: o.description })));
103
+ }
104
+ else {
105
+ await api.sendText(u.waId, questionBody(q, session.cursor, total, true));
106
+ }
107
+ return undefined;
108
+ },
109
+ answered: (u, _card, a) => api.sendText(u.waId, `✓ ${a.answer}`),
110
+ fetchMedia: async (m, maxBytes) => {
111
+ const info = await api.getMediaInfo(m.ref.id);
112
+ if (!info.url || (info.fileSize ?? 0) > maxBytes)
113
+ return null;
114
+ return { bytes: await api.downloadMedia(info.url), mediaType: info.mimeType ?? m.mediaType };
115
+ },
116
+ };
117
+ }
118
+ // ── The channel ─────────────────────────────────────────────────────────────
119
+ export function whatsappChannel(config) {
120
+ const api = whatsappApi({ accessToken: config.accessToken, phoneNumberId: config.phoneNumberId }, config.apiOptions);
121
+ const defer = (p) => (config.waitUntil ? config.waitUntil(p) : p);
122
+ const resolveLazy = async (v) => (typeof v === "function" ? v() : v);
123
+ const kernel = createTurnKernel({
124
+ agent: config.agent,
125
+ botRef: config.botRef,
126
+ runtime: config.runtime,
127
+ authorizeUser: config.authorizeUser,
128
+ ...(config.seen ? { seen: config.seen } : {}),
129
+ ...(config.claimTurn ? { claimTurn: config.claimTurn } : {}),
130
+ ...(config.releaseTurn ? { releaseTurn: config.releaseTurn } : {}),
131
+ ...(config.askState ? { askState: config.askState } : {}),
132
+ ...(config.thread ? { thread: config.thread } : {}),
133
+ ...(config.stashPending
134
+ ? {
135
+ stashPending: (i) => config.stashPending({ ...i.user, text: i.text, ...(i.media ? { media: i.media } : {}) }),
136
+ }
137
+ : {}),
138
+ ...(config.authorizeUrl
139
+ ? {
140
+ authorizeUrl: (name, ctx) => config.authorizeUrl(name, { chatId: ctx.chatId, waId: ctx.user.waId, approvalId: ctx.approvalId }),
141
+ }
142
+ : {}),
143
+ }, whatsappSurface(api, config.botRef));
144
+ /** One Meta message → the kernel's inbound event. Button/list replies are actions. */
145
+ function parse(msg, value) {
146
+ const profileName = value.contacts?.find((c) => c.wa_id === msg.from)?.profile?.name;
147
+ const user = { waId: msg.from, ...(profileName ? { profileName } : {}) };
148
+ const deliveryKey = `${config.botRef}:${msg.from}:${msg.id}`;
149
+ const replyId = msg.interactive?.button_reply?.id ?? msg.interactive?.list_reply?.id;
150
+ if (replyId)
151
+ return { kind: "action", user, data: replyId, deliveryKey };
152
+ return {
153
+ kind: "message",
154
+ user,
155
+ text: (msg.text?.body ?? msg.image?.caption ?? msg.document?.caption ?? msg.video?.caption ?? "").trim(),
156
+ media: mediaRefs(msg),
157
+ deliveryKey,
158
+ };
159
+ }
160
+ // ── The per-bot webhook entrypoint (GET verify + POST events) ──────────────
161
+ async function handleRequest(request) {
162
+ if (request.method === "GET") {
163
+ const challenge = whatsappVerifyChallenge(new URL(request.url), await resolveLazy(config.verifyToken));
164
+ return challenge !== null
165
+ ? new Response(challenge, { status: 200, headers: { "content-type": "text/plain" } })
166
+ : new Response("verification failed", { status: 403 });
167
+ }
168
+ const raw = await request.text();
169
+ const valid = verifyWhatsappSignature({
170
+ appSecret: await resolveLazy(config.appSecret),
171
+ rawBody: raw,
172
+ signature: request.headers.get("x-hub-signature-256"),
173
+ });
174
+ if (!valid)
175
+ return new Response("invalid signature", { status: 401 });
176
+ let body;
177
+ try {
178
+ body = JSON.parse(raw || "{}");
179
+ }
180
+ catch {
181
+ return ok();
182
+ }
183
+ if (body.object !== "whatsapp_business_account")
184
+ return ok();
185
+ // One Meta app/WABA can deliver events for MULTIPLE registered numbers to
186
+ // the same endpoint — only dispatch changes addressed to OUR number, or a
187
+ // message for a sibling number would run this bot's agent and reply
188
+ // through this binding.
189
+ const ourPhoneNumberId = await resolveLazy(config.phoneNumberId);
190
+ for (const entry of body.entry ?? []) {
191
+ for (const change of entry.changes ?? []) {
192
+ if (change.field !== "messages" || !change.value)
193
+ continue;
194
+ const recipient = change.value.metadata?.phone_number_id;
195
+ if (recipient && recipient !== ourPhoneNumberId) {
196
+ console.log(`[whatsapp] change for phone_number_id ${recipient} ignored (bot ${config.botRef} serves ${ourPhoneNumberId})`);
197
+ continue;
198
+ }
199
+ for (const msg of change.value.messages ?? []) {
200
+ defer(kernel.handleInbound(parse(msg, change.value)));
201
+ }
202
+ // statuses[] (delivery receipts) are intentionally ignored.
203
+ }
204
+ }
205
+ return ok();
206
+ }
207
+ const kernelCtx = ({ waId, ...rest }) => ({ user: { waId }, ...rest });
208
+ return {
209
+ handleRequest,
210
+ /** Run one already-gated, already-leased message (scheduled delivery). */
211
+ runMessage: (ctx) => kernel.runMessage(kernelCtx(ctx)),
212
+ /** Replay a stashed message under the turn lease (the post-link resume). */
213
+ replayMessage: (ctx) => kernel.replayMessage(kernelCtx(ctx)),
214
+ api,
215
+ };
216
+ }
217
+ //# sourceMappingURL=whatsappChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsappChannel.js","sourceRoot":"","sources":["../../src/whatsapp/whatsappChannel.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@intentface/latch-channels",
3
+ "version": "0.9.1",
4
+ "description": "Latch channels — bring a Latch agent to chat platforms (Slack first). A generic run→render-event projection plus per-platform adapters, reusing the core runtime, storage and durability (no separate state store).",
5
+ "keywords": [
6
+ "ai",
7
+ "ai-sdk",
8
+ "agents",
9
+ "llm",
10
+ "slack",
11
+ "telegram",
12
+ "whatsapp",
13
+ "chatbot"
14
+ ],
15
+ "license": "MIT",
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "module": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ },
25
+ "./slack": {
26
+ "types": "./dist/slack/index.d.ts",
27
+ "import": "./dist/slack/index.js"
28
+ },
29
+ "./telegram": {
30
+ "types": "./dist/telegram/index.d.ts",
31
+ "import": "./dist/telegram/index.js"
32
+ },
33
+ "./whatsapp": {
34
+ "types": "./dist/whatsapp/index.d.ts",
35
+ "import": "./dist/whatsapp/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "sideEffects": false,
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org"
45
+ },
46
+ "dependencies": {
47
+ "@intentface/latch-core": "^0.9.1"
48
+ },
49
+ "peerDependencies": {
50
+ "ai": ">=7.0.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^22",
54
+ "ai": "^7.0.2",
55
+ "typescript": "^5.7.0",
56
+ "vitest": "^4"
57
+ },
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/Intentface/intentface-latch.git",
61
+ "directory": "packages/latch-channels"
62
+ },
63
+ "scripts": {
64
+ "build": "tsc -p tsconfig.build.json",
65
+ "typecheck": "tsc -p tsconfig.json --noEmit",
66
+ "test": "vitest run"
67
+ }
68
+ }