commune-ai 0.2.6 → 0.2.62
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 +173 -93
- package/dist/client.d.ts +2 -1
- package/dist/client.js +6 -0
- package/dist/types.d.ts +8 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
# @commune/sdk
|
|
2
2
|
|
|
3
3
|
Commune is the **communication infrastructure for agents**. It gives your agent a **unified inbox**
|
|
4
|
-
for **email
|
|
4
|
+
for **email**, so your agent can talk to humans where they already work. Most teams
|
|
5
5
|
get a working integration in **~15 minutes**.
|
|
6
6
|
|
|
7
7
|
## Why Commune exists (what it enables)
|
|
8
|
-
Agents are powerful, but users already live in **email
|
|
8
|
+
Agents are powerful, but users already live in **email**. Commune bridges that gap so:
|
|
9
9
|
- your agent is reachable where humans already work
|
|
10
|
-
- you don’t have to build deliverability, threading, or
|
|
10
|
+
- you don’t have to build deliverability, threading, or email plumbing
|
|
11
11
|
- you can ship an agent‑first experience in minutes, not weeks
|
|
12
12
|
|
|
13
13
|
In practice, Commune lets you:
|
|
14
14
|
- give an agent a real inbox on your domain
|
|
15
|
-
- respond in the correct email
|
|
15
|
+
- respond in the correct email thread every time
|
|
16
16
|
- use **conversation state** to make smarter, context‑aware replies
|
|
17
17
|
|
|
18
18
|
## How it works (mental model)
|
|
19
|
-
1) Commune receives inbound email
|
|
19
|
+
1) Commune receives inbound email events.
|
|
20
20
|
2) Commune normalizes them into a **UnifiedMessage**.
|
|
21
21
|
3) Commune sends the UnifiedMessage to your webhook.
|
|
22
22
|
4) Your agent replies using one API call.
|
|
@@ -48,7 +48,7 @@ const handler = createWebhookHandler({
|
|
|
48
48
|
});
|
|
49
49
|
},
|
|
50
50
|
onEvent: async (message, context) => {
|
|
51
|
-
// Example inbound payload
|
|
51
|
+
// Example inbound payload:
|
|
52
52
|
// message = {
|
|
53
53
|
// channel: "email",
|
|
54
54
|
// conversation_id: "thread_id",
|
|
@@ -61,32 +61,17 @@ const handler = createWebhookHandler({
|
|
|
61
61
|
const agentReply = await llm.complete(prompt); // replace with your LLM client
|
|
62
62
|
|
|
63
63
|
// Email reply (same thread)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Slack reply (same thread)
|
|
79
|
-
if (message.channel === "slack") {
|
|
80
|
-
const channelId = message.metadata.slack_channel_id;
|
|
81
|
-
if (!channelId) return;
|
|
82
|
-
|
|
83
|
-
await client.messages.send({
|
|
84
|
-
channel: "slack",
|
|
85
|
-
to: channelId,
|
|
86
|
-
text: agentReply,
|
|
87
|
-
conversation_id: message.conversation_id, // thread_ts
|
|
88
|
-
});
|
|
89
|
-
}
|
|
64
|
+
const sender = message.participants.find(p => p.role === "sender")?.identity;
|
|
65
|
+
if (!sender) return;
|
|
66
|
+
|
|
67
|
+
await client.messages.send({
|
|
68
|
+
channel: "email",
|
|
69
|
+
to: sender,
|
|
70
|
+
text: agentReply,
|
|
71
|
+
conversation_id: message.conversation_id,
|
|
72
|
+
domainId: context.payload.domainId,
|
|
73
|
+
inboxId: context.payload.inboxId,
|
|
74
|
+
});
|
|
90
75
|
},
|
|
91
76
|
});
|
|
92
77
|
|
|
@@ -103,16 +88,16 @@ npm install @commune/sdk
|
|
|
103
88
|
---
|
|
104
89
|
|
|
105
90
|
## Unified inbox (what your webhook receives)
|
|
106
|
-
Every inbound email
|
|
91
|
+
Every inbound email arrives in this shape:
|
|
107
92
|
|
|
108
93
|
```ts
|
|
109
94
|
export interface UnifiedMessage {
|
|
110
|
-
channel: "email"
|
|
95
|
+
channel: "email";
|
|
111
96
|
message_id: string;
|
|
112
|
-
conversation_id: string; // email thread
|
|
97
|
+
conversation_id: string; // email thread
|
|
113
98
|
participants: { role: string; identity: string }[];
|
|
114
99
|
content: string;
|
|
115
|
-
metadata: {
|
|
100
|
+
metadata: { ... };
|
|
116
101
|
}
|
|
117
102
|
```
|
|
118
103
|
|
|
@@ -132,6 +117,81 @@ const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
|
132
117
|
|
|
133
118
|
---
|
|
134
119
|
|
|
120
|
+
<!-- -->
|
|
121
|
+
## Attachments
|
|
122
|
+
Send and receive email attachments with secure storage and temporary download URLs.
|
|
123
|
+
|
|
124
|
+
### Sending attachments
|
|
125
|
+
Upload attachments first, then use the attachment ID when sending emails.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import fs from 'fs';
|
|
129
|
+
|
|
130
|
+
// 1. Upload attachment (base64 encoded)
|
|
131
|
+
const fileBuffer = fs.readFileSync('invoice.pdf');
|
|
132
|
+
const base64Content = fileBuffer.toString('base64');
|
|
133
|
+
|
|
134
|
+
const { attachment_id } = await client.attachments.upload(
|
|
135
|
+
base64Content,
|
|
136
|
+
'invoice.pdf',
|
|
137
|
+
'application/pdf'
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// 2. Send email with attachment
|
|
141
|
+
await client.messages.send({
|
|
142
|
+
to: 'customer@example.com',
|
|
143
|
+
subject: 'Your invoice',
|
|
144
|
+
text: 'Please find your invoice attached.',
|
|
145
|
+
attachments: [attachment_id],
|
|
146
|
+
domainId: 'your-domain-id',
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Receiving attachments
|
|
151
|
+
Attachments are available through:
|
|
152
|
+
- Email events in the incoming webhook
|
|
153
|
+
- Metadata of semantic search results
|
|
154
|
+
|
|
155
|
+
Use the `attachment_id` to access the attachment.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
// Get attachment metadata
|
|
159
|
+
const attachment = await client.attachments.get("att_123");
|
|
160
|
+
console.log(attachment.filename, attachment.size);
|
|
161
|
+
|
|
162
|
+
// Get download URL (expires in 1 hour)
|
|
163
|
+
const { url } = await client.attachments.get("att_123", { url: true });
|
|
164
|
+
// Use the URL to download or display the file
|
|
165
|
+
|
|
166
|
+
// Custom expiration (2 hours)
|
|
167
|
+
const { url } = await client.attachments.get("att_123", { url: true, expiresIn: 7200 });
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Handling attachments in webhook
|
|
171
|
+
```ts
|
|
172
|
+
const handler = createWebhookHandler({
|
|
173
|
+
onEvent: async (message, context) => {
|
|
174
|
+
const { attachments } = context.payload;
|
|
175
|
+
|
|
176
|
+
if (attachments && attachments.length > 0) {
|
|
177
|
+
for (const att of attachments) {
|
|
178
|
+
console.log(`Attachment: ${att.filename} (${att.size} bytes)`);
|
|
179
|
+
|
|
180
|
+
// Get download URL
|
|
181
|
+
const { url } = await client.attachments.get(att.attachment_id, { url: true });
|
|
182
|
+
|
|
183
|
+
// Download the file
|
|
184
|
+
const response = await fetch(url);
|
|
185
|
+
const buffer = await response.arrayBuffer();
|
|
186
|
+
// Process the file...
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
135
195
|
## Semantic Search
|
|
136
196
|
Commune provides powerful semantic search capabilities to help your agent find relevant conversations and context. The search is powered by embeddings and vector similarity, allowing natural language queries.
|
|
137
197
|
|
|
@@ -230,10 +290,33 @@ interface SearchResult {
|
|
|
230
290
|
participants: string[];
|
|
231
291
|
threadId: string;
|
|
232
292
|
timestamp: Date;
|
|
293
|
+
attachmentIds?: string[]; // Attachment IDs in this conversation
|
|
294
|
+
hasAttachments?: boolean; // Whether conversation has attachments
|
|
295
|
+
attachmentCount?: number; // Number of attachments
|
|
233
296
|
};
|
|
234
297
|
}
|
|
235
298
|
```
|
|
236
299
|
|
|
300
|
+
### Search with attachments
|
|
301
|
+
```ts
|
|
302
|
+
// Search for conversations with attachments
|
|
303
|
+
const results = await client.searchConversations(
|
|
304
|
+
"invoice with receipt",
|
|
305
|
+
{ organizationId: "org_123" }
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Filter results that have attachments
|
|
309
|
+
const withAttachments = results.filter(r => r.metadata.hasAttachments);
|
|
310
|
+
|
|
311
|
+
// Access attachments from search results
|
|
312
|
+
for (const result of withAttachments) {
|
|
313
|
+
for (const attachmentId of result.metadata.attachmentIds || []) {
|
|
314
|
+
const { url, filename } = await client.attachments.get(attachmentId, { url: true });
|
|
315
|
+
console.log(`Download: ${filename} - ${url}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
237
320
|
## Spam Protection
|
|
238
321
|
Commune includes built-in spam detection and protection to keep your agent safe from malicious emails and prevent abuse.
|
|
239
322
|
|
|
@@ -283,7 +366,7 @@ Commune stores conversation state so your agent can respond with context.
|
|
|
283
366
|
import { CommuneClient } from "@commune/sdk";
|
|
284
367
|
const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
285
368
|
|
|
286
|
-
// Thread history (email thread
|
|
369
|
+
// Thread history (email thread)
|
|
287
370
|
const thread = await client.messages.listByConversation(message.conversation_id, {
|
|
288
371
|
order: "asc",
|
|
289
372
|
limit: 50,
|
|
@@ -305,8 +388,8 @@ const inboxMessages = await client.messages.list({
|
|
|
305
388
|
|
|
306
389
|
---
|
|
307
390
|
|
|
308
|
-
##
|
|
309
|
-
The
|
|
391
|
+
## Email handling
|
|
392
|
+
The `UnifiedMessage` shape works for email messages.
|
|
310
393
|
|
|
311
394
|
```ts
|
|
312
395
|
import express from "express";
|
|
@@ -317,31 +400,17 @@ const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
|
317
400
|
|
|
318
401
|
const handler = createWebhookHandler({
|
|
319
402
|
onEvent: async (message, context) => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (message.channel === "slack") {
|
|
335
|
-
const channelId = message.metadata.slack_channel_id;
|
|
336
|
-
if (!channelId) return;
|
|
337
|
-
|
|
338
|
-
await client.messages.send({
|
|
339
|
-
channel: "slack",
|
|
340
|
-
to: channelId,
|
|
341
|
-
text: "Replying in thread ✅",
|
|
342
|
-
conversation_id: message.conversation_id,
|
|
343
|
-
});
|
|
344
|
-
}
|
|
403
|
+
const sender = message.participants.find(p => p.role === "sender")?.identity;
|
|
404
|
+
if (!sender) return;
|
|
405
|
+
|
|
406
|
+
await client.messages.send({
|
|
407
|
+
channel: "email",
|
|
408
|
+
to: sender,
|
|
409
|
+
text: "Got it — thanks for the message.",
|
|
410
|
+
conversation_id: message.conversation_id,
|
|
411
|
+
domainId: context.payload.domainId,
|
|
412
|
+
inboxId: context.payload.inboxId,
|
|
413
|
+
});
|
|
345
414
|
},
|
|
346
415
|
});
|
|
347
416
|
|
|
@@ -527,47 +596,58 @@ const handler = createWebhookHandler({
|
|
|
527
596
|
## Full example (single file)
|
|
528
597
|
A complete copy‑paste example that:
|
|
529
598
|
- receives webhook
|
|
599
|
+
- handles incoming attachments
|
|
600
|
+
- sends email with attachments
|
|
530
601
|
- replies by email
|
|
531
|
-
- replies in Slack thread
|
|
532
|
-
- fetches conversation history
|
|
533
602
|
|
|
534
603
|
```ts
|
|
535
604
|
import express from "express";
|
|
536
605
|
import { CommuneClient, createWebhookHandler } from "@commune/sdk";
|
|
606
|
+
import fs from "fs";
|
|
537
607
|
|
|
538
608
|
const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
539
609
|
|
|
540
610
|
const handler = createWebhookHandler({
|
|
541
611
|
onEvent: async (message, context) => {
|
|
542
|
-
// 1)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return;
|
|
612
|
+
// 1) Handle incoming attachments
|
|
613
|
+
const { attachments } = context.payload;
|
|
614
|
+
if (attachments && attachments.length > 0) {
|
|
615
|
+
console.log(`Received ${attachments.length} attachments`);
|
|
616
|
+
|
|
617
|
+
for (const att of attachments) {
|
|
618
|
+
// Get download URL
|
|
619
|
+
const { url, filename } = await client.attachments.get(att.attachment_id, { url: true });
|
|
620
|
+
console.log(`Attachment: ${filename} - ${url}`);
|
|
621
|
+
|
|
622
|
+
// Download if needed
|
|
623
|
+
// const response = await fetch(url);
|
|
624
|
+
// const buffer = await response.arrayBuffer();
|
|
625
|
+
}
|
|
557
626
|
}
|
|
558
627
|
|
|
559
|
-
// 2)
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
628
|
+
// 2) Email reply with attachment (same thread)
|
|
629
|
+
const sender = message.participants.find(p => p.role === "sender")?.identity;
|
|
630
|
+
if (!sender) return;
|
|
631
|
+
|
|
632
|
+
// Upload attachment for sending
|
|
633
|
+
const fileBuffer = fs.readFileSync('receipt.pdf');
|
|
634
|
+
const base64Content = fileBuffer.toString('base64');
|
|
635
|
+
const { attachment_id } = await client.attachments.upload(
|
|
636
|
+
base64Content,
|
|
637
|
+
'receipt.pdf',
|
|
638
|
+
'application/pdf'
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
await client.messages.send({
|
|
642
|
+
channel: "email",
|
|
643
|
+
to: sender,
|
|
644
|
+
subject: "Your receipt",
|
|
645
|
+
text: "Thanks! Here's your receipt.",
|
|
646
|
+
attachments: [attachment_id],
|
|
647
|
+
conversation_id: message.conversation_id,
|
|
648
|
+
domainId: context.payload.domainId,
|
|
649
|
+
inboxId: context.payload.inboxId,
|
|
650
|
+
});
|
|
571
651
|
},
|
|
572
652
|
});
|
|
573
653
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AttachmentRecord, AttachmentUrl, CreateDomainPayload, DomainEntry, InboxEntry, MessageListParams, SendMessagePayload, UnifiedMessage, SearchFilter, SearchOptions, SearchResult, IndexConversationPayload } from './types.js';
|
|
1
|
+
import type { AttachmentRecord, AttachmentUrl, AttachmentUploadResponse, CreateDomainPayload, DomainEntry, InboxEntry, MessageListParams, SendMessagePayload, UnifiedMessage, SearchFilter, SearchOptions, SearchResult, IndexConversationPayload } from './types.js';
|
|
2
2
|
export type ClientOptions = {
|
|
3
3
|
baseUrl?: string;
|
|
4
4
|
apiKey: string;
|
|
@@ -72,6 +72,7 @@ export declare class CommuneClient {
|
|
|
72
72
|
}>;
|
|
73
73
|
};
|
|
74
74
|
attachments: {
|
|
75
|
+
upload: (content: string, filename: string, mimeType: string) => Promise<AttachmentUploadResponse>;
|
|
75
76
|
get: (attachmentId: string, options?: {
|
|
76
77
|
url?: boolean;
|
|
77
78
|
expiresIn?: number;
|
package/dist/client.js
CHANGED
|
@@ -128,6 +128,12 @@ export class CommuneClient {
|
|
|
128
128
|
},
|
|
129
129
|
};
|
|
130
130
|
this.attachments = {
|
|
131
|
+
upload: async (content, filename, mimeType) => {
|
|
132
|
+
return this.request('/api/attachments/upload', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
body: JSON.stringify({ content, filename, mimeType }),
|
|
135
|
+
});
|
|
136
|
+
},
|
|
131
137
|
get: async (attachmentId, options) => {
|
|
132
138
|
if (options?.url) {
|
|
133
139
|
const expiresIn = options.expiresIn || 3600;
|
package/dist/types.d.ts
CHANGED
|
@@ -59,6 +59,13 @@ export interface AttachmentUrl {
|
|
|
59
59
|
mimeType: string;
|
|
60
60
|
size: number;
|
|
61
61
|
}
|
|
62
|
+
export interface AttachmentUploadResponse {
|
|
63
|
+
attachment_id: string;
|
|
64
|
+
filename: string;
|
|
65
|
+
mime_type: string;
|
|
66
|
+
size: number;
|
|
67
|
+
storage_type: 'cloudinary' | 'database';
|
|
68
|
+
}
|
|
62
69
|
export interface DomainWebhook {
|
|
63
70
|
id?: string;
|
|
64
71
|
endpoint?: string;
|
|
@@ -104,10 +111,7 @@ export interface SendMessagePayload {
|
|
|
104
111
|
to: string | string[];
|
|
105
112
|
text?: string;
|
|
106
113
|
html?: string;
|
|
107
|
-
attachments?:
|
|
108
|
-
id?: string;
|
|
109
|
-
attachment_id?: string;
|
|
110
|
-
}[];
|
|
114
|
+
attachments?: string[];
|
|
111
115
|
subject?: string;
|
|
112
116
|
cc?: string[];
|
|
113
117
|
bcc?: string[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commune-ai",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.62",
|
|
4
|
+
"description": "SDK-first email infrastructure for agents - threads, history, semantic search, structured data output and attachments.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|