@attrove/sdk 0.1.18 → 0.2.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.
Files changed (62) hide show
  1. package/cjs/client.js +4 -0
  2. package/cjs/constants.js +1 -1
  3. package/cjs/resources/index.js +5 -1
  4. package/cjs/resources/messages.js +3 -0
  5. package/cjs/resources/notes.js +106 -0
  6. package/cjs/resources/push.js +315 -0
  7. package/cjs/resources/query.js +6 -2
  8. package/cjs/resources/webhooks.js +1 -0
  9. package/cjs/types/index.js +19 -3
  10. package/cjs/utils/index.js +3 -1
  11. package/cjs/utils/streaming.js +8 -0
  12. package/cjs/utils/validate-ref-pair.js +45 -0
  13. package/esm/client.d.ts +12 -0
  14. package/esm/client.d.ts.map +1 -1
  15. package/esm/client.js +4 -0
  16. package/esm/client.js.map +1 -1
  17. package/esm/constants.d.ts +1 -1
  18. package/esm/constants.d.ts.map +1 -1
  19. package/esm/constants.js +1 -1
  20. package/esm/constants.js.map +1 -1
  21. package/esm/index.d.ts +1 -0
  22. package/esm/index.d.ts.map +1 -1
  23. package/esm/index.js.map +1 -1
  24. package/esm/resources/index.d.ts +3 -0
  25. package/esm/resources/index.d.ts.map +1 -1
  26. package/esm/resources/index.js +2 -0
  27. package/esm/resources/index.js.map +1 -1
  28. package/esm/resources/messages.d.ts.map +1 -1
  29. package/esm/resources/messages.js +3 -0
  30. package/esm/resources/messages.js.map +1 -1
  31. package/esm/resources/notes.d.ts +80 -0
  32. package/esm/resources/notes.d.ts.map +1 -0
  33. package/esm/resources/notes.js +103 -0
  34. package/esm/resources/notes.js.map +1 -0
  35. package/esm/resources/push.d.ts +140 -0
  36. package/esm/resources/push.d.ts.map +1 -0
  37. package/esm/resources/push.js +312 -0
  38. package/esm/resources/push.js.map +1 -0
  39. package/esm/resources/query.d.ts.map +1 -1
  40. package/esm/resources/query.js +6 -2
  41. package/esm/resources/query.js.map +1 -1
  42. package/esm/resources/webhooks.d.ts +1 -1
  43. package/esm/resources/webhooks.d.ts.map +1 -1
  44. package/esm/resources/webhooks.js +1 -0
  45. package/esm/resources/webhooks.js.map +1 -1
  46. package/esm/types/index.d.ts +202 -0
  47. package/esm/types/index.d.ts.map +1 -1
  48. package/esm/types/index.js +18 -2
  49. package/esm/types/index.js.map +1 -1
  50. package/esm/utils/index.d.ts +1 -0
  51. package/esm/utils/index.d.ts.map +1 -1
  52. package/esm/utils/index.js +1 -0
  53. package/esm/utils/index.js.map +1 -1
  54. package/esm/utils/streaming.d.ts +4 -0
  55. package/esm/utils/streaming.d.ts.map +1 -1
  56. package/esm/utils/streaming.js +8 -0
  57. package/esm/utils/streaming.js.map +1 -1
  58. package/esm/utils/validate-ref-pair.d.ts +23 -0
  59. package/esm/utils/validate-ref-pair.d.ts.map +1 -0
  60. package/esm/utils/validate-ref-pair.js +42 -0
  61. package/esm/utils/validate-ref-pair.js.map +1 -0
  62. package/package.json +1 -1
package/cjs/client.js CHANGED
@@ -18,6 +18,8 @@ const calendars_js_1 = require("./resources/calendars.js");
18
18
  const events_js_1 = require("./resources/events.js");
19
19
  const meetings_js_1 = require("./resources/meetings.js");
20
20
  const threads_js_1 = require("./resources/threads.js");
21
+ const notes_js_1 = require("./resources/notes.js");
22
+ const push_js_1 = require("./resources/push.js");
21
23
  const query_js_1 = require("./resources/query.js");
22
24
  const index_js_1 = require("./errors/index.js");
23
25
  const index_js_2 = require("./types/index.js");
@@ -124,6 +126,8 @@ class Attrove {
124
126
  this.events = new events_js_1.EventsResource(this.http, this.config.userId);
125
127
  this.meetings = new meetings_js_1.MeetingsResource(this.http, this.config.userId);
126
128
  this.threads = new threads_js_1.ThreadsResource(this.http, this.config.userId);
129
+ this.notes = new notes_js_1.NotesResource(this.http, this.config.userId);
130
+ this.push = new push_js_1.PushResource(this.http, this.config.userId);
127
131
  this.queryResource = new query_js_1.QueryResource(this.http, this.config.userId);
128
132
  }
129
133
  /**
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.18"; // auto-synced from package.json during release
16
+ exports.SDK_VERSION = "0.2.0"; // auto-synced from package.json during release
17
17
  /**
18
18
  * Default API base URL for Attrove services.
19
19
  */
@@ -3,7 +3,7 @@
3
3
  * Resource exports
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AdminWebhooksResource = exports.AdminSettingsResource = exports.QueryResource = exports.ThreadsResource = exports.MeetingsResource = exports.EventsResource = exports.CalendarsResource = exports.EntitiesResource = exports.IntegrationsResource = exports.ConversationsResource = exports.MessagesResource = exports.UsersResource = void 0;
6
+ exports.AdminWebhooksResource = exports.PushResource = exports.NotesResource = exports.AdminSettingsResource = exports.QueryResource = exports.ThreadsResource = exports.MeetingsResource = exports.EventsResource = exports.CalendarsResource = exports.EntitiesResource = exports.IntegrationsResource = exports.ConversationsResource = exports.MessagesResource = exports.UsersResource = void 0;
7
7
  var users_js_1 = require("./users.js");
8
8
  Object.defineProperty(exports, "UsersResource", { enumerable: true, get: function () { return users_js_1.UsersResource; } });
9
9
  var messages_js_1 = require("./messages.js");
@@ -26,5 +26,9 @@ var query_js_1 = require("./query.js");
26
26
  Object.defineProperty(exports, "QueryResource", { enumerable: true, get: function () { return query_js_1.QueryResource; } });
27
27
  var settings_js_1 = require("./settings.js");
28
28
  Object.defineProperty(exports, "AdminSettingsResource", { enumerable: true, get: function () { return settings_js_1.AdminSettingsResource; } });
29
+ var notes_js_1 = require("./notes.js");
30
+ Object.defineProperty(exports, "NotesResource", { enumerable: true, get: function () { return notes_js_1.NotesResource; } });
31
+ var push_js_1 = require("./push.js");
32
+ Object.defineProperty(exports, "PushResource", { enumerable: true, get: function () { return push_js_1.PushResource; } });
29
33
  var webhooks_js_1 = require("./webhooks.js");
30
34
  Object.defineProperty(exports, "AdminWebhooksResource", { enumerable: true, get: function () { return webhooks_js_1.AdminWebhooksResource; } });
@@ -71,6 +71,9 @@ class MessagesResource {
71
71
  if (options.entityId) {
72
72
  params.entity_id = options.entityId;
73
73
  }
74
+ if (options.entityScope) {
75
+ params.entity_scope = options.entityScope;
76
+ }
74
77
  if (options.excludeBots !== undefined) {
75
78
  params.exclude_bots = String(options.excludeBots);
76
79
  }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /**
3
+ * Notes Resource
4
+ *
5
+ * Provides methods for listing and retrieving notes.
6
+ * Notes are analyst observations, partner-pushed context, or session summaries
7
+ * that are RAG-indexed and become queryable via query() and search().
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.NotesResource = void 0;
11
+ const validate_ref_pair_js_1 = require("../utils/validate-ref-pair.js");
12
+ const index_js_1 = require("../errors/index.js");
13
+ const index_js_2 = require("../types/index.js");
14
+ /**
15
+ * Notes resource for listing and retrieving notes.
16
+ *
17
+ * Notes are RAG-indexed context items that can be created via the Push API
18
+ * (`attrove.push.note()`) or by partner systems. They support cross-referencing
19
+ * to other primitives (messages, meetings, events, entities) via `ref_type` and `ref_id`.
20
+ */
21
+ class NotesResource {
22
+ constructor(http, userId) {
23
+ this.http = http;
24
+ this.userId = userId;
25
+ }
26
+ /**
27
+ * List notes with optional filtering.
28
+ *
29
+ * Supports filtering by specific IDs (useful after receiving a `notes.new` webhook),
30
+ * by reference type/ID (find all notes linked to a specific message or meeting),
31
+ * and standard pagination.
32
+ *
33
+ * @param options - Filtering and pagination options
34
+ * @returns Paginated list of notes
35
+ *
36
+ * @throws {AuthenticationError} If the API key is invalid or expired
37
+ * @throws {ValidationError} If the filter parameters are invalid
38
+ * @throws {NetworkError} If unable to reach the API
39
+ * @throws {ServerError} If the server encounters an error
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * // List recent notes
44
+ * const { data, pagination } = await attrove.notes.list();
45
+ *
46
+ * // Fetch specific notes from a webhook payload
47
+ * const { data } = await attrove.notes.list({
48
+ * ids: ['note_1Z', 'note_2B4'],
49
+ * });
50
+ *
51
+ * // Find notes linked to a specific message
52
+ * const { data } = await attrove.notes.list({
53
+ * refType: 'message',
54
+ * refId: 'msg_xxx',
55
+ * });
56
+ * ```
57
+ */
58
+ async list(options = {}) {
59
+ (0, validate_ref_pair_js_1.validateRefPair)(options.refType, options.refId);
60
+ const params = {};
61
+ if (options.ids?.length) {
62
+ params.ids = options.ids.join(',');
63
+ }
64
+ if (options.refType) {
65
+ params.ref_type = options.refType;
66
+ }
67
+ if (options.refId) {
68
+ params.ref_id = options.refId;
69
+ }
70
+ if (options.limit !== undefined) {
71
+ params.limit = String(options.limit);
72
+ }
73
+ if (options.offset !== undefined) {
74
+ params.offset = String(options.offset);
75
+ }
76
+ const response = await this.http.request(`/v1/users/${this.userId}/notes`, { method: 'GET' }, params);
77
+ return {
78
+ data: response.data,
79
+ pagination: response.pagination,
80
+ };
81
+ }
82
+ /**
83
+ * Get a single note by ID.
84
+ *
85
+ * @param id - Note ID (opaque note_xxx format)
86
+ * @returns The requested note
87
+ *
88
+ * @throws {AuthenticationError} If the API key is invalid or expired
89
+ * @throws {NotFoundError} If the note does not exist
90
+ * @throws {ValidationError} If the ID format is invalid
91
+ * @throws {NetworkError} If unable to reach the API
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * const note = await attrove.notes.get('note_1Z');
96
+ * console.log(note.title, note.body);
97
+ * ```
98
+ */
99
+ async get(id) {
100
+ if (!id || typeof id !== 'string') {
101
+ throw new index_js_1.ValidationError('id is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'id', received: typeof id });
102
+ }
103
+ return this.http.get(`/v1/users/${this.userId}/notes/${id}`);
104
+ }
105
+ }
106
+ exports.NotesResource = NotesResource;
@@ -0,0 +1,315 @@
1
+ "use strict";
2
+ /**
3
+ * Push Resource
4
+ *
5
+ * Provides methods for pushing data directly into Attrove without
6
+ * requiring user OAuth connections. Supports messages, meetings,
7
+ * events, and notes. All pushed items are asynchronously RAG-indexed
8
+ * and become queryable via query() and search().
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.PushResource = void 0;
12
+ const validate_ref_pair_js_1 = require("../utils/validate-ref-pair.js");
13
+ const index_js_1 = require("../errors/index.js");
14
+ const index_js_2 = require("../types/index.js");
15
+ /**
16
+ * Push resource for ingesting data directly into a user's context.
17
+ *
18
+ * Each method auto-provisions a virtual integration (e.g., `manual_email`,
19
+ * `manual_notes`) on first push for the user. Items are queued for
20
+ * asynchronous RAG processing and fire webhook events when indexed.
21
+ *
22
+ * Idempotent when `externalId` is provided: re-pushing the same external ID
23
+ * updates the existing item and re-indexes it.
24
+ */
25
+ class PushResource {
26
+ constructor(http, userId) {
27
+ this.http = http;
28
+ this.userId = userId;
29
+ }
30
+ /**
31
+ * Push a message (email, chat, alert, or custom) for the user.
32
+ *
33
+ * The `source` field determines the virtual integration type:
34
+ * - `email` → `manual_email` (subject-based threading)
35
+ * - `chat`, `alert`, `custom` → `manual_chat` (channel-based grouping)
36
+ *
37
+ * @param input - Message data
38
+ * @returns Push response with opaque ID (msg_xxx) and processing status
39
+ *
40
+ * @throws {ValidationError} If required fields are missing or invalid
41
+ * @throws {AuthenticationError} If the API key is invalid or expired
42
+ * @throws {ServerError} If the server encounters an error
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const result = await attrove.push.message({
47
+ * source: 'email',
48
+ * bodyText: 'Hey team, the Q4 report is ready for review.',
49
+ * subject: 'Q4 Report Ready',
50
+ * senderEmail: 'alice@acme.com',
51
+ * senderName: 'Alice Chen',
52
+ * externalId: 'email-12345',
53
+ * });
54
+ * console.log(result.id); // msg_xxx
55
+ * console.log(result.status); // 'queued'
56
+ * ```
57
+ */
58
+ async message(input) {
59
+ const validSources = index_js_2.PUSH_MESSAGE_SOURCES;
60
+ if (!input.source || !validSources.includes(input.source)) {
61
+ throw new index_js_1.ValidationError(`source must be one of: ${validSources.join(', ')}`, index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'source', received: input.source });
62
+ }
63
+ if (!input.bodyText ||
64
+ typeof input.bodyText !== 'string' ||
65
+ input.bodyText.trim() === '') {
66
+ throw new index_js_1.ValidationError('bodyText is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'bodyText' });
67
+ }
68
+ const body = {
69
+ source: input.source,
70
+ body_text: input.bodyText,
71
+ };
72
+ if (input.subject != null)
73
+ body.subject = input.subject;
74
+ if (input.senderName != null)
75
+ body.sender_name = input.senderName;
76
+ if (input.senderEmail != null)
77
+ body.sender_email = input.senderEmail;
78
+ if (input.recipientEmails != null)
79
+ body.recipient_emails = input.recipientEmails;
80
+ if (input.receivedAt != null)
81
+ body.received_at = input.receivedAt;
82
+ if (input.externalId != null)
83
+ body.external_id = input.externalId;
84
+ if (input.threadId != null)
85
+ body.thread_id = input.threadId;
86
+ if (input.metadata != null)
87
+ body.metadata = input.metadata;
88
+ const data = await this.http.post(`/v1/users/${this.userId}/messages`, body);
89
+ if (!data?.id || !data?.status) {
90
+ throw new index_js_1.ServerError('Unexpected response shape from push message endpoint', 500, {
91
+ missingFields: [
92
+ ...(!data?.id ? ['id'] : []),
93
+ ...(!data?.status ? ['status'] : []),
94
+ ],
95
+ received: (() => {
96
+ if (data == null)
97
+ return String(data);
98
+ const raw = JSON.stringify(data);
99
+ return raw.length > 200 ? raw.substring(0, 200) + '...' : raw;
100
+ })(),
101
+ });
102
+ }
103
+ return { ...data };
104
+ }
105
+ /**
106
+ * Push a meeting with transcript, summary, and attendees for the user.
107
+ *
108
+ * @param input - Meeting data
109
+ * @returns Push response with opaque ID (mtg_xxx) and processing status
110
+ *
111
+ * @throws {ValidationError} If required fields are missing or invalid
112
+ * @throws {AuthenticationError} If the API key is invalid or expired
113
+ * @throws {ServerError} If the server encounters an error
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * const result = await attrove.push.meeting({
118
+ * title: 'Weekly Standup',
119
+ * startTime: '2026-03-23T09:00:00Z',
120
+ * endTime: '2026-03-23T09:30:00Z',
121
+ * transcript: 'Alice: Good morning everyone...',
122
+ * summary: 'Discussed sprint progress and blockers.',
123
+ * attendees: [
124
+ * { name: 'Alice Chen', email: 'alice@acme.com' },
125
+ * { name: 'Bob Smith', email: 'bob@acme.com' },
126
+ * ],
127
+ * externalId: 'meeting-xyz',
128
+ * });
129
+ * console.log(result.id); // mtg_xxx
130
+ * ```
131
+ */
132
+ async meeting(input) {
133
+ if (!input.title ||
134
+ typeof input.title !== 'string' ||
135
+ input.title.trim() === '') {
136
+ throw new index_js_1.ValidationError('title is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'title' });
137
+ }
138
+ if (!input.startTime) {
139
+ throw new index_js_1.ValidationError('startTime is required (ISO 8601 datetime)', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'startTime' });
140
+ }
141
+ if (!input.endTime) {
142
+ throw new index_js_1.ValidationError('endTime is required (ISO 8601 datetime)', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'endTime' });
143
+ }
144
+ const body = {
145
+ title: input.title,
146
+ start_time: input.startTime,
147
+ end_time: input.endTime,
148
+ };
149
+ if (input.transcript != null)
150
+ body.transcript = input.transcript;
151
+ if (input.summary != null)
152
+ body.summary = input.summary;
153
+ if (input.shortSummary != null)
154
+ body.short_summary = input.shortSummary;
155
+ if (input.actionItems != null)
156
+ body.action_items = input.actionItems;
157
+ if (input.attendees != null)
158
+ body.attendees = input.attendees;
159
+ if (input.externalId != null)
160
+ body.external_id = input.externalId;
161
+ if (input.metadata != null)
162
+ body.metadata = input.metadata;
163
+ const data = await this.http.post(`/v1/users/${this.userId}/meetings`, body);
164
+ if (!data?.id || !data?.status) {
165
+ throw new index_js_1.ServerError('Unexpected response shape from push meeting endpoint', 500, {
166
+ missingFields: [
167
+ ...(!data?.id ? ['id'] : []),
168
+ ...(!data?.status ? ['status'] : []),
169
+ ],
170
+ received: (() => {
171
+ if (data == null)
172
+ return String(data);
173
+ const raw = JSON.stringify(data);
174
+ return raw.length > 200 ? raw.substring(0, 200) + '...' : raw;
175
+ })(),
176
+ });
177
+ }
178
+ return { ...data };
179
+ }
180
+ /**
181
+ * Push a calendar event or milestone for the user.
182
+ *
183
+ * If `endTime` is omitted, it defaults to `startTime` (point-in-time event).
184
+ *
185
+ * @param input - Event data
186
+ * @returns Push response with opaque ID (evt_xxx) and processing status
187
+ *
188
+ * @throws {ValidationError} If required fields are missing or invalid
189
+ * @throws {AuthenticationError} If the API key is invalid or expired
190
+ * @throws {ServerError} If the server encounters an error
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const result = await attrove.push.event({
195
+ * title: 'Product Launch',
196
+ * startTime: '2026-04-01T00:00:00Z',
197
+ * description: 'V2 launch day — all hands on deck.',
198
+ * allDay: true,
199
+ * externalId: 'milestone-launch-v2',
200
+ * });
201
+ * console.log(result.id); // evt_xxx
202
+ * ```
203
+ */
204
+ async event(input) {
205
+ if (!input.title ||
206
+ typeof input.title !== 'string' ||
207
+ input.title.trim() === '') {
208
+ throw new index_js_1.ValidationError('title is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'title' });
209
+ }
210
+ if (!input.startTime) {
211
+ throw new index_js_1.ValidationError('startTime is required (ISO 8601 datetime)', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'startTime' });
212
+ }
213
+ const body = {
214
+ title: input.title,
215
+ start_time: input.startTime,
216
+ };
217
+ if (input.endTime != null)
218
+ body.end_time = input.endTime;
219
+ if (input.description != null)
220
+ body.description = input.description;
221
+ if (input.location != null)
222
+ body.location = input.location;
223
+ if (input.allDay != null)
224
+ body.all_day = input.allDay;
225
+ if (input.externalId != null)
226
+ body.external_id = input.externalId;
227
+ if (input.metadata != null)
228
+ body.metadata = input.metadata;
229
+ const data = await this.http.post(`/v1/users/${this.userId}/events`, body);
230
+ if (!data?.id || !data?.status) {
231
+ throw new index_js_1.ServerError('Unexpected response shape from push event endpoint', 500, {
232
+ missingFields: [
233
+ ...(!data?.id ? ['id'] : []),
234
+ ...(!data?.status ? ['status'] : []),
235
+ ],
236
+ received: (() => {
237
+ if (data == null)
238
+ return String(data);
239
+ const raw = JSON.stringify(data);
240
+ return raw.length > 200 ? raw.substring(0, 200) + '...' : raw;
241
+ })(),
242
+ });
243
+ }
244
+ return { ...data };
245
+ }
246
+ /**
247
+ * Push an analyst note or observation for the user.
248
+ *
249
+ * Notes can optionally reference another primitive (message, meeting, event,
250
+ * or entity) via `refType` and `refId`. Both must be provided together or
251
+ * both omitted. The `refId` must use the correct opaque prefix for its type
252
+ * (msg_xxx, mtg_xxx, evt_xxx, ent_xxx).
253
+ *
254
+ * @param input - Note data
255
+ * @returns Push response with opaque ID (note_xxx) and processing status
256
+ *
257
+ * @throws {ValidationError} If required fields are missing or invalid
258
+ * @throws {AuthenticationError} If the API key is invalid or expired
259
+ * @throws {ServerError} If the server encounters an error
260
+ *
261
+ * @example
262
+ * ```ts
263
+ * // Standalone note
264
+ * const result = await attrove.push.note({
265
+ * body: 'Decision: chose Redis for caching due to latency requirements.',
266
+ * title: 'Architecture Decision',
267
+ * });
268
+ *
269
+ * // Note linked to a message
270
+ * const linked = await attrove.push.note({
271
+ * body: 'This email contains the final pricing agreement.',
272
+ * refType: 'message',
273
+ * refId: 'msg_1Z',
274
+ * });
275
+ * ```
276
+ */
277
+ async note(input) {
278
+ if (!input.body ||
279
+ typeof input.body !== 'string' ||
280
+ input.body.trim() === '') {
281
+ throw new index_js_1.ValidationError('body is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'body' });
282
+ }
283
+ const validated = (0, validate_ref_pair_js_1.validateRefPair)(input.refType, input.refId);
284
+ const body = {
285
+ body: input.body,
286
+ };
287
+ if (input.title != null)
288
+ body.title = input.title;
289
+ if (validated.refType != null)
290
+ body.ref_type = validated.refType;
291
+ if (validated.refId != null)
292
+ body.ref_id = validated.refId;
293
+ if (input.externalId != null)
294
+ body.external_id = input.externalId;
295
+ if (input.metadata != null)
296
+ body.metadata = input.metadata;
297
+ const data = await this.http.post(`/v1/users/${this.userId}/notes`, body);
298
+ if (!data?.id || !data?.status) {
299
+ throw new index_js_1.ServerError('Unexpected response shape from push note endpoint', 500, {
300
+ missingFields: [
301
+ ...(!data?.id ? ['id'] : []),
302
+ ...(!data?.status ? ['status'] : []),
303
+ ],
304
+ received: (() => {
305
+ if (data == null)
306
+ return String(data);
307
+ const raw = JSON.stringify(data);
308
+ return raw.length > 200 ? raw.substring(0, 200) + '...' : raw;
309
+ })(),
310
+ });
311
+ }
312
+ return { ...data };
313
+ }
314
+ }
315
+ exports.PushResource = PushResource;
@@ -214,7 +214,7 @@ class QueryResource {
214
214
  body.expand = 'sources';
215
215
  }
216
216
  const config = options.idempotencyKey
217
- ? { headers: { "Idempotency-Key": options.idempotencyKey } }
217
+ ? { headers: { 'Idempotency-Key': options.idempotencyKey } }
218
218
  : undefined;
219
219
  const response = await this.http.post(`/v1/users/${this.userId}/query`, body, config);
220
220
  return {
@@ -223,6 +223,7 @@ class QueryResource {
223
223
  used_message_ids: response.used_message_ids,
224
224
  used_meeting_ids: response.used_meeting_ids ?? [],
225
225
  used_event_ids: response.used_event_ids ?? [],
226
+ used_note_ids: response.used_note_ids ?? [],
226
227
  sources: response.sources,
227
228
  };
228
229
  }
@@ -285,6 +286,9 @@ class QueryResource {
285
286
  if (options.entityIds?.length) {
286
287
  body.entity_ids = options.entityIds;
287
288
  }
289
+ if (options.retrievalMode) {
290
+ body.retrieval_mode = options.retrievalMode;
291
+ }
288
292
  if (options.includeBodyText) {
289
293
  expandFields.add('body_text');
290
294
  }
@@ -292,7 +296,7 @@ class QueryResource {
292
296
  body.expand = Array.from(expandFields).join(',');
293
297
  }
294
298
  const config = options.idempotencyKey
295
- ? { headers: { "Idempotency-Key": options.idempotencyKey } }
299
+ ? { headers: { 'Idempotency-Key': options.idempotencyKey } }
296
300
  : undefined;
297
301
  const response = await this.http.post(`/v1/users/${this.userId}/search`, body, config);
298
302
  return normalizeSearchResponse(response);
@@ -11,6 +11,7 @@ const ADMIN_WEBHOOK_SUBSCRIBABLE_EVENT_TYPES = [
11
11
  'meetings.new',
12
12
  'events.new',
13
13
  'events.starting_soon',
14
+ 'notes.new',
14
15
  ];
15
16
  const _arrayCoversType = true;
16
17
  void _arrayCoversType;
@@ -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.UNKNOWN_SENDER = 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;
9
+ exports.PUSH_MESSAGE_SOURCES = exports.NOTE_REF_TYPES = exports.isValidStreamFrame = exports.UNKNOWN_SENDER = 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
  *
@@ -340,7 +340,8 @@ function isValidStreamFrame(data) {
340
340
  ['completed', 'cancelled', 'error'].includes(frame.reason) &&
341
341
  isOptionalStringArray(frame.used_message_ids) &&
342
342
  isOptionalStringArray(frame.used_meeting_ids) &&
343
- isOptionalStringArray(frame.used_event_ids));
343
+ isOptionalStringArray(frame.used_event_ids) &&
344
+ isOptionalStringArray(frame.used_note_ids));
344
345
  case 'error':
345
346
  return typeof frame.error === 'string';
346
347
  case 'state':
@@ -349,7 +350,8 @@ function isValidStreamFrame(data) {
349
350
  return (Array.isArray(frame.used_message_ids) &&
350
351
  frame.used_message_ids.every((id) => typeof id === 'string') &&
351
352
  isOptionalStringArray(frame.used_meeting_ids) &&
352
- isOptionalStringArray(frame.used_event_ids));
353
+ isOptionalStringArray(frame.used_event_ids) &&
354
+ isOptionalStringArray(frame.used_note_ids));
353
355
  case 'stream_start':
354
356
  return true;
355
357
  default:
@@ -363,3 +365,17 @@ function isOptionalStringArray(value) {
363
365
  return (Array.isArray(value) &&
364
366
  value.every((item) => typeof item === 'string'));
365
367
  }
368
+ /** Canonical list of valid note reference types — use for runtime validation. */
369
+ exports.NOTE_REF_TYPES = [
370
+ 'message',
371
+ 'meeting',
372
+ 'event',
373
+ 'entity',
374
+ ];
375
+ /** Canonical list of valid push message source types — use for runtime validation. */
376
+ exports.PUSH_MESSAGE_SOURCES = [
377
+ 'email',
378
+ 'chat',
379
+ 'alert',
380
+ 'custom',
381
+ ];
@@ -3,7 +3,7 @@
3
3
  * Utility exports
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.verifyWebhookSignatureDetailed = exports.verifyWebhookSignature = exports.generateMessageId = exports.StreamingClient = exports.HttpClient = void 0;
6
+ exports.validateRefPair = exports.verifyWebhookSignatureDetailed = exports.verifyWebhookSignature = exports.generateMessageId = exports.StreamingClient = exports.HttpClient = void 0;
7
7
  var fetch_js_1 = require("./fetch.js");
8
8
  Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return fetch_js_1.HttpClient; } });
9
9
  var streaming_js_1 = require("./streaming.js");
@@ -12,3 +12,5 @@ Object.defineProperty(exports, "generateMessageId", { enumerable: true, get: fun
12
12
  var webhooks_js_1 = require("./webhooks.js");
13
13
  Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return webhooks_js_1.verifyWebhookSignature; } });
14
14
  Object.defineProperty(exports, "verifyWebhookSignatureDetailed", { enumerable: true, get: function () { return webhooks_js_1.verifyWebhookSignatureDetailed; } });
15
+ var validate_ref_pair_js_1 = require("./validate-ref-pair.js");
16
+ Object.defineProperty(exports, "validateRefPair", { enumerable: true, get: function () { return validate_ref_pair_js_1.validateRefPair; } });
@@ -110,6 +110,7 @@ class StreamingClient {
110
110
  let usedMessageIds = [];
111
111
  let usedMeetingIds = [];
112
112
  let usedEventIds = [];
113
+ let usedNoteIds = [];
113
114
  let cancelled = false;
114
115
  let completed = false;
115
116
  // Connect to WebSocket
@@ -233,6 +234,9 @@ class StreamingClient {
233
234
  if (frame.used_event_ids !== undefined) {
234
235
  usedEventIds = frame.used_event_ids;
235
236
  }
237
+ if (frame.used_note_ids !== undefined) {
238
+ usedNoteIds = frame.used_note_ids;
239
+ }
236
240
  break;
237
241
  case "error": {
238
242
  const error = new index_js_2.AttroveError(frame.error, index_js_3.ErrorCodes.INTERNAL_ERROR);
@@ -252,6 +256,9 @@ class StreamingClient {
252
256
  if (frame.used_event_ids !== undefined) {
253
257
  usedEventIds = frame.used_event_ids;
254
258
  }
259
+ if (frame.used_note_ids !== undefined) {
260
+ usedNoteIds = frame.used_note_ids;
261
+ }
255
262
  onEnd?.(frame.reason);
256
263
  // Build updated history
257
264
  const updatedHistory = [
@@ -265,6 +272,7 @@ class StreamingClient {
265
272
  usedMessageIds,
266
273
  usedMeetingIds,
267
274
  usedEventIds,
275
+ usedNoteIds,
268
276
  cancelled,
269
277
  });
270
278
  break;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Shared ref_type / ref_id pairing validation used by NotesResource and PushResource.
4
+ * Ensures ref_type is a valid NoteRefType and that both fields are present or both absent.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.validateRefPair = void 0;
8
+ const index_js_1 = require("../errors/index.js");
9
+ const index_js_2 = require("../types/index.js");
10
+ /**
11
+ * Validate refType against NOTE_REF_TYPES and enforce the refType/refId pairing constraint.
12
+ * Both must be provided or both omitted.
13
+ */
14
+ function validateRefPair(refType, refId) {
15
+ if (refType != null) {
16
+ if (typeof refType !== 'string') {
17
+ throw new index_js_1.ValidationError(`refType must be a string, received ${typeof refType}`, index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'refType', received: typeof refType });
18
+ }
19
+ const valid = index_js_2.NOTE_REF_TYPES;
20
+ if (!valid.includes(refType)) {
21
+ throw new index_js_1.ValidationError(`refType must be one of: ${valid.join(', ')}`, index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'refType', received: refType });
22
+ }
23
+ }
24
+ if (refId != null) {
25
+ if (typeof refId !== 'string') {
26
+ throw new index_js_1.ValidationError(`refId must be a string, received ${typeof refId}`, index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'refId', received: typeof refId });
27
+ }
28
+ if (refId.trim() === '') {
29
+ throw new index_js_1.ValidationError('refId must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'refId' });
30
+ }
31
+ }
32
+ const hasRefType = refType != null;
33
+ const hasRefId = refId != null;
34
+ if (hasRefType !== hasRefId) {
35
+ throw new index_js_1.ValidationError('refType and refId must both be provided or both omitted', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: hasRefType ? 'refId' : 'refType' });
36
+ }
37
+ if (hasRefType) {
38
+ return {
39
+ refType: refType,
40
+ refId: refId,
41
+ };
42
+ }
43
+ return {};
44
+ }
45
+ exports.validateRefPair = validateRefPair;