@attrove/sdk 0.1.11 → 0.1.13

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
@@ -41,6 +41,9 @@ Ask questions about the user's unified context with AI-generated answers.
41
41
  // Simple query
42
42
  const response = await attrove.query('What did Sarah say about the Q4 budget?');
43
43
  console.log(response.answer);
44
+ console.log(response.used_message_ids); // msg_xxx IDs
45
+ console.log(response.used_meeting_ids); // mtg_xxx IDs
46
+ console.log(response.used_event_ids); // evt_xxx IDs
44
47
 
45
48
  // Multi-turn conversation - pass history from previous response
46
49
  let history = response.history;
@@ -50,14 +53,14 @@ history = followUp.history;
50
53
 
51
54
  // With filters
52
55
  const filtered = await attrove.query('Latest updates', {
53
- integrationIds: ['integration-uuid'], // Only search specific integration
56
+ integrationIds: ['int_xxx'], // Only search specific integration
54
57
  includeSources: true // Include source snippets
55
58
  });
56
59
  ```
57
60
 
58
61
  ### `search(query, options?)`
59
62
 
60
- Semantic search that returns raw messages without AI summarization.
63
+ Semantic search that returns raw matches across messages, meetings, and events without AI summarization.
61
64
 
62
65
  ```typescript
63
66
  const results = await attrove.search('product launch', {
package/cjs/constants.js CHANGED
@@ -13,7 +13,7 @@ exports.getWsCloseReason = exports.WS_CLOSE_CODES = exports.RETRYABLE_STATUS_SET
13
13
  * Automatically synchronized with package.json during the release process.
14
14
  * For programmatic access, use `getVersion()` from './version'.
15
15
  */
16
- exports.SDK_VERSION = "0.1.11"; // auto-synced from package.json during release
16
+ exports.SDK_VERSION = "0.1.13"; // auto-synced from package.json during release
17
17
  /**
18
18
  * Default API base URL for Attrove services.
19
19
  */
@@ -65,6 +65,9 @@ class MessagesResource {
65
65
  if (options.offset !== undefined) {
66
66
  params.offset = String(options.offset);
67
67
  }
68
+ if (options.excludeBots !== undefined) {
69
+ params.exclude_bots = String(options.excludeBots);
70
+ }
68
71
  if (options.expand?.length) {
69
72
  params.expand = options.expand.join(",");
70
73
  }
@@ -6,6 +6,146 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.QueryResource = void 0;
9
+ function normalizeOptionalId(value) {
10
+ return value == null ? null : String(value);
11
+ }
12
+ function normalizeRequiredId(value) {
13
+ return String(value);
14
+ }
15
+ function normalizeKeyMessage(message) {
16
+ return {
17
+ message_id: normalizeRequiredId(message.message_id),
18
+ thread_id: normalizeOptionalId(message.thread_id),
19
+ conversation_id: normalizeOptionalId(message.conversation_id),
20
+ };
21
+ }
22
+ function normalizeThreadMessage(message) {
23
+ return {
24
+ message_id: normalizeRequiredId(message.message_id),
25
+ received_at: message.received_at,
26
+ integration_type: normalizeIntegrationType(message.integration_type),
27
+ integration_type_generic: message.integration_type_generic,
28
+ sender_name: message.sender_name,
29
+ recipient_names: message.recipient_names,
30
+ body_text: message.body_text,
31
+ thread_id: normalizeOptionalId(message.thread_id),
32
+ thread_message_count: message.thread_message_count,
33
+ thread_position: message.thread_position,
34
+ parent_message_id: normalizeOptionalId(message.parent_message_id),
35
+ conversation_type: message.conversation_type,
36
+ conversation_id: normalizeOptionalId(message.conversation_id),
37
+ conversation_participants: message.conversation_participants,
38
+ };
39
+ }
40
+ const VALID_MEETING_PROVIDERS = new Set([
41
+ 'google_meet',
42
+ 'zoom',
43
+ 'teams',
44
+ 'unknown',
45
+ ]);
46
+ function normalizeMeetingProvider(provider) {
47
+ if (VALID_MEETING_PROVIDERS.has(provider)) {
48
+ return provider;
49
+ }
50
+ // prettier-ignore
51
+ console.warn(`[Attrove SDK] Unknown meeting provider "${provider}" normalized to "unknown". SDK may need updating.`);
52
+ return 'unknown';
53
+ }
54
+ const VALID_INTEGRATION_TYPES = new Set([
55
+ 'slack',
56
+ 'gmail',
57
+ 'outlook',
58
+ 'google_calendar',
59
+ 'outlook_calendar',
60
+ 'unknown',
61
+ ]);
62
+ function normalizeIntegrationType(type) {
63
+ if (VALID_INTEGRATION_TYPES.has(type)) {
64
+ return type;
65
+ }
66
+ // prettier-ignore
67
+ console.warn(`[Attrove SDK] Unknown integration type "${type}" normalized to "unknown". SDK may need updating.`);
68
+ return 'unknown';
69
+ }
70
+ function normalizeSearchMeeting(meeting) {
71
+ return {
72
+ id: meeting.id,
73
+ title: meeting.title,
74
+ meeting_code: meeting.meeting_code,
75
+ start_time: meeting.start_time,
76
+ end_time: meeting.end_time,
77
+ summary: meeting.summary,
78
+ short_summary: meeting.short_summary,
79
+ action_items: meeting.action_items,
80
+ attendees: meeting.attendees,
81
+ meeting_link: meeting.meeting_link,
82
+ has_transcript: meeting.has_transcript,
83
+ provider: normalizeMeetingProvider(meeting.provider),
84
+ event_id: meeting.event_id,
85
+ created_at: meeting.created_at,
86
+ updated_at: meeting.updated_at,
87
+ };
88
+ }
89
+ function normalizeSearchEvent(event) {
90
+ let attendees;
91
+ if (Array.isArray(event.attendees)) {
92
+ attendees = event.attendees;
93
+ }
94
+ else if (event.attendees !== undefined && event.attendees !== null) {
95
+ // prettier-ignore
96
+ console.warn(`[Attrove SDK] Event ${event.id} has non-array attendees (${typeof event.attendees}), ignoring.`);
97
+ }
98
+ return {
99
+ id: event.id,
100
+ calendar_id: event.calendar_id,
101
+ title: event.title,
102
+ description: event.description,
103
+ start_time: event.start_time,
104
+ end_time: event.end_time,
105
+ location: event.location,
106
+ status: event.status,
107
+ all_day: event.all_day,
108
+ organizer_entity_id: event.organizer_entity_id,
109
+ attendee_entity_ids: event.attendee_entity_ids,
110
+ attendees,
111
+ html_link: event.html_link,
112
+ event_link: event.event_link,
113
+ created_at: event.created_at,
114
+ updated_at: event.updated_at,
115
+ };
116
+ }
117
+ function normalizeSearchResponse(response) {
118
+ const conversations = response.conversations || {};
119
+ if (!response.conversations) {
120
+ // prettier-ignore
121
+ console.warn('[Attrove SDK] Search response missing conversations field, defaulting to empty object.');
122
+ }
123
+ const normalizedConversations = {};
124
+ for (const [conversationId, conversation] of Object.entries(conversations)) {
125
+ const normalizedThreads = {};
126
+ for (const [threadId, messages] of Object.entries(conversation.threads || {})) {
127
+ normalizedThreads[threadId] = (messages || []).map(normalizeThreadMessage);
128
+ }
129
+ normalizedConversations[conversationId] = {
130
+ conversation_name: conversation.conversation_name,
131
+ threads: normalizedThreads,
132
+ };
133
+ }
134
+ if (!response.key_messages) {
135
+ // prettier-ignore
136
+ console.warn('[Attrove SDK] Search response missing key_messages field, defaulting to empty array.');
137
+ }
138
+ const result = {
139
+ key_messages: (response.key_messages || []).map(normalizeKeyMessage),
140
+ conversations: normalizedConversations,
141
+ key_meetings: (response.key_meetings || []).map(normalizeSearchMeeting),
142
+ key_events: (response.key_events || []).map(normalizeSearchEvent),
143
+ };
144
+ if (response.warnings && response.warnings.length > 0) {
145
+ result.warnings = response.warnings;
146
+ }
147
+ return result;
148
+ }
9
149
  /**
10
150
  * Query resource for RAG operations.
11
151
  *
@@ -48,7 +188,7 @@ class QueryResource {
48
188
  *
49
189
  * // With filters
50
190
  * const response = await attrove.query('Latest updates', {
51
- * integrationIds: ['550e8400-e29b-41d4-a716-446655440000'],
191
+ * integrationIds: ['int_xxx'],
52
192
  * includeSources: true
53
193
  * });
54
194
  * ```
@@ -71,25 +211,28 @@ class QueryResource {
71
211
  body.allow_bot_messages = options.allowBotMessages;
72
212
  }
73
213
  if (options.includeSources) {
74
- body.expand = "sources";
214
+ body.expand = 'sources';
75
215
  }
76
216
  const response = await this.http.post(`/v1/users/${this.userId}/query`, body);
77
217
  return {
78
218
  answer: response.answer,
79
219
  history: response.history,
80
220
  used_message_ids: response.used_message_ids,
221
+ used_meeting_ids: response.used_meeting_ids ?? [],
222
+ used_event_ids: response.used_event_ids ?? [],
81
223
  sources: response.sources,
82
224
  };
83
225
  }
84
226
  /**
85
227
  * Semantic search across the user's context.
86
228
  *
87
- * Returns raw matching messages grouped by conversation, without AI summarization.
88
- * Use this when you need full control over how results are displayed.
229
+ * Returns raw matching messages (grouped by conversation), plus matched meetings
230
+ * and calendar events, without AI summarization.
231
+ * Use this when you need full control over how results are displayed and processed.
89
232
  *
90
233
  * @param query - The search query (semantic, not keyword-based)
91
234
  * @param options - Search options including filters, date range, etc.
92
- * @returns Matching messages grouped by conversation
235
+ * @returns Matching messages grouped by conversation, plus matched meetings and calendar events
93
236
  *
94
237
  * @throws {AuthenticationError} If the API key is invalid or expired
95
238
  * @throws {ValidationError} If the search parameters are invalid
@@ -108,10 +251,16 @@ class QueryResource {
108
251
  * senderDomains: ['acme.com'],
109
252
  * includeBodyText: true
110
253
  * });
254
+ *
255
+ * // Expand meeting and event detail fields
256
+ * const richResults = await attrove.search('standup action items', {
257
+ * expand: ['summary', 'action_items', 'description']
258
+ * });
111
259
  * ```
112
260
  */
113
261
  async search(query, options = {}) {
114
262
  const body = { query };
263
+ const expandFields = new Set(options.expand || []);
115
264
  if (options.integrationIds?.length) {
116
265
  body.integration_ids = options.integrationIds;
117
266
  }
@@ -134,9 +283,13 @@ class QueryResource {
134
283
  body.entity_ids = options.entityIds;
135
284
  }
136
285
  if (options.includeBodyText) {
137
- body.expand = "body_text";
286
+ expandFields.add('body_text');
287
+ }
288
+ if (expandFields.size > 0) {
289
+ body.expand = Array.from(expandFields).join(',');
138
290
  }
139
- return this.http.post(`/v1/users/${this.userId}/search`, body);
291
+ const response = await this.http.post(`/v1/users/${this.userId}/search`, body);
292
+ return normalizeSearchResponse(response);
140
293
  }
141
294
  }
142
295
  exports.QueryResource = QueryResource;
@@ -6,7 +6,7 @@
6
6
  * and do not depend on internal monorepo packages.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.isValidStreamFrame = exports.ErrorCodes = exports.isValidISODate = exports.isValidApiKey = exports.createUUID = exports.createISODate = exports.createApiKey = exports.createConversationId = exports.createMessageId = exports.createIntegrationId = exports.createUserId = exports.isValidUUID = exports.asBrandedString = void 0;
9
+ exports.isValidStreamFrame = exports.ErrorCodes = exports.isValidISODate = exports.isApiKey = exports.isValidApiKey = exports.createUUID = exports.createISODate = exports.createApiKey = exports.createConversationId = exports.createMessageId = exports.createIntegrationId = exports.createUserId = exports.isValidUUID = exports.asBrandedString = void 0;
10
10
  /**
11
11
  * Helper to cast a string to a branded type.
12
12
  *
@@ -79,11 +79,11 @@ exports.isValidUUID = isValidUUID;
79
79
  * ```
80
80
  */
81
81
  function createUserId(value) {
82
- if (!value || typeof value !== "string") {
83
- return { success: false, error: "UserId must be a non-empty string" };
82
+ if (!value || typeof value !== 'string') {
83
+ return { success: false, error: 'UserId must be a non-empty string' };
84
84
  }
85
85
  if (!UUID_REGEX.test(value)) {
86
- return { success: false, error: "UserId must be a valid UUID format" };
86
+ return { success: false, error: 'UserId must be a valid UUID format' };
87
87
  }
88
88
  return { success: true, value: value };
89
89
  }
@@ -92,16 +92,16 @@ exports.createUserId = createUserId;
92
92
  * Validate and create an IntegrationId from a string.
93
93
  */
94
94
  function createIntegrationId(value) {
95
- if (!value || typeof value !== "string") {
95
+ if (!value || typeof value !== 'string') {
96
96
  return {
97
97
  success: false,
98
- error: "IntegrationId must be a non-empty string",
98
+ error: 'IntegrationId must be a non-empty string',
99
99
  };
100
100
  }
101
101
  if (!UUID_REGEX.test(value)) {
102
102
  return {
103
103
  success: false,
104
- error: "IntegrationId must be a valid UUID format",
104
+ error: 'IntegrationId must be a valid UUID format',
105
105
  };
106
106
  }
107
107
  return { success: true, value: value };
@@ -111,11 +111,11 @@ exports.createIntegrationId = createIntegrationId;
111
111
  * Validate and create a MessageId from a string.
112
112
  */
113
113
  function createMessageId(value) {
114
- if (!value || typeof value !== "string") {
115
- return { success: false, error: "MessageId must be a non-empty string" };
114
+ if (!value || typeof value !== 'string') {
115
+ return { success: false, error: 'MessageId must be a non-empty string' };
116
116
  }
117
117
  if (!UUID_REGEX.test(value)) {
118
- return { success: false, error: "MessageId must be a valid UUID format" };
118
+ return { success: false, error: 'MessageId must be a valid UUID format' };
119
119
  }
120
120
  return { success: true, value: value };
121
121
  }
@@ -124,16 +124,16 @@ exports.createMessageId = createMessageId;
124
124
  * Validate and create a ConversationId from a string.
125
125
  */
126
126
  function createConversationId(value) {
127
- if (!value || typeof value !== "string") {
127
+ if (!value || typeof value !== 'string') {
128
128
  return {
129
129
  success: false,
130
- error: "ConversationId must be a non-empty string",
130
+ error: 'ConversationId must be a non-empty string',
131
131
  };
132
132
  }
133
133
  if (!UUID_REGEX.test(value)) {
134
134
  return {
135
135
  success: false,
136
- error: "ConversationId must be a valid UUID format",
136
+ error: 'ConversationId must be a valid UUID format',
137
137
  };
138
138
  }
139
139
  return { success: true, value: value };
@@ -151,8 +151,8 @@ exports.createConversationId = createConversationId;
151
151
  * ```
152
152
  */
153
153
  function createApiKey(value) {
154
- if (!value || typeof value !== "string") {
155
- return { success: false, error: "ApiKey must be a non-empty string" };
154
+ if (!value || typeof value !== 'string') {
155
+ return { success: false, error: 'ApiKey must be a non-empty string' };
156
156
  }
157
157
  if (!API_KEY_REGEX.test(value)) {
158
158
  return {
@@ -175,22 +175,22 @@ exports.createApiKey = createApiKey;
175
175
  * ```
176
176
  */
177
177
  function createISODate(value) {
178
- if (!value || typeof value !== "string") {
178
+ if (!value || typeof value !== 'string') {
179
179
  return {
180
180
  success: false,
181
- error: "ISODateString must be a non-empty string",
181
+ error: 'ISODateString must be a non-empty string',
182
182
  };
183
183
  }
184
184
  if (!ISO_DATE_REGEX.test(value)) {
185
185
  return {
186
186
  success: false,
187
- error: "ISODateString must be a valid ISO 8601 date format",
187
+ error: 'ISODateString must be a valid ISO 8601 date format',
188
188
  };
189
189
  }
190
190
  // Additional validation: ensure it parses to a valid date
191
191
  const parsed = Date.parse(value);
192
192
  if (Number.isNaN(parsed)) {
193
- return { success: false, error: "ISODateString must be a valid date" };
193
+ return { success: false, error: 'ISODateString must be a valid date' };
194
194
  }
195
195
  return { success: true, value: value };
196
196
  }
@@ -199,22 +199,54 @@ exports.createISODate = createISODate;
199
199
  * Validate and create a UUID from a string.
200
200
  */
201
201
  function createUUID(value) {
202
- if (!value || typeof value !== "string") {
203
- return { success: false, error: "UUID must be a non-empty string" };
202
+ if (!value || typeof value !== 'string') {
203
+ return { success: false, error: 'UUID must be a non-empty string' };
204
204
  }
205
205
  if (!UUID_REGEX.test(value)) {
206
- return { success: false, error: "UUID must be a valid UUID format" };
206
+ return { success: false, error: 'UUID must be a valid UUID format' };
207
207
  }
208
208
  return { success: true, value: value };
209
209
  }
210
210
  exports.createUUID = createUUID;
211
211
  /**
212
212
  * Check if a string is a valid API key format.
213
+ * Returns a boolean without type narrowing.
214
+ *
215
+ * Handles non-string inputs defensively for runtime safety.
216
+ *
217
+ * @see {@link isApiKey} for a type guard that narrows to ApiKeyFormat
213
218
  */
214
219
  function isValidApiKey(value) {
215
- return API_KEY_REGEX.test(value);
220
+ return typeof value === 'string' && API_KEY_REGEX.test(value);
216
221
  }
217
222
  exports.isValidApiKey = isValidApiKey;
223
+ /**
224
+ * Type guard that narrows a string to ApiKeyFormat.
225
+ * Use this when you want compile-time type safety after runtime validation.
226
+ *
227
+ * **Note:** This narrows to `ApiKeyFormat` (template literal type), not the
228
+ * branded `ApiKeyToken`. Use {@link createApiKey} if you need a branded token.
229
+ *
230
+ * @param value - String to check
231
+ * @returns True if the value is a valid API key format, narrowing the type
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * const apiKey = process.env.ATTROVE_SECRET_KEY;
236
+ * if (apiKey && isApiKey(apiKey)) {
237
+ * // apiKey is now typed as ApiKeyFormat
238
+ * // TypeScript knows it starts with 'sk_'
239
+ * doSomethingWithKey(apiKey);
240
+ * }
241
+ * ```
242
+ *
243
+ * @see {@link createApiKey} for validation that returns a branded ApiKeyToken
244
+ * @see {@link isValidApiKey} for a simple boolean check without type narrowing
245
+ */
246
+ function isApiKey(value) {
247
+ return typeof value === 'string' && API_KEY_REGEX.test(value);
248
+ }
249
+ exports.isApiKey = isApiKey;
218
250
  /**
219
251
  * Check if a string is a valid ISO 8601 date format.
220
252
  */
@@ -230,44 +262,44 @@ exports.isValidISODate = isValidISODate;
230
262
  */
231
263
  exports.ErrorCodes = {
232
264
  // Authentication errors
233
- AUTH_MISSING_TOKEN: "AUTH_MISSING_TOKEN",
234
- AUTH_INVALID_TOKEN: "AUTH_INVALID_TOKEN",
235
- AUTH_EXPIRED_TOKEN: "AUTH_EXPIRED_TOKEN",
236
- AUTH_USER_MISMATCH: "AUTH_USER_MISMATCH",
237
- AUTH_INSUFFICIENT_PERMISSIONS: "AUTH_INSUFFICIENT_PERMISSIONS",
265
+ AUTH_MISSING_TOKEN: 'AUTH_MISSING_TOKEN',
266
+ AUTH_INVALID_TOKEN: 'AUTH_INVALID_TOKEN',
267
+ AUTH_EXPIRED_TOKEN: 'AUTH_EXPIRED_TOKEN',
268
+ AUTH_USER_MISMATCH: 'AUTH_USER_MISMATCH',
269
+ AUTH_INSUFFICIENT_PERMISSIONS: 'AUTH_INSUFFICIENT_PERMISSIONS',
238
270
  // Resource errors
239
- RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND",
240
- RESOURCE_ACCESS_DENIED: "RESOURCE_ACCESS_DENIED",
241
- RESOURCE_ALREADY_EXISTS: "RESOURCE_ALREADY_EXISTS",
242
- RESOURCE_DELETED: "RESOURCE_DELETED",
271
+ RESOURCE_NOT_FOUND: 'RESOURCE_NOT_FOUND',
272
+ RESOURCE_ACCESS_DENIED: 'RESOURCE_ACCESS_DENIED',
273
+ RESOURCE_ALREADY_EXISTS: 'RESOURCE_ALREADY_EXISTS',
274
+ RESOURCE_DELETED: 'RESOURCE_DELETED',
243
275
  // Validation errors
244
- VALIDATION_INVALID_ID: "VALIDATION_INVALID_ID",
245
- VALIDATION_REQUIRED_FIELD: "VALIDATION_REQUIRED_FIELD",
246
- VALIDATION_INVALID_FORMAT: "VALIDATION_INVALID_FORMAT",
247
- VALIDATION_OUT_OF_RANGE: "VALIDATION_OUT_OF_RANGE",
276
+ VALIDATION_INVALID_ID: 'VALIDATION_INVALID_ID',
277
+ VALIDATION_REQUIRED_FIELD: 'VALIDATION_REQUIRED_FIELD',
278
+ VALIDATION_INVALID_FORMAT: 'VALIDATION_INVALID_FORMAT',
279
+ VALIDATION_OUT_OF_RANGE: 'VALIDATION_OUT_OF_RANGE',
248
280
  // Integration errors
249
- INTEGRATION_OAUTH_FAILED: "INTEGRATION_OAUTH_FAILED",
250
- INTEGRATION_EMAIL_EXISTS: "INTEGRATION_EMAIL_EXISTS",
251
- INTEGRATION_TOKEN_EXPIRED: "INTEGRATION_TOKEN_EXPIRED",
252
- INTEGRATION_SYNC_FAILED: "INTEGRATION_SYNC_FAILED",
253
- INTEGRATION_NOT_CONNECTED: "INTEGRATION_NOT_CONNECTED",
281
+ INTEGRATION_OAUTH_FAILED: 'INTEGRATION_OAUTH_FAILED',
282
+ INTEGRATION_EMAIL_EXISTS: 'INTEGRATION_EMAIL_EXISTS',
283
+ INTEGRATION_TOKEN_EXPIRED: 'INTEGRATION_TOKEN_EXPIRED',
284
+ INTEGRATION_SYNC_FAILED: 'INTEGRATION_SYNC_FAILED',
285
+ INTEGRATION_NOT_CONNECTED: 'INTEGRATION_NOT_CONNECTED',
254
286
  // Rate limiting
255
- RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
287
+ RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',
256
288
  // Server errors
257
- INTERNAL_ERROR: "INTERNAL_ERROR",
258
- SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
259
- REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
289
+ INTERNAL_ERROR: 'INTERNAL_ERROR',
290
+ SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',
291
+ REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',
260
292
  };
261
293
  /**
262
294
  * Valid stream frame types.
263
295
  */
264
296
  const VALID_FRAME_TYPES = new Set([
265
- "chunk",
266
- "end",
267
- "error",
268
- "state",
269
- "message_ids",
270
- "stream_start",
297
+ 'chunk',
298
+ 'end',
299
+ 'error',
300
+ 'state',
301
+ 'message_ids',
302
+ 'stream_start',
271
303
  ]);
272
304
  /**
273
305
  * Runtime validator for StreamFrame objects.
@@ -286,35 +318,46 @@ const VALID_FRAME_TYPES = new Set([
286
318
  * ```
287
319
  */
288
320
  function isValidStreamFrame(data) {
289
- if (!data || typeof data !== "object") {
321
+ if (!data || typeof data !== 'object') {
290
322
  return false;
291
323
  }
292
324
  const frame = data;
293
325
  // All frames must have type and message_id
294
- if (typeof frame.type !== "string" || !VALID_FRAME_TYPES.has(frame.type)) {
326
+ if (typeof frame.type !== 'string' || !VALID_FRAME_TYPES.has(frame.type)) {
295
327
  return false;
296
328
  }
297
- if (typeof frame.message_id !== "string") {
329
+ if (typeof frame.message_id !== 'string') {
298
330
  return false;
299
331
  }
300
332
  // Type-specific validation
301
333
  switch (frame.type) {
302
- case "chunk":
303
- return typeof frame.content === "string";
304
- case "end":
305
- return (typeof frame.reason === "string" &&
306
- ["completed", "cancelled", "error"].includes(frame.reason));
307
- case "error":
308
- return typeof frame.error === "string";
309
- case "state":
310
- return typeof frame.state === "string";
311
- case "message_ids":
334
+ case 'chunk':
335
+ return typeof frame.content === 'string';
336
+ case 'end':
337
+ return (typeof frame.reason === 'string' &&
338
+ ['completed', 'cancelled', 'error'].includes(frame.reason) &&
339
+ isOptionalStringArray(frame.used_message_ids) &&
340
+ isOptionalStringArray(frame.used_meeting_ids) &&
341
+ isOptionalStringArray(frame.used_event_ids));
342
+ case 'error':
343
+ return typeof frame.error === 'string';
344
+ case 'state':
345
+ return typeof frame.state === 'string';
346
+ case 'message_ids':
312
347
  return (Array.isArray(frame.used_message_ids) &&
313
- frame.used_message_ids.every((id) => typeof id === "string"));
314
- case "stream_start":
348
+ frame.used_message_ids.every((id) => typeof id === 'string') &&
349
+ isOptionalStringArray(frame.used_meeting_ids) &&
350
+ isOptionalStringArray(frame.used_event_ids));
351
+ case 'stream_start':
315
352
  return true;
316
353
  default:
317
354
  return false;
318
355
  }
319
356
  }
320
357
  exports.isValidStreamFrame = isValidStreamFrame;
358
+ function isOptionalStringArray(value) {
359
+ if (value === undefined)
360
+ return true;
361
+ return (Array.isArray(value) &&
362
+ value.every((item) => typeof item === 'string'));
363
+ }
@@ -108,6 +108,8 @@ class StreamingClient {
108
108
  this.onWarning = onWarning;
109
109
  let answer = "";
110
110
  let usedMessageIds = [];
111
+ let usedMeetingIds = [];
112
+ let usedEventIds = [];
111
113
  let cancelled = false;
112
114
  let completed = false;
113
115
  // Connect to WebSocket
@@ -225,6 +227,12 @@ class StreamingClient {
225
227
  break;
226
228
  case "message_ids":
227
229
  usedMessageIds = frame.used_message_ids;
230
+ if (frame.used_meeting_ids !== undefined) {
231
+ usedMeetingIds = frame.used_meeting_ids;
232
+ }
233
+ if (frame.used_event_ids !== undefined) {
234
+ usedEventIds = frame.used_event_ids;
235
+ }
228
236
  break;
229
237
  case "error": {
230
238
  const error = new index_js_2.AttroveError(frame.error, index_js_3.ErrorCodes.INTERNAL_ERROR);
@@ -238,6 +246,12 @@ class StreamingClient {
238
246
  if (frame.used_message_ids) {
239
247
  usedMessageIds = frame.used_message_ids;
240
248
  }
249
+ if (frame.used_meeting_ids !== undefined) {
250
+ usedMeetingIds = frame.used_meeting_ids;
251
+ }
252
+ if (frame.used_event_ids !== undefined) {
253
+ usedEventIds = frame.used_event_ids;
254
+ }
241
255
  onEnd?.(frame.reason);
242
256
  // Build updated history
243
257
  const updatedHistory = [
@@ -249,6 +263,8 @@ class StreamingClient {
249
263
  answer,
250
264
  history: updatedHistory,
251
265
  usedMessageIds,
266
+ usedMeetingIds,
267
+ usedEventIds,
252
268
  cancelled,
253
269
  });
254
270
  break;
@@ -10,7 +10,7 @@
10
10
  * Automatically synchronized with package.json during the release process.
11
11
  * For programmatic access, use `getVersion()` from './version'.
12
12
  */
13
- export declare const SDK_VERSION = "0.1.11";
13
+ export declare const SDK_VERSION = "0.1.13";
14
14
  /**
15
15
  * Default API base URL for Attrove services.
16
16
  */
package/esm/constants.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * Automatically synchronized with package.json during the release process.
11
11
  * For programmatic access, use `getVersion()` from './version'.
12
12
  */
13
- export const SDK_VERSION = "0.1.11"; // auto-synced from package.json during release
13
+ export const SDK_VERSION = "0.1.13"; // auto-synced from package.json during release
14
14
  /**
15
15
  * Default API base URL for Attrove services.
16
16
  */
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../../../packages/sdk/src/resources/messages.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,OAAO,EAEP,UAAU,EACV,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM;IAGjC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,IAAI,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC;IAwCpE;;;;;;;;;;;;;;;OAeG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGxC"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../../../packages/sdk/src/resources/messages.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,OAAO,EAEP,UAAU,EACV,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM;IAGjC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,IAAI,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC;IA2CpE;;;;;;;;;;;;;;;OAeG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGxC"}
@@ -62,6 +62,9 @@ export class MessagesResource {
62
62
  if (options.offset !== undefined) {
63
63
  params.offset = String(options.offset);
64
64
  }
65
+ if (options.excludeBots !== undefined) {
66
+ params.exclude_bots = String(options.excludeBots);
67
+ }
65
68
  if (options.expand?.length) {
66
69
  params.expand = options.expand.join(",");
67
70
  }