@unboundcx/sdk 2.8.7 → 2.8.8

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/index.js CHANGED
@@ -25,6 +25,8 @@ import { RecordTypesService } from './services/recordTypes.js';
25
25
  import { GenerateIdService } from './services/generateId.js';
26
26
  import { EngagementMetricsService } from './services/engagementMetrics.js';
27
27
  import { TaskRouterService } from './services/taskRouter.js';
28
+ import { KnowledgeBaseService } from './services/knowledgeBase.js';
29
+ import { FaxService } from './services/fax.js';
28
30
 
29
31
  class UnboundSDK extends BaseSDK {
30
32
  constructor(options = {}) {
@@ -91,6 +93,8 @@ class UnboundSDK extends BaseSDK {
91
93
  this.generateId = new GenerateIdService(this);
92
94
  this.engagementMetrics = new EngagementMetricsService(this);
93
95
  this.taskRouter = new TaskRouterService(this);
96
+ this.knowledgeBase = new KnowledgeBaseService(this);
97
+ this.fax = new FaxService(this);
94
98
 
95
99
  // Add additional services that might be missing
96
100
  this._initializeAdditionalServices();
@@ -268,4 +272,6 @@ export { GenerateIdService } from './services/generateId.js';
268
272
  export { EngagementMetricsService } from './services/engagementMetrics.js';
269
273
  export { TaskRouterService } from './services/taskRouter.js';
270
274
  export { WorkerService } from './services/taskRouter/WorkerService.js';
275
+ export { KnowledgeBaseService } from './services/knowledgeBase.js';
276
+ export { FaxService } from './services/fax.js';
271
277
  export { BaseSDK } from './base.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "2.8.7",
3
+ "version": "2.8.8",
4
4
  "description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/services/ai.js CHANGED
@@ -20,39 +20,81 @@ export class GenerativeService {
20
20
  prompt,
21
21
  messages,
22
22
  systemPrompt,
23
+ appendSystemPrompt,
23
24
  relatedId,
25
+ peopleId,
26
+ companyId,
24
27
  provider,
25
28
  model,
26
29
  temperature,
30
+ maxTokens,
27
31
  subscriptionId,
28
32
  stream,
29
33
  isPlayground = false,
30
34
  responseFormat,
35
+ sessionId,
36
+ scope,
37
+ manageSession,
38
+ tools,
39
+ toolConfig,
40
+ maxToolRounds,
41
+ recordTypeId,
42
+ meta,
43
+ qualityCheck,
44
+ isPublic,
31
45
  }) {
32
46
  this.sdk.validateParams(
33
47
  {
34
48
  stream,
35
49
  temperature,
50
+ maxTokens,
36
51
  subscriptionId,
37
52
  provider,
38
53
  model,
39
54
  prompt,
40
55
  messages,
41
56
  systemPrompt,
57
+ appendSystemPrompt,
42
58
  isPlayground,
43
59
  responseFormat,
60
+ sessionId,
61
+ scope,
62
+ manageSession,
63
+ tools,
64
+ toolConfig,
65
+ maxToolRounds,
66
+ recordTypeId,
67
+ peopleId,
68
+ companyId,
69
+ meta,
70
+ qualityCheck,
71
+ isPublic,
44
72
  },
45
73
  {
46
74
  prompt: { type: 'string', required: false },
47
75
  messages: { type: 'array', required: false },
48
76
  systemPrompt: { type: 'string', required: false },
77
+ appendSystemPrompt: { type: 'string', required: false },
49
78
  provider: { type: 'string', required: false },
50
79
  model: { type: 'string', required: false },
51
80
  temperature: { type: 'number', required: false },
81
+ maxTokens: { type: 'number', required: false },
52
82
  subscriptionId: { type: 'string', required: false },
53
83
  stream: { type: 'boolean', required: false },
54
84
  isPlayground: { type: 'boolean', required: false },
55
85
  responseFormat: { type: 'object', required: false },
86
+ sessionId: { type: 'string', required: false },
87
+ scope: { type: 'string', required: false },
88
+ manageSession: { type: 'boolean', required: false },
89
+ tools: { type: 'array', required: false },
90
+ toolConfig: { type: 'object', required: false },
91
+ maxToolRounds: { type: 'number', required: false },
92
+ recordTypeId: { type: 'string', required: false },
93
+ peopleId: { type: 'string', required: false },
94
+ companyId: { type: 'string', required: false },
95
+ meta: { type: 'object', required: false },
96
+ qualityCheck: { type: 'boolean', required: false },
97
+ isPublic: { type: 'boolean', required: false },
56
98
  },
57
99
  );
58
100
 
@@ -62,20 +104,44 @@ export class GenerativeService {
62
104
  prompt,
63
105
  messages,
64
106
  systemPrompt,
107
+ appendSystemPrompt,
65
108
  relatedId,
109
+ peopleId,
110
+ companyId,
66
111
  provider,
67
112
  model,
68
113
  temperature,
114
+ maxTokens,
69
115
  subscriptionId,
70
116
  stream,
71
117
  responseFormat,
118
+ sessionId,
119
+ scope,
120
+ manageSession,
121
+ tools,
122
+ toolConfig,
123
+ maxToolRounds,
124
+ recordTypeId,
125
+ meta,
126
+ qualityCheck,
127
+ isPublic,
72
128
  },
73
129
  // Return raw response for streaming to allow client-side stream handling
74
130
  returnRawResponse: stream === true,
75
131
  };
76
132
 
77
- // Force HTTP transport when streaming is enabled since NATS doesn't support streaming responses
78
- const forceFetch = stream === true;
133
+ // Force HTTP transport when streaming or when messages contain large content (too large for NATS)
134
+ const hasLargeContent = messages?.some(
135
+ (m) =>
136
+ Array.isArray(m.content) &&
137
+ m.content.some(
138
+ (c) =>
139
+ c.type === 'image' ||
140
+ c.type === 'image_url' ||
141
+ c.type === 'document',
142
+ ),
143
+ );
144
+ const forceFetch = stream === true || hasLargeContent;
79
145
  const result = await this.sdk._fetch(
80
146
  '/ai/generative/chat',
81
147
  'POST',
@@ -85,6 +151,180 @@ export class GenerativeService {
85
151
  return result;
86
152
  }
87
153
 
154
+ /**
155
+ * Clone an existing chat session into a new one, importing conversation context.
156
+ * Filters out system prompts and tool calls, keeps user/assistant turns.
157
+ * If the transcript exceeds ~50k tokens it is summarized automatically.
158
+ *
159
+ * @param {Object} options
160
+ * @param {string} options.sourceSessionId - Session to clone from (required)
161
+ * @param {string} [options.systemPrompt] - New system prompt (appended after conversation context)
162
+ * @param {string} [options.provider] - LLM provider (falls back to source session)
163
+ * @param {string} [options.model] - Model ID (falls back to source session)
164
+ * @param {Array} [options.tools] - Tool names (falls back to source session)
165
+ * @param {Object} [options.toolConfig] - Tool config (falls back to source session)
166
+ * @param {string} [options.scope] - 'user' or 'account'
167
+ * @param {string} [options.userId] - Owner user ID
168
+ * @param {string} [options.peopleId] - Linked people record
169
+ * @param {string} [options.companyId] - Linked company record
170
+ * @param {string} [options.relatedId] - Linked related record
171
+ * @returns {Promise<Object>} { id, sourceSessionId, wasSummarized }
172
+ */
173
+ async cloneSession({
174
+ sourceSessionId,
175
+ systemPrompt,
176
+ provider,
177
+ model,
178
+ tools,
179
+ toolConfig,
180
+ scope,
181
+ userId,
182
+ peopleId,
183
+ companyId,
184
+ relatedId,
185
+ }) {
186
+ this.sdk.validateParams(
187
+ { sourceSessionId },
188
+ {
189
+ sourceSessionId: { type: 'string', required: true },
190
+ systemPrompt: { type: 'string', required: false },
191
+ provider: { type: 'string', required: false },
192
+ model: { type: 'string', required: false },
193
+ tools: { type: 'array', required: false },
194
+ toolConfig: { type: 'object', required: false },
195
+ scope: { type: 'string', required: false },
196
+ userId: { type: 'string', required: false },
197
+ peopleId: { type: 'string', required: false },
198
+ companyId: { type: 'string', required: false },
199
+ relatedId: { type: 'string', required: false },
200
+ },
201
+ );
202
+
203
+ const params = {
204
+ body: {
205
+ sourceSessionId,
206
+ systemPrompt,
207
+ provider,
208
+ model,
209
+ tools,
210
+ toolConfig,
211
+ scope,
212
+ userId,
213
+ peopleId,
214
+ companyId,
215
+ relatedId,
216
+ },
217
+ };
218
+
219
+ const result = await this.sdk._fetch(
220
+ '/ai/generative/chat/clone',
221
+ 'POST',
222
+ params,
223
+ );
224
+ return result;
225
+ }
226
+
227
+ /**
228
+ * List available chat tools and their metadata
229
+ * @returns {Promise<Object>} { tools: Array<{ name, label, description, configRequirements }>, count: number }
230
+ */
231
+ async listTools() {
232
+ return await this.sdk._fetch('/ai/generative/tools', 'GET');
233
+ }
234
+
235
+ /**
236
+ * List chat sessions visible to the current user
237
+ * @param {Object} [options] - Filter options
238
+ * @param {string} [options.relatedId] - Filter by related object ID
239
+ * @param {string} [options.scope] - Filter by scope ('user' or 'account')
240
+ * @param {number} [options.limit=25] - Results limit
241
+ * @param {number} [options.offset=0] - Pagination offset
242
+ * @returns {Promise<Object>} { sessions: Array }
243
+ */
244
+ async listSessions({ relatedId, scope, limit, offset } = {}) {
245
+ this.sdk.validateParams(
246
+ { relatedId, scope, limit, offset },
247
+ {
248
+ relatedId: { type: 'string', required: false },
249
+ scope: { type: 'string', required: false },
250
+ limit: { type: 'number', required: false },
251
+ offset: { type: 'number', required: false },
252
+ },
253
+ );
254
+
255
+ const params = { query: { relatedId, scope, limit, offset } };
256
+ const result = await this.sdk._fetch(
257
+ '/ai/generative/sessions',
258
+ 'GET',
259
+ params,
260
+ );
261
+ return result;
262
+ }
263
+
264
+ /**
265
+ * Get a chat session with its message history
266
+ * @param {Object} options
267
+ * @param {string} options.sessionId - Session ID to retrieve
268
+ * @returns {Promise<Object>} { session: { id, name, scope, relatedId, provider, model, messages, messageCount, lastMessageAt, createdAt } }
269
+ */
270
+ async getSession({ sessionId }) {
271
+ this.sdk.validateParams(
272
+ { sessionId },
273
+ { sessionId: { type: 'string', required: true } },
274
+ );
275
+
276
+ const result = await this.sdk._fetch(
277
+ `/ai/generative/sessions/${sessionId}`,
278
+ 'GET',
279
+ );
280
+ return result;
281
+ }
282
+
283
+ /**
284
+ * Update a chat session (name, scope)
285
+ * @param {Object} options
286
+ * @param {string} options.sessionId - Session ID to update
287
+ * @param {string} [options.name] - New session name
288
+ * @param {string} [options.scope] - New scope ('user' or 'account')
289
+ * @returns {Promise<Object>} { success: true }
290
+ */
291
+ async updateSession({ sessionId, name, scope }) {
292
+ this.sdk.validateParams(
293
+ { sessionId, name, scope },
294
+ {
295
+ sessionId: { type: 'string', required: true },
296
+ name: { type: 'string', required: false },
297
+ scope: { type: 'string', required: false },
298
+ },
299
+ );
300
+
301
+ const result = await this.sdk._fetch(
302
+ `/ai/generative/sessions/${sessionId}`,
303
+ 'PUT',
304
+ { body: { name, scope } },
305
+ );
306
+ return result;
307
+ }
308
+
309
+ /**
310
+ * Delete a chat session
311
+ * @param {Object} options
312
+ * @param {string} options.sessionId - Session ID to delete
313
+ * @returns {Promise<Object>} { success: true }
314
+ */
315
+ async deleteSession({ sessionId }) {
316
+ this.sdk.validateParams(
317
+ { sessionId },
318
+ { sessionId: { type: 'string', required: true } },
319
+ );
320
+
321
+ const result = await this.sdk._fetch(
322
+ `/ai/generative/sessions/${sessionId}`,
323
+ 'DELETE',
324
+ );
325
+ return result;
326
+ }
327
+
88
328
  async playbook({
89
329
  prompt,
90
330
  messages,
@@ -122,4 +122,49 @@ export class ExternalOAuthService {
122
122
  const result = await this.sdk._fetch('/externalOAuth', 'GET');
123
123
  return result;
124
124
  }
125
+
126
+ async listUnified() {
127
+ const result = await this.sdk._fetch('/externalOAuth/unified', 'GET');
128
+ return result;
129
+ }
130
+
131
+ async providers() {
132
+ const result = await this.sdk._fetch('/externalOAuth/providers', 'GET');
133
+ return result;
134
+ }
135
+
136
+ async authorize({
137
+ name,
138
+ provider,
139
+ clientId,
140
+ clientSecret,
141
+ scopes,
142
+ authorizationUrl,
143
+ tokenUrl,
144
+ }) {
145
+ this.sdk.validateParams(
146
+ { name, provider },
147
+ {
148
+ name: { type: 'string', required: true },
149
+ provider: { type: 'string', required: true },
150
+ clientId: { type: 'string', required: false },
151
+ clientSecret: { type: 'string', required: false },
152
+ scopes: { type: 'array', required: false },
153
+ authorizationUrl: { type: 'string', required: false },
154
+ tokenUrl: { type: 'string', required: false },
155
+ },
156
+ );
157
+
158
+ const body = { name, provider };
159
+ if (clientId) body.clientId = clientId;
160
+ if (clientSecret) body.clientSecret = clientSecret;
161
+ if (scopes) body.scopes = scopes;
162
+ if (authorizationUrl) body.authorizationUrl = authorizationUrl;
163
+ if (tokenUrl) body.tokenUrl = tokenUrl;
164
+
165
+ const result = await this.sdk._fetch('/externalOAuth/authorize', 'POST', {
166
+ body,
167
+ });
168
+ return result;
169
+ }
125
170
  }
@@ -0,0 +1,249 @@
1
+ export class FaxService {
2
+ constructor(sdk) {
3
+ this.sdk = sdk;
4
+ }
5
+
6
+ /**
7
+ * Create an inbound fax document record with status 'receiving'.
8
+ * Called by the media manager when an inbound fax is first detected.
9
+ * After the fax is fully received, call sdk.fax.status() to update
10
+ * with the final storage IDs and completion metadata.
11
+ *
12
+ * @param {Object} options - Parameters
13
+ * @param {string} options.faxMailboxId - ID of the fax mailbox receiving the fax (required)
14
+ * @param {string} [options.sipCallId] - SIP call correlation ID
15
+ * @param {string} [options.name] - Display name for the fax document
16
+ * @param {string} [options.faxHeader] - TSI header string from the sender
17
+ * @param {string} [options.resolution] - Fax resolution (e.g. 'fine', 'standard')
18
+ * @param {string} [options.toNumber] - Destination number in E.164 format
19
+ * @param {string} [options.fromNumber] - Sender number in E.164 format
20
+ * @returns {Promise<Object>} Created fax document
21
+ * @returns {string} result.id - The fax document ID (use in subsequent status calls)
22
+ * @returns {string} result.status - 'receiving'
23
+ *
24
+ * @example
25
+ * const { id } = await sdk.fax.receive({
26
+ * faxMailboxId: '157abc123...',
27
+ * sipCallId: 'sip-call-uuid',
28
+ * name: 'Fax from +15551234567',
29
+ * toNumber: '+15559876543',
30
+ * fromNumber: '+15551234567',
31
+ * });
32
+ * // Save id for use with sdk.fax.status() after reception completes
33
+ */
34
+ async receive({
35
+ faxMailboxId,
36
+ sipCallId,
37
+ name,
38
+ faxHeader,
39
+ resolution,
40
+ toNumber,
41
+ fromNumber,
42
+ cId,
43
+ }) {
44
+ this.sdk.validateParams(
45
+ { faxMailboxId },
46
+ {
47
+ faxMailboxId: { type: 'string', required: true },
48
+ },
49
+ );
50
+
51
+ const params = {
52
+ body: {
53
+ faxMailboxId,
54
+ sipCallId,
55
+ name,
56
+ faxHeader,
57
+ resolution,
58
+ toNumber,
59
+ fromNumber,
60
+ cId,
61
+ },
62
+ };
63
+
64
+ return await this.sdk._fetch('/fax/receive', 'POST', params);
65
+ }
66
+
67
+ /**
68
+ * Send an outbound fax.
69
+ * Creates a fax document record, retrieves the document from storage,
70
+ * and publishes to the media manager via NATS to initiate the fax call.
71
+ *
72
+ * @param {Object} options - Parameters
73
+ * @param {string} options.faxMailboxId - ID of the fax mailbox to send from (required)
74
+ * @param {string} options.toNumber - Destination number in E.164 format (required)
75
+ * @param {string} options.fromNumber - Caller ID number in E.164 format (required)
76
+ * @param {string} [options.storageId] - Storage ID of a PDF or TIFF file to fax. The server will
77
+ * automatically convert it to the missing format so both PDF and TIFF are stored.
78
+ * Required if pdfStorageId and tiffStorageId are not provided.
79
+ * @param {string} [options.pdfStorageId] - Storage ID of the PDF version. Required if storageId is not provided.
80
+ * @param {string} [options.tiffStorageId] - Storage ID of the TIFF version. Required if storageId is not provided.
81
+ * @param {string} [options.faxHeader] - TSI header text (defaults to mailbox faxHeader)
82
+ * @param {string} [options.resolution] - Fax resolution (defaults to mailbox resolution)
83
+ * @param {boolean} [options.ecm] - Enable Error Correction Mode (default: true)
84
+ * @param {number} [options.timeout] - Dial timeout in seconds (defaults to mailbox dialTimeout)
85
+ * @returns {Promise<Object>} Send result
86
+ * @returns {string} result.id - The fax document ID
87
+ * @returns {string} result.status - 'sending' on success, 'failed' on NATS error
88
+ * @returns {string} [result.requestId] - NATS request ID (on success)
89
+ * @returns {string} [result.error] - Error message (on failure)
90
+ *
91
+ * @example
92
+ * // Send using a single storageId (PDF or TIFF — server auto-converts the missing format)
93
+ * const result = await sdk.fax.send({
94
+ * faxMailboxId: '157abc123...',
95
+ * toNumber: '+15551234567',
96
+ * fromNumber: '+15559876543',
97
+ * storageId: '017xyz788...',
98
+ * });
99
+ * console.log(result.id); // '158def456...'
100
+ * console.log(result.status); // 'sending'
101
+ *
102
+ * @example
103
+ * // Send using explicit PDF and TIFF storage IDs
104
+ * const result = await sdk.fax.send({
105
+ * faxMailboxId: '157abc123...',
106
+ * toNumber: '+15551234567',
107
+ * fromNumber: '+15559876543',
108
+ * pdfStorageId: '017xyz789...',
109
+ * tiffStorageId: '017xyz788...',
110
+ * faxHeader: 'My Company',
111
+ * resolution: 'fine',
112
+ * ecm: true,
113
+ * timeout: 90,
114
+ * });
115
+ */
116
+ async send({
117
+ faxMailboxId,
118
+ toNumber,
119
+ fromNumber,
120
+ storageId,
121
+ pdfStorageId,
122
+ tiffStorageId,
123
+ faxHeader,
124
+ resolution,
125
+ ecm,
126
+ timeout,
127
+ }) {
128
+ this.sdk.validateParams(
129
+ { faxMailboxId, toNumber, fromNumber },
130
+ {
131
+ faxMailboxId: { type: 'string', required: true },
132
+ toNumber: { type: 'string', required: true },
133
+ fromNumber: { type: 'string', required: true },
134
+ storageId: { type: 'string', required: false },
135
+ pdfStorageId: { type: 'string', required: false },
136
+ tiffStorageId: { type: 'string', required: false },
137
+ },
138
+ );
139
+
140
+ if (!storageId && (!pdfStorageId || !tiffStorageId)) {
141
+ throw new Error(
142
+ 'Either storageId or both pdfStorageId and tiffStorageId are required.',
143
+ );
144
+ }
145
+
146
+ const params = {
147
+ body: {
148
+ faxMailboxId,
149
+ toNumber,
150
+ fromNumber,
151
+ storageId,
152
+ pdfStorageId,
153
+ tiffStorageId,
154
+ faxHeader,
155
+ resolution,
156
+ ecm,
157
+ timeout,
158
+ },
159
+ };
160
+
161
+ return await this.sdk._fetch('/fax/send', 'POST', params);
162
+ }
163
+
164
+ /**
165
+ * Update the status and metadata of a fax document.
166
+ * Called by the media manager to report progress and completion
167
+ * of both inbound and outbound faxes.
168
+ *
169
+ * @param {Object} options - Parameters
170
+ * @param {string} options.faxDocumentId - ID of the fax document to update (required)
171
+ * @param {string} options.status - New status (required): 'receiving', 'sending', 'sent', 'completed', 'failed'
172
+ * @param {string} [options.sipCallId] - SIP call correlation ID
173
+ * @param {number} [options.pages] - Number of pages transmitted/received
174
+ * @param {number} [options.duration] - Call duration in seconds
175
+ * @param {number} [options.transferRate] - Baud rate (e.g. 14400)
176
+ * @param {number} [options.ecmUsed] - Whether ECM was used (1 or 0)
177
+ * @param {number} [options.isError] - Whether an error occurred (1 or 0)
178
+ * @param {string} [options.errorMessage] - Error description
179
+ * @param {number} [options.sendAttempts] - Number of send attempts made
180
+ * @param {string} [options.pdfStorageId] - Storage ID of the PDF version
181
+ * @param {string} [options.tiffStorageId] - Storage ID of the TIFF version
182
+ * @returns {Promise<Object>} Updated fields
183
+ * @returns {string} result.id - The fax document ID
184
+ *
185
+ * @example
186
+ * // Complete an inbound fax with storage files and metadata
187
+ * await sdk.fax.status({
188
+ * faxDocumentId: '158abc123...',
189
+ * status: 'completed',
190
+ * pdfStorageId: '017pdf456...',
191
+ * tiffStorageId: '017tiff789...',
192
+ * pages: 3,
193
+ * duration: 45,
194
+ * transferRate: 14400,
195
+ * ecmUsed: 1,
196
+ * });
197
+ *
198
+ * @example
199
+ * // Report a fax failure
200
+ * await sdk.fax.status({
201
+ * faxDocumentId: '158abc123...',
202
+ * status: 'failed',
203
+ * isError: 1,
204
+ * errorMessage: 'Remote side disconnected',
205
+ * sendAttempts: 2,
206
+ * });
207
+ */
208
+ async status({
209
+ faxDocumentId,
210
+ status,
211
+ sipCallId,
212
+ pages,
213
+ duration,
214
+ transferRate,
215
+ ecmUsed,
216
+ isError,
217
+ errorMessage,
218
+ sendAttempts,
219
+ pdfStorageId,
220
+ tiffStorageId,
221
+ }) {
222
+ this.sdk.validateParams(
223
+ { faxDocumentId, status },
224
+ {
225
+ faxDocumentId: { type: 'string', required: true },
226
+ status: { type: 'string', required: true },
227
+ },
228
+ );
229
+
230
+ const params = {
231
+ body: {
232
+ faxDocumentId,
233
+ status,
234
+ sipCallId,
235
+ pages,
236
+ duration,
237
+ transferRate,
238
+ ecmUsed,
239
+ isError,
240
+ errorMessage,
241
+ sendAttempts,
242
+ pdfStorageId,
243
+ tiffStorageId,
244
+ },
245
+ };
246
+
247
+ return await this.sdk._fetch('/fax/status', 'POST', params);
248
+ }
249
+ }