commune-ai 0.2.4 → 0.2.5

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 CHANGED
@@ -132,6 +132,108 @@ const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
132
132
 
133
133
  ---
134
134
 
135
+ ## Semantic Search
136
+ 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
+
138
+ ### Basic Search
139
+ ```ts
140
+ import { CommuneClient } from "@commune/sdk";
141
+ const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
142
+
143
+ // Search across all conversations in an organization
144
+ const results = await client.searchConversations(
145
+ "customer asking about refund policy",
146
+ { organizationId: "org_123" }
147
+ );
148
+
149
+ // Search with inbox filter
150
+ const inboxResults = await client.searchConversations(
151
+ "shipping delays",
152
+ {
153
+ organizationId: "org_123",
154
+ inboxIds: ["inbox_1", "inbox_2"]
155
+ }
156
+ );
157
+
158
+ // Search by participant
159
+ const userResults = await client.searchConversations(
160
+ "account upgrade request",
161
+ {
162
+ organizationId: "org_123",
163
+ participants: ["user@example.com"]
164
+ }
165
+ );
166
+
167
+ // Search with date range
168
+ const dateResults = await client.searchConversations(
169
+ "feature request",
170
+ {
171
+ organizationId: "org_123",
172
+ startDate: "2026-01-01",
173
+ endDate: "2026-02-01"
174
+ }
175
+ );
176
+ ```
177
+
178
+ ### Manual Indexing
179
+ By default, all conversations are automatically indexed. You can also manually index conversations:
180
+
181
+ ```ts
182
+ // Index a single conversation
183
+ await client.indexConversation("org_123", {
184
+ id: "conv_123",
185
+ subject: "Product Inquiry",
186
+ content: "Customer asking about product features",
187
+ metadata: {
188
+ subject: "Product Inquiry",
189
+ organizationId: "org_123",
190
+ inboxId: "inbox_1",
191
+ domainId: "domain_1",
192
+ participants: ["customer@example.com"],
193
+ threadId: "thread_1",
194
+ timestamp: new Date()
195
+ }
196
+ });
197
+
198
+ // Batch index multiple conversations
199
+ await client.indexConversationBatch("org_123", [
200
+ {
201
+ id: "conv_1",
202
+ subject: "Support Request",
203
+ content: "Customer needs help with login",
204
+ metadata: {
205
+ subject: "Support Request",
206
+ organizationId: "org_123",
207
+ inboxId: "support_inbox",
208
+ domainId: "domain_1",
209
+ participants: ["customer@example.com"],
210
+ threadId: "thread_1",
211
+ timestamp: new Date()
212
+ }
213
+ },
214
+ // ... more conversations
215
+ ]);
216
+ ```
217
+
218
+ ### Search Results
219
+ Search results include relevance scores and metadata:
220
+
221
+ ```ts
222
+ interface SearchResult {
223
+ id: string; // Conversation ID
224
+ score: number; // Similarity score (0-1)
225
+ metadata: {
226
+ subject: string; // Email subject
227
+ organizationId: string;
228
+ inboxId: string;
229
+ domainId: string;
230
+ participants: string[];
231
+ threadId: string;
232
+ timestamp: Date;
233
+ };
234
+ }
235
+ ```
236
+
135
237
  ## Context (conversation state)
136
238
  Commune stores conversation state so your agent can respond with context.
137
239
 
@@ -0,0 +1,91 @@
1
+ import { SearchFilter, SearchOptions, SearchResult, IndexConversationPayload } from '../types.js';
2
+ export declare class SearchClient {
3
+ private baseUrl;
4
+ private headers;
5
+ constructor(baseUrl: string, headers: Record<string, string>);
6
+ /**
7
+ * Search conversations using semantic search
8
+ * @param query - The search query in natural language
9
+ * @param filter - Filter criteria for the search
10
+ * @param options - Additional search options
11
+ * @returns Promise<SearchResult[]>
12
+ * @example
13
+ * ```typescript
14
+ * // Search across organization
15
+ * const results = await client.conversations.search(
16
+ * "customer asking about refund policy",
17
+ * { organizationId: "org_123" }
18
+ * );
19
+ *
20
+ * // Search in specific inboxes
21
+ * const results = await client.conversations.search(
22
+ * "shipping delays",
23
+ * {
24
+ * organizationId: "org_123",
25
+ * inboxIds: ["inbox_1", "inbox_2"]
26
+ * }
27
+ * );
28
+ *
29
+ * // Search with participant filter
30
+ * const results = await client.conversations.search(
31
+ * "account upgrade request",
32
+ * {
33
+ * organizationId: "org_123",
34
+ * participants: ["user@example.com"]
35
+ * }
36
+ * );
37
+ * ```
38
+ */
39
+ search(query: string, filter: SearchFilter, options?: SearchOptions): Promise<SearchResult[]>;
40
+ /**
41
+ * Index a conversation for semantic search
42
+ * @param organizationId - The organization ID
43
+ * @param conversation - The conversation to index
44
+ * @returns Promise<void>
45
+ * @example
46
+ * ```typescript
47
+ * await client.conversations.index("org_123", {
48
+ * id: "conv_123",
49
+ * subject: "Product Inquiry",
50
+ * content: "Customer asking about product features",
51
+ * metadata: {
52
+ * subject: "Product Inquiry",
53
+ * organizationId: "org_123",
54
+ * inboxId: "inbox_1",
55
+ * domainId: "domain_1",
56
+ * participants: ["customer@example.com"],
57
+ * threadId: "thread_1",
58
+ * timestamp: new Date()
59
+ * }
60
+ * });
61
+ * ```
62
+ */
63
+ index(organizationId: string, conversation: IndexConversationPayload): Promise<void>;
64
+ /**
65
+ * Index multiple conversations in batch
66
+ * @param organizationId - The organization ID
67
+ * @param conversations - Array of conversations to index
68
+ * @returns Promise<void>
69
+ * @example
70
+ * ```typescript
71
+ * await client.conversations.indexBatch("org_123", [
72
+ * {
73
+ * id: "conv_1",
74
+ * subject: "Support Request",
75
+ * content: "Customer needs help with login",
76
+ * metadata: {
77
+ * subject: "Support Request",
78
+ * organizationId: "org_123",
79
+ * inboxId: "support_inbox",
80
+ * domainId: "domain_1",
81
+ * participants: ["customer@example.com"],
82
+ * threadId: "thread_1",
83
+ * timestamp: new Date()
84
+ * }
85
+ * },
86
+ * // ... more conversations
87
+ * ]);
88
+ * ```
89
+ */
90
+ indexBatch(organizationId: string, conversations: IndexConversationPayload[]): Promise<void>;
91
+ }
@@ -0,0 +1,131 @@
1
+ export class SearchClient {
2
+ constructor(baseUrl, headers) {
3
+ this.baseUrl = baseUrl;
4
+ this.headers = headers;
5
+ }
6
+ /**
7
+ * Search conversations using semantic search
8
+ * @param query - The search query in natural language
9
+ * @param filter - Filter criteria for the search
10
+ * @param options - Additional search options
11
+ * @returns Promise<SearchResult[]>
12
+ * @example
13
+ * ```typescript
14
+ * // Search across organization
15
+ * const results = await client.conversations.search(
16
+ * "customer asking about refund policy",
17
+ * { organizationId: "org_123" }
18
+ * );
19
+ *
20
+ * // Search in specific inboxes
21
+ * const results = await client.conversations.search(
22
+ * "shipping delays",
23
+ * {
24
+ * organizationId: "org_123",
25
+ * inboxIds: ["inbox_1", "inbox_2"]
26
+ * }
27
+ * );
28
+ *
29
+ * // Search with participant filter
30
+ * const results = await client.conversations.search(
31
+ * "account upgrade request",
32
+ * {
33
+ * organizationId: "org_123",
34
+ * participants: ["user@example.com"]
35
+ * }
36
+ * );
37
+ * ```
38
+ */
39
+ async search(query, filter, options) {
40
+ const response = await fetch(`${this.baseUrl}/search`, {
41
+ method: 'POST',
42
+ headers: {
43
+ ...this.headers,
44
+ 'Content-Type': 'application/json',
45
+ },
46
+ body: JSON.stringify({ query, filter, options }),
47
+ });
48
+ const data = await response.json();
49
+ if (data.error) {
50
+ throw new Error(data.error.message || 'Search failed');
51
+ }
52
+ return data.data.results;
53
+ }
54
+ /**
55
+ * Index a conversation for semantic search
56
+ * @param organizationId - The organization ID
57
+ * @param conversation - The conversation to index
58
+ * @returns Promise<void>
59
+ * @example
60
+ * ```typescript
61
+ * await client.conversations.index("org_123", {
62
+ * id: "conv_123",
63
+ * subject: "Product Inquiry",
64
+ * content: "Customer asking about product features",
65
+ * metadata: {
66
+ * subject: "Product Inquiry",
67
+ * organizationId: "org_123",
68
+ * inboxId: "inbox_1",
69
+ * domainId: "domain_1",
70
+ * participants: ["customer@example.com"],
71
+ * threadId: "thread_1",
72
+ * timestamp: new Date()
73
+ * }
74
+ * });
75
+ * ```
76
+ */
77
+ async index(organizationId, conversation) {
78
+ const response = await fetch(`${this.baseUrl}/search/index`, {
79
+ method: 'POST',
80
+ headers: {
81
+ ...this.headers,
82
+ 'Content-Type': 'application/json',
83
+ },
84
+ body: JSON.stringify({ organizationId, conversation }),
85
+ });
86
+ const data = await response.json();
87
+ if (data.error || !data.data.success) {
88
+ throw new Error(data.error?.message || 'Failed to index conversation');
89
+ }
90
+ }
91
+ /**
92
+ * Index multiple conversations in batch
93
+ * @param organizationId - The organization ID
94
+ * @param conversations - Array of conversations to index
95
+ * @returns Promise<void>
96
+ * @example
97
+ * ```typescript
98
+ * await client.conversations.indexBatch("org_123", [
99
+ * {
100
+ * id: "conv_1",
101
+ * subject: "Support Request",
102
+ * content: "Customer needs help with login",
103
+ * metadata: {
104
+ * subject: "Support Request",
105
+ * organizationId: "org_123",
106
+ * inboxId: "support_inbox",
107
+ * domainId: "domain_1",
108
+ * participants: ["customer@example.com"],
109
+ * threadId: "thread_1",
110
+ * timestamp: new Date()
111
+ * }
112
+ * },
113
+ * // ... more conversations
114
+ * ]);
115
+ * ```
116
+ */
117
+ async indexBatch(organizationId, conversations) {
118
+ const response = await fetch(`${this.baseUrl}/search/index/batch`, {
119
+ method: 'POST',
120
+ headers: {
121
+ ...this.headers,
122
+ 'Content-Type': 'application/json',
123
+ },
124
+ body: JSON.stringify({ organizationId, conversations }),
125
+ });
126
+ const data = await response.json();
127
+ if (data.error || !data.data.success) {
128
+ throw new Error(data.error?.message || 'Failed to index conversations');
129
+ }
130
+ }
131
+ }
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AttachmentRecord, ConversationListParams, CreateDomainPayload, DomainEntry, InboxEntry, MessageListParams, SemanticSearchParams, SemanticSearchResult, SendMessagePayload, UnifiedMessage } from './types.js';
1
+ import type { AttachmentRecord, 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;
@@ -6,6 +6,7 @@ export type ClientOptions = {
6
6
  fetcher?: typeof fetch;
7
7
  };
8
8
  export declare class CommuneClient {
9
+ private searchClient;
9
10
  private baseUrl;
10
11
  private apiKey;
11
12
  private headers?;
@@ -59,9 +60,17 @@ export declare class CommuneClient {
59
60
  messages: {
60
61
  send: (payload: SendMessagePayload) => Promise<Record<string, unknown>>;
61
62
  list: (params: MessageListParams) => Promise<UnifiedMessage[]>;
62
- listByConversation: (conversationId: string, params?: ConversationListParams) => Promise<UnifiedMessage[]>;
63
+ listByConversation: (conversationId: string, params?: MessageListParams) => Promise<UnifiedMessage[]>;
64
+ };
65
+ conversations: {
66
+ search: (query: string, filter: SearchFilter, options?: SearchOptions) => Promise<SearchResult[]>;
67
+ index: (organizationId: string, conversation: IndexConversationPayload) => Promise<{
68
+ success: boolean;
69
+ }>;
70
+ indexBatch: (organizationId: string, conversations: IndexConversationPayload[]) => Promise<{
71
+ success: boolean;
72
+ }>;
63
73
  };
64
- search: (params: SemanticSearchParams) => Promise<SemanticSearchResult[]>;
65
74
  attachments: {
66
75
  get: (attachmentId: string) => Promise<AttachmentRecord>;
67
76
  };
package/dist/client.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { SearchClient } from './client/search.js';
1
2
  const DEFAULT_BASE_URL = 'https://web-production-3f46f.up.railway.app';
2
3
  const buildQuery = (params) => {
3
4
  const query = new URLSearchParams();
@@ -89,23 +90,42 @@ export class CommuneClient {
89
90
  inbox_id: params.inbox_id,
90
91
  })}`);
91
92
  },
92
- listByConversation: async (conversationId, params = {}) => {
93
+ listByConversation: async (conversationId, params) => {
93
94
  return this.request(`/api/conversations/${encodeURIComponent(conversationId)}/messages${buildQuery({
94
- limit: params.limit,
95
- order: params.order,
95
+ limit: params?.limit,
96
+ order: params?.order,
96
97
  })}`);
97
98
  },
98
99
  };
99
- // Semantic search across all organizational emails
100
- this.search = async (params) => {
101
- return this.request(`/api/search${buildQuery({
102
- q: params.query,
103
- limit: params.limit || 10,
104
- threshold: params.threshold || 0.7,
105
- before: params.before,
106
- after: params.after,
107
- sender: params.sender,
108
- })}`);
100
+ this.conversations = {
101
+ search: async (query, filter, options) => {
102
+ return this.request('/api/search', {
103
+ method: 'POST',
104
+ json: {
105
+ query,
106
+ filter,
107
+ options,
108
+ },
109
+ });
110
+ },
111
+ index: async (organizationId, conversation) => {
112
+ return this.request('/api/search/index', {
113
+ method: 'POST',
114
+ json: {
115
+ organizationId,
116
+ conversation,
117
+ },
118
+ });
119
+ },
120
+ indexBatch: async (organizationId, conversations) => {
121
+ return this.request('/api/search/index/batch', {
122
+ method: 'POST',
123
+ json: {
124
+ organizationId,
125
+ conversations,
126
+ },
127
+ });
128
+ },
109
129
  };
110
130
  this.attachments = {
111
131
  get: async (attachmentId) => {
@@ -116,6 +136,7 @@ export class CommuneClient {
116
136
  this.apiKey = options.apiKey;
117
137
  this.headers = options.headers;
118
138
  this.fetcher = options.fetcher || fetch;
139
+ this.searchClient = new SearchClient(this.baseUrl, { Authorization: `Bearer ${this.apiKey}` });
119
140
  }
120
141
  async request(path, options = {}) {
121
142
  const { json, headers, ...rest } = options;
@@ -0,0 +1,35 @@
1
+ export interface SearchFilter {
2
+ organizationId: string;
3
+ inboxIds?: string[];
4
+ participants?: string[];
5
+ domainId?: string;
6
+ startDate?: string;
7
+ endDate?: string;
8
+ }
9
+ export interface SearchOptions {
10
+ limit?: number;
11
+ offset?: number;
12
+ minScore?: number;
13
+ }
14
+ export interface SearchResult {
15
+ id: string;
16
+ score: number;
17
+ metadata: {
18
+ subject: string;
19
+ organizationId: string;
20
+ inboxId: string;
21
+ domainId: string;
22
+ participants: string[];
23
+ threadId: string;
24
+ timestamp: Date;
25
+ };
26
+ }
27
+ export interface ConversationMetadata {
28
+ subject: string;
29
+ organizationId: string;
30
+ inboxId: string;
31
+ domainId: string;
32
+ participants: string[];
33
+ threadId: string;
34
+ timestamp: Date;
35
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/types.d.ts CHANGED
@@ -126,6 +126,8 @@ export interface MessageListParams {
126
126
  export interface ConversationListParams {
127
127
  limit?: number;
128
128
  order?: 'asc' | 'desc';
129
+ before?: string;
130
+ after?: string;
129
131
  }
130
132
  export interface SvixHeaders {
131
133
  id: string;
@@ -149,16 +151,45 @@ export interface ApiResponse<T> {
149
151
  data: T;
150
152
  error?: ApiError;
151
153
  }
152
- export interface SemanticSearchParams {
153
- query: string;
154
+ export interface SearchFilter {
155
+ organizationId: string;
156
+ inboxIds?: string[];
157
+ participants?: string[];
158
+ domainId?: string;
159
+ startDate?: string;
160
+ endDate?: string;
161
+ }
162
+ export interface SearchOptions {
154
163
  limit?: number;
155
- threshold?: number;
156
- before?: string;
157
- after?: string;
158
- sender?: string;
164
+ offset?: number;
165
+ minScore?: number;
159
166
  }
160
- export interface SemanticSearchResult {
161
- message: UnifiedMessage;
162
- similarity: number;
163
- highlights: string[];
167
+ export interface SearchResult {
168
+ id: string;
169
+ score: number;
170
+ metadata: {
171
+ subject: string;
172
+ organizationId: string;
173
+ inboxId: string;
174
+ domainId: string;
175
+ participants: string[];
176
+ threadId: string;
177
+ timestamp: Date;
178
+ };
179
+ }
180
+ export interface ConversationMetadata {
181
+ subject: string;
182
+ organizationId: string;
183
+ inboxId: string;
184
+ domainId: string;
185
+ participants: string[];
186
+ threadId: string;
187
+ timestamp: Date;
188
+ }
189
+ export interface IndexConversationPayload {
190
+ id: string;
191
+ subject: string;
192
+ content: string;
193
+ metadata: ConversationMetadata;
164
194
  }
195
+ export type SearchType = 'vector' | 'agent';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commune-ai",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Our email infrastructure - webhooks, threads, history, and semantic search",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",