@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.
- package/cjs/client.js +4 -0
- package/cjs/constants.js +1 -1
- package/cjs/resources/index.js +5 -1
- package/cjs/resources/messages.js +3 -0
- package/cjs/resources/notes.js +106 -0
- package/cjs/resources/push.js +315 -0
- package/cjs/resources/query.js +6 -2
- package/cjs/resources/webhooks.js +1 -0
- package/cjs/types/index.js +19 -3
- package/cjs/utils/index.js +3 -1
- package/cjs/utils/streaming.js +8 -0
- package/cjs/utils/validate-ref-pair.js +45 -0
- package/esm/client.d.ts +12 -0
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js +4 -0
- package/esm/client.js.map +1 -1
- package/esm/constants.d.ts +1 -1
- package/esm/constants.d.ts.map +1 -1
- package/esm/constants.js +1 -1
- package/esm/constants.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/resources/index.d.ts +3 -0
- package/esm/resources/index.d.ts.map +1 -1
- package/esm/resources/index.js +2 -0
- package/esm/resources/index.js.map +1 -1
- package/esm/resources/messages.d.ts.map +1 -1
- package/esm/resources/messages.js +3 -0
- package/esm/resources/messages.js.map +1 -1
- package/esm/resources/notes.d.ts +80 -0
- package/esm/resources/notes.d.ts.map +1 -0
- package/esm/resources/notes.js +103 -0
- package/esm/resources/notes.js.map +1 -0
- package/esm/resources/push.d.ts +140 -0
- package/esm/resources/push.d.ts.map +1 -0
- package/esm/resources/push.js +312 -0
- package/esm/resources/push.js.map +1 -0
- package/esm/resources/query.d.ts.map +1 -1
- package/esm/resources/query.js +6 -2
- package/esm/resources/query.js.map +1 -1
- package/esm/resources/webhooks.d.ts +1 -1
- package/esm/resources/webhooks.d.ts.map +1 -1
- package/esm/resources/webhooks.js +1 -0
- package/esm/resources/webhooks.js.map +1 -1
- package/esm/types/index.d.ts +202 -0
- package/esm/types/index.d.ts.map +1 -1
- package/esm/types/index.js +18 -2
- package/esm/types/index.js.map +1 -1
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +1 -0
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/streaming.d.ts +4 -0
- package/esm/utils/streaming.d.ts.map +1 -1
- package/esm/utils/streaming.js +8 -0
- package/esm/utils/streaming.js.map +1 -1
- package/esm/utils/validate-ref-pair.d.ts +23 -0
- package/esm/utils/validate-ref-pair.d.ts.map +1 -0
- package/esm/utils/validate-ref-pair.js +42 -0
- package/esm/utils/validate-ref-pair.js.map +1 -0
- 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.
|
|
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
|
*/
|
package/cjs/resources/index.js
CHANGED
|
@@ -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;
|
package/cjs/resources/query.js
CHANGED
|
@@ -214,7 +214,7 @@ class QueryResource {
|
|
|
214
214
|
body.expand = 'sources';
|
|
215
215
|
}
|
|
216
216
|
const config = options.idempotencyKey
|
|
217
|
-
? { headers: {
|
|
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: {
|
|
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);
|
package/cjs/types/index.js
CHANGED
|
@@ -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
|
+
];
|
package/cjs/utils/index.js
CHANGED
|
@@ -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; } });
|
package/cjs/utils/streaming.js
CHANGED
|
@@ -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;
|