@gradientlabs/client 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,1173 @@
1
+ import { createHmac, timingSafeEqual } from 'crypto';
2
+
3
+ // src/errors.ts
4
+ var ErrorCode = {
5
+ NotFound: "not_found",
6
+ Unauthenticated: "unauthenticated",
7
+ PermissionDenied: "permission_denied",
8
+ InvalidArgument: "invalid_argument",
9
+ FailedPrecondition: "failed_precondition",
10
+ ResourceExhausted: "resource_exhausted",
11
+ AlreadyExists: "already_exists",
12
+ Unavailable: "unavailable",
13
+ DeadlineExceeded: "deadline_exceeded",
14
+ Internal: "internal"
15
+ };
16
+ var GradientLabsError = class extends Error {
17
+ constructor(message, options) {
18
+ super(message, options);
19
+ this.name = "GradientLabsError";
20
+ }
21
+ };
22
+ var ConfigurationError = class extends GradientLabsError {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "ConfigurationError";
26
+ }
27
+ };
28
+ var ApiError = class extends GradientLabsError {
29
+ /** HTTP status code of the response. */
30
+ statusCode;
31
+ /** Machine-readable error code from the response envelope. */
32
+ code;
33
+ /** Arbitrary structured details returned with the error. */
34
+ details;
35
+ constructor(args) {
36
+ super(args.message || `unexpected response status: ${args.statusCode}`);
37
+ this.name = "ApiError";
38
+ this.statusCode = args.statusCode;
39
+ this.code = args.code;
40
+ this.details = args.details ?? {};
41
+ }
42
+ /**
43
+ * The identifier that can be given to Gradient Labs technical support to
44
+ * investigate an error, if present in the error details.
45
+ */
46
+ get traceId() {
47
+ const traceId = this.details["trace_id"];
48
+ return typeof traceId === "string" ? traceId : void 0;
49
+ }
50
+ };
51
+
52
+ // src/internal/version.ts
53
+ var VERSION = "0.1.0";
54
+
55
+ // src/internal/user-agent.ts
56
+ function userAgent() {
57
+ const runtimeVersion = typeof process !== "undefined" && process.version ? process.version : "unknown";
58
+ return `Gradient-Labs-Node/${VERSION} (node/${runtimeVersion})`;
59
+ }
60
+
61
+ // src/internal/http.ts
62
+ var HttpClient = class {
63
+ constructor(config) {
64
+ this.config = config;
65
+ }
66
+ config;
67
+ async request(method, path, options = {}) {
68
+ const url = this.buildUrl(path, options.query);
69
+ const headers = {
70
+ Authorization: `Bearer ${this.config.apiKey}`,
71
+ Accept: "application/json",
72
+ "User-Agent": userAgent()
73
+ };
74
+ let body;
75
+ if (options.body !== void 0) {
76
+ body = JSON.stringify(options.body);
77
+ headers["Content-Type"] = "application/json";
78
+ }
79
+ const signal = this.buildSignal(options.signal);
80
+ let response;
81
+ try {
82
+ response = await this.config.fetch(url, { method, headers, body, signal });
83
+ } catch (cause) {
84
+ throw new GradientLabsError(`request to ${method} ${path} failed: ${errorMessage(cause)}`, {
85
+ cause
86
+ });
87
+ }
88
+ const rawBody = await response.text();
89
+ if (response.status < 200 || response.status > 299) {
90
+ throw toApiError(response.status, rawBody);
91
+ }
92
+ if (!rawBody) {
93
+ return void 0;
94
+ }
95
+ try {
96
+ return JSON.parse(rawBody);
97
+ } catch (cause) {
98
+ throw new GradientLabsError(`failed to parse response body: ${errorMessage(cause)}`, {
99
+ cause
100
+ });
101
+ }
102
+ }
103
+ buildUrl(path, query) {
104
+ const base = this.config.baseUrl.replace(/\/+$/, "");
105
+ const cleanPath = path.replace(/^\/+/, "");
106
+ const url = new URL(`${base}/${cleanPath}`);
107
+ if (query) {
108
+ for (const [key, value] of Object.entries(query)) {
109
+ if (value !== void 0 && value !== null) {
110
+ url.searchParams.set(key, String(value));
111
+ }
112
+ }
113
+ }
114
+ return url.toString();
115
+ }
116
+ buildSignal(callerSignal) {
117
+ const { timeoutMs } = this.config;
118
+ if (timeoutMs === void 0) {
119
+ return callerSignal;
120
+ }
121
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
122
+ if (!callerSignal) {
123
+ return timeoutSignal;
124
+ }
125
+ if (typeof AbortSignal.any === "function") {
126
+ return AbortSignal.any([callerSignal, timeoutSignal]);
127
+ }
128
+ return callerSignal;
129
+ }
130
+ };
131
+ function toApiError(statusCode, rawBody) {
132
+ let code = "";
133
+ let message = "";
134
+ let details = {};
135
+ if (rawBody) {
136
+ try {
137
+ const parsed = JSON.parse(rawBody);
138
+ code = parsed.code ?? "";
139
+ message = parsed.message ?? "";
140
+ details = parsed.details ?? {};
141
+ } catch {
142
+ }
143
+ }
144
+ return new ApiError({ statusCode, code, message, details });
145
+ }
146
+ function errorMessage(cause) {
147
+ return cause instanceof Error ? cause.message : String(cause);
148
+ }
149
+
150
+ // src/resources/articles.ts
151
+ var Articles = class {
152
+ constructor(http) {
153
+ this.http = http;
154
+ }
155
+ http;
156
+ /** Creates or updates a help article. */
157
+ upsert(params, config = {}) {
158
+ return this.http.request("POST", "articles", { body: params, signal: config.signal });
159
+ }
160
+ /** Updates whether the AI agent can use an article or not. */
161
+ setUsageStatus(id, params, config = {}) {
162
+ return this.http.request("POST", `articles/${encodeURIComponent(id)}/usage-status`, {
163
+ body: params,
164
+ signal: config.signal
165
+ });
166
+ }
167
+ /** Marks an article as deleted. */
168
+ delete(id, config = {}) {
169
+ return this.http.request("DELETE", `articles/${encodeURIComponent(id)}`, {
170
+ signal: config.signal
171
+ });
172
+ }
173
+ };
174
+
175
+ // src/resources/back-office-tasks.ts
176
+ var BackOfficeTasks = class {
177
+ constructor(http) {
178
+ this.http = http;
179
+ }
180
+ http;
181
+ /** Creates a new back-office task. */
182
+ create(params, config = {}) {
183
+ return this.http.request("POST", "back-office-tasks", {
184
+ body: params,
185
+ signal: config.signal
186
+ });
187
+ }
188
+ /** Retrieves detailed information about a back-office task. */
189
+ get(id, config = {}) {
190
+ return this.http.request("GET", `back-office-tasks/${encodeURIComponent(id)}/read`, {
191
+ signal: config.signal
192
+ });
193
+ }
194
+ };
195
+
196
+ // src/resources/conversations.ts
197
+ var Conversations = class {
198
+ constructor(http) {
199
+ this.http = http;
200
+ }
201
+ http;
202
+ /** Begins a conversation. */
203
+ start(params, config = {}) {
204
+ return this.http.request("POST", "conversations", { body: params, signal: config.signal });
205
+ }
206
+ /** Retrieves a conversation, including the latest AI agent metadata. */
207
+ get(id, params = {}, config = {}) {
208
+ return this.http.request("GET", `conversations/${encodeURIComponent(id)}/read`, {
209
+ query: { support_platform: params.support_platform },
210
+ signal: config.signal
211
+ });
212
+ }
213
+ /**
214
+ * Retrieves a conversation.
215
+ *
216
+ * @deprecated Use {@link Conversations.get} instead, which reads from the
217
+ * canonical `/read` endpoint.
218
+ */
219
+ getDeprecated(id, config = {}) {
220
+ return this.http.request("GET", `conversations/${encodeURIComponent(id)}`, {
221
+ signal: config.signal
222
+ });
223
+ }
224
+ /** Adds a new message to an existing conversation. */
225
+ addMessage(id, params, config = {}) {
226
+ return this.http.request("POST", `conversations/${encodeURIComponent(id)}/messages`, {
227
+ body: params,
228
+ signal: config.signal
229
+ });
230
+ }
231
+ /** Transfers responsibility for handling a conversation to a participant. */
232
+ assign(id, params, config = {}) {
233
+ return this.http.request("PUT", `conversations/${encodeURIComponent(id)}/assignee`, {
234
+ body: params,
235
+ signal: config.signal
236
+ });
237
+ }
238
+ /** Logs an event against the conversation (e.g. typing, delivered, read). */
239
+ addEvent(id, params, config = {}) {
240
+ return this.http.request("POST", `conversations/${encodeURIComponent(id)}/events`, {
241
+ body: params,
242
+ signal: config.signal
243
+ });
244
+ }
245
+ /** Adds the result of a customer survey (e.g. CSAT) to a conversation. */
246
+ rate(id, params, config = {}) {
247
+ return this.http.request("PUT", `conversations/${encodeURIComponent(id)}/rate`, {
248
+ body: params,
249
+ signal: config.signal
250
+ });
251
+ }
252
+ /** Cancels a conversation (e.g. because a human has taken it over). */
253
+ cancel(id, params = {}, config = {}) {
254
+ return this.http.request("PUT", `conversations/${encodeURIComponent(id)}/cancel`, {
255
+ body: params,
256
+ signal: config.signal
257
+ });
258
+ }
259
+ /** Finishes a conversation that has reached a natural end state. */
260
+ finish(id, params = {}, config = {}) {
261
+ return this.http.request("PUT", `conversations/${encodeURIComponent(id)}/finish`, {
262
+ body: params,
263
+ signal: config.signal
264
+ });
265
+ }
266
+ /** Re-opens a conversation that was previously finished. */
267
+ resume(id, params, config = {}) {
268
+ return this.http.request("PUT", `conversations/${encodeURIComponent(id)}/resume`, {
269
+ body: params,
270
+ signal: config.signal
271
+ });
272
+ }
273
+ /** Returns the result of an async tool execution. */
274
+ returnAsyncToolResult(id, params, config = {}) {
275
+ return this.http.request(
276
+ "PUT",
277
+ `conversations/${encodeURIComponent(id)}/return-async-tool-result`,
278
+ { body: params, signal: config.signal }
279
+ );
280
+ }
281
+ };
282
+
283
+ // src/resources/hand-off-targets.ts
284
+ var HandOffTargets = class {
285
+ constructor(http) {
286
+ this.http = http;
287
+ }
288
+ http;
289
+ /** Lists the available hand-off targets. */
290
+ async list(config = {}) {
291
+ const rsp = await this.http.request("GET", "hand-off-targets", {
292
+ signal: config.signal
293
+ });
294
+ return rsp.targets;
295
+ }
296
+ /** Creates or updates a hand-off target. */
297
+ upsert(params, config = {}) {
298
+ return this.http.request("POST", "hand-off-targets", { body: params, signal: config.signal });
299
+ }
300
+ /** Deletes a hand-off target. */
301
+ delete(params, config = {}) {
302
+ return this.http.request("DELETE", "hand-off-targets", {
303
+ query: { id: params.id },
304
+ signal: config.signal
305
+ });
306
+ }
307
+ /** Gets the current default hand-off target for a channel. Returns "" if unset. */
308
+ async getDefault(params, config = {}) {
309
+ const rsp = await this.http.request("GET", "hand-off-targets/default", {
310
+ query: { channel: params.channel },
311
+ signal: config.signal
312
+ });
313
+ return rsp.id;
314
+ }
315
+ /** Sets the default hand-off target for a channel. */
316
+ setDefault(params, config = {}) {
317
+ return this.http.request("PUT", "hand-off-targets/default", {
318
+ body: params,
319
+ signal: config.signal
320
+ });
321
+ }
322
+ };
323
+
324
+ // src/resources/ip-addresses.ts
325
+ var IpAddressesResource = class {
326
+ constructor(http) {
327
+ this.http = http;
328
+ }
329
+ http;
330
+ /** Returns the list of IP addresses Gradient Labs uses, in CIDR format. */
331
+ list(config = {}) {
332
+ return this.http.request("GET", "ip-addresses", { signal: config.signal });
333
+ }
334
+ };
335
+
336
+ // src/resources/notes.ts
337
+ var Notes = class {
338
+ constructor(http) {
339
+ this.http = http;
340
+ }
341
+ http;
342
+ /** Creates a new note. */
343
+ create(params, config = {}) {
344
+ return this.http.request("POST", "notes", { body: params, signal: config.signal });
345
+ }
346
+ /** Updates an existing note's contents. */
347
+ update(id, params, config = {}) {
348
+ return this.http.request("POST", `notes/${encodeURIComponent(id)}`, {
349
+ body: params,
350
+ signal: config.signal
351
+ });
352
+ }
353
+ /** Updates a note's status. */
354
+ setStatus(id, params, config = {}) {
355
+ return this.http.request("POST", `notes/${encodeURIComponent(id)}/status`, {
356
+ body: params,
357
+ signal: config.signal
358
+ });
359
+ }
360
+ /** Marks a note as deleted. */
361
+ delete(id, config = {}) {
362
+ return this.http.request("DELETE", `notes/${encodeURIComponent(id)}`, {
363
+ signal: config.signal
364
+ });
365
+ }
366
+ };
367
+
368
+ // src/resources/outbound-conversations.ts
369
+ var OutboundConversations = class {
370
+ constructor(http) {
371
+ this.http = http;
372
+ }
373
+ http;
374
+ /** Creates and starts a new outbound conversation initiated by the AI agent. */
375
+ start(params, config = {}) {
376
+ return this.http.request("POST", "outbound/conversations", {
377
+ body: params,
378
+ signal: config.signal
379
+ });
380
+ }
381
+ };
382
+
383
+ // src/internal/pagination.ts
384
+ async function* paginate(fetchPage) {
385
+ let after;
386
+ do {
387
+ const page = await fetchPage(after);
388
+ for (const item of page.data) {
389
+ yield item;
390
+ }
391
+ after = page.pageInfo.next;
392
+ } while (after);
393
+ }
394
+
395
+ // src/resources/procedures.ts
396
+ var Procedures = class {
397
+ constructor(http) {
398
+ this.http = http;
399
+ }
400
+ http;
401
+ /** Retrieves one page of procedures. */
402
+ async list(params = {}, config = {}) {
403
+ const rsp = await this.http.request("GET", "procedures", {
404
+ query: { cursor: params.cursor, status: params.status },
405
+ signal: config.signal
406
+ });
407
+ return { data: rsp.procedures, pageInfo: rsp.pagination };
408
+ }
409
+ /** Iterates over all procedures, transparently following pagination cursors. */
410
+ listAll(params = {}, config = {}) {
411
+ return paginate((cursor) => this.list({ ...params, cursor }, config));
412
+ }
413
+ /** Retrieves a specific procedure by ID. */
414
+ get(id, config = {}) {
415
+ return this.http.request("GET", `procedure/${encodeURIComponent(id)}`, {
416
+ signal: config.signal
417
+ });
418
+ }
419
+ /** Configures daily usage limits for a procedure. Returns the updated procedure. */
420
+ async setLimit(id, params, config = {}) {
421
+ const rsp = await this.http.request(
422
+ "POST",
423
+ `procedure/${encodeURIComponent(id)}/limit`,
424
+ { body: params, signal: config.signal }
425
+ );
426
+ return rsp.procedure;
427
+ }
428
+ /** Lists the non-ephemeral versions of a procedure. */
429
+ async listVersions(id, config = {}) {
430
+ const rsp = await this.http.request(
431
+ "GET",
432
+ `procedures/${encodeURIComponent(id)}/versions`,
433
+ { signal: config.signal }
434
+ );
435
+ return rsp.Versions;
436
+ }
437
+ /** Promotes a procedure version to be the live (production) version. */
438
+ setLiveVersion(id, version, config = {}) {
439
+ return this.http.request(
440
+ "POST",
441
+ `procedures/${encodeURIComponent(id)}/versions/${version}/set-live`,
442
+ { signal: config.signal }
443
+ );
444
+ }
445
+ /** Removes a procedure version from being the live revision. */
446
+ unsetLiveVersion(id, version, config = {}) {
447
+ return this.http.request(
448
+ "POST",
449
+ `procedures/${encodeURIComponent(id)}/versions/${version}/unset-live`,
450
+ { signal: config.signal }
451
+ );
452
+ }
453
+ /** Marks a procedure version as gated for A/B testing. */
454
+ setGatedVersion(id, version, params, config = {}) {
455
+ return this.http.request(
456
+ "POST",
457
+ `procedures/${encodeURIComponent(id)}/versions/${version}/set-gated`,
458
+ { body: params, signal: config.signal }
459
+ );
460
+ }
461
+ /** Removes the gated marking from a procedure version. */
462
+ unsetGatedVersion(id, version, config = {}) {
463
+ return this.http.request(
464
+ "POST",
465
+ `procedures/${encodeURIComponent(id)}/versions/${version}/unset-gated`,
466
+ { signal: config.signal }
467
+ );
468
+ }
469
+ };
470
+
471
+ // src/resources/resource-sources.ts
472
+ var ResourceSources = class {
473
+ constructor(http) {
474
+ this.http = http;
475
+ }
476
+ http;
477
+ /** Lists all resource sources accessible to the company. */
478
+ async list(config = {}) {
479
+ const rsp = await this.http.request("GET", "resource-sources", {
480
+ signal: config.signal
481
+ });
482
+ return rsp.resource_sources;
483
+ }
484
+ /** Creates a new resource source. */
485
+ create(params, config = {}) {
486
+ return this.http.request("POST", "resource-sources", { body: params, signal: config.signal });
487
+ }
488
+ /** Retrieves a specific resource source by ID. */
489
+ get(id, config = {}) {
490
+ return this.http.request("GET", `resource-sources/${encodeURIComponent(id)}`, {
491
+ signal: config.signal
492
+ });
493
+ }
494
+ /** Updates an existing resource source. */
495
+ update(id, params, config = {}) {
496
+ return this.http.request("PUT", `resource-sources/${encodeURIComponent(id)}`, {
497
+ body: params,
498
+ signal: config.signal
499
+ });
500
+ }
501
+ /** Deletes a resource source. */
502
+ delete(id, config = {}) {
503
+ return this.http.request("DELETE", `resource-sources/${encodeURIComponent(id)}`, {
504
+ signal: config.signal
505
+ });
506
+ }
507
+ /** Modifies the source schema based on the provided payload examples. */
508
+ updateSchemaByExamples(id, params, config = {}) {
509
+ return this.http.request(
510
+ "POST",
511
+ `resource-sources/${encodeURIComponent(id)}/schema-by-examples`,
512
+ { body: params, signal: config.signal }
513
+ );
514
+ }
515
+ };
516
+
517
+ // src/resources/resource-types.ts
518
+ var ResourceTypes = class {
519
+ constructor(http) {
520
+ this.http = http;
521
+ }
522
+ http;
523
+ /** Lists all resource types accessible to the company. */
524
+ async list(config = {}) {
525
+ const rsp = await this.http.request("GET", "resource-types", {
526
+ signal: config.signal
527
+ });
528
+ return rsp.resource_types;
529
+ }
530
+ /** Creates a new resource type. */
531
+ create(params, config = {}) {
532
+ return this.http.request("POST", "resource-types", { body: params, signal: config.signal });
533
+ }
534
+ /** Retrieves a specific resource type by ID. */
535
+ get(id, config = {}) {
536
+ return this.http.request("GET", `resource-types/${encodeURIComponent(id)}`, {
537
+ signal: config.signal
538
+ });
539
+ }
540
+ /** Updates an existing resource type. */
541
+ update(id, params, config = {}) {
542
+ return this.http.request("PUT", `resource-types/${encodeURIComponent(id)}`, {
543
+ body: params,
544
+ signal: config.signal
545
+ });
546
+ }
547
+ /** Deletes a resource type. */
548
+ delete(id, config = {}) {
549
+ return this.http.request("DELETE", `resource-types/${encodeURIComponent(id)}`, {
550
+ signal: config.signal
551
+ });
552
+ }
553
+ };
554
+
555
+ // src/resources/secrets.ts
556
+ var Secrets = class {
557
+ constructor(http) {
558
+ this.http = http;
559
+ }
560
+ http;
561
+ /** Lists the company's configured secrets. */
562
+ async list(config = {}) {
563
+ const rsp = await this.http.request("GET", "secrets", {
564
+ signal: config.signal
565
+ });
566
+ return rsp.secrets;
567
+ }
568
+ /** Creates or updates a secret. */
569
+ write(name, params, config = {}) {
570
+ return this.http.request("PUT", `secrets/${encodeURIComponent(name)}`, {
571
+ body: params,
572
+ signal: config.signal
573
+ });
574
+ }
575
+ /** Revokes a secret so it can no longer be used. */
576
+ revoke(name, config = {}) {
577
+ return this.http.request("DELETE", `secrets/${encodeURIComponent(name)}`, {
578
+ signal: config.signal
579
+ });
580
+ }
581
+ };
582
+
583
+ // src/resources/terminology-substitutions.ts
584
+ var TerminologySubstitutions = class {
585
+ constructor(http) {
586
+ this.http = http;
587
+ }
588
+ http;
589
+ /** Returns all terminology substitutions configured for the organization. */
590
+ async list(config = {}) {
591
+ const rsp = await this.http.request("GET", "terminology-substitutions", {
592
+ signal: config.signal
593
+ });
594
+ return rsp.substitutions;
595
+ }
596
+ /** Creates a new terminology substitution. */
597
+ create(params, config = {}) {
598
+ return this.http.request("POST", "terminology-substitutions", {
599
+ body: params,
600
+ signal: config.signal
601
+ });
602
+ }
603
+ /** Returns a single terminology substitution by ID. */
604
+ get(id, config = {}) {
605
+ return this.http.request("GET", `terminology-substitutions/${encodeURIComponent(id)}`, {
606
+ signal: config.signal
607
+ });
608
+ }
609
+ /** Updates an existing terminology substitution. */
610
+ update(id, params, config = {}) {
611
+ return this.http.request("PUT", `terminology-substitutions/${encodeURIComponent(id)}`, {
612
+ body: params,
613
+ signal: config.signal
614
+ });
615
+ }
616
+ /** Deletes a terminology substitution by ID. */
617
+ delete(id, config = {}) {
618
+ return this.http.request("DELETE", `terminology-substitutions/${encodeURIComponent(id)}`, {
619
+ signal: config.signal
620
+ });
621
+ }
622
+ };
623
+
624
+ // src/resources/tools.ts
625
+ var Tools = class {
626
+ constructor(http) {
627
+ this.http = http;
628
+ }
629
+ http;
630
+ /** Returns all tools created by the company. */
631
+ async list(config = {}) {
632
+ const rsp = await this.http.request("GET", "tools", {
633
+ signal: config.signal
634
+ });
635
+ return rsp.tools;
636
+ }
637
+ /** Creates a new custom tool. */
638
+ create(params, config = {}) {
639
+ return this.http.request("POST", "tools", { body: params, signal: config.signal });
640
+ }
641
+ /** Reads a tool by ID and optional version. */
642
+ get(id, params = {}, config = {}) {
643
+ return this.http.request("GET", `tools/${encodeURIComponent(id)}`, {
644
+ query: { version: params.version },
645
+ signal: config.signal
646
+ });
647
+ }
648
+ /** Creates a new revision of a tool. The name and type cannot be changed. */
649
+ update(id, params, config = {}) {
650
+ return this.http.request("PUT", `tools/${encodeURIComponent(id)}`, {
651
+ body: params,
652
+ signal: config.signal
653
+ });
654
+ }
655
+ /** Deletes a tool. */
656
+ delete(id, config = {}) {
657
+ return this.http.request("DELETE", `tools/${encodeURIComponent(id)}`, {
658
+ signal: config.signal
659
+ });
660
+ }
661
+ /** Executes a tool, enabling you to test it without an actual conversation. */
662
+ execute(id, params, config = {}) {
663
+ return this.http.request("POST", `tools/${encodeURIComponent(id)}/execute`, {
664
+ body: { arguments: params.arguments, token: params.token ?? "" },
665
+ signal: config.signal
666
+ });
667
+ }
668
+ };
669
+
670
+ // src/resources/topics.ts
671
+ var Topics = class {
672
+ constructor(http) {
673
+ this.http = http;
674
+ }
675
+ http;
676
+ /** Lists the company's topics, optionally filtered by support platform. */
677
+ async list(params = {}, config = {}) {
678
+ const rsp = await this.http.request("GET", "topics", {
679
+ query: { support_platform: params.support_platform },
680
+ signal: config.signal
681
+ });
682
+ return rsp.Topics;
683
+ }
684
+ /** Reads a single article topic. */
685
+ get(id, params = {}, config = {}) {
686
+ return this.http.request("GET", `topic/${encodeURIComponent(id)}`, {
687
+ query: { support_platform: params.support_platform },
688
+ signal: config.signal
689
+ });
690
+ }
691
+ /** Creates or updates an article topic. */
692
+ upsert(params, config = {}) {
693
+ return this.http.request("POST", "topics", { body: params, signal: config.signal });
694
+ }
695
+ };
696
+
697
+ // src/resources/traffic-groups.ts
698
+ var TrafficGroups = class {
699
+ constructor(http) {
700
+ this.http = http;
701
+ }
702
+ http;
703
+ /** Lists all traffic groups for the company. */
704
+ async list(config = {}) {
705
+ const rsp = await this.http.request("GET", "traffic-groups", {
706
+ signal: config.signal
707
+ });
708
+ return rsp.traffic_groups;
709
+ }
710
+ /** Creates a new traffic group. */
711
+ create(params, config = {}) {
712
+ return this.http.request("POST", "traffic-groups", { body: params, signal: config.signal });
713
+ }
714
+ /** Updates an existing traffic group. */
715
+ update(id, params, config = {}) {
716
+ return this.http.request("PUT", `traffic-groups/${encodeURIComponent(id)}`, {
717
+ body: params,
718
+ signal: config.signal
719
+ });
720
+ }
721
+ /** Deletes a traffic group and all associated targets. */
722
+ delete(id, config = {}) {
723
+ return this.http.request("DELETE", `traffic-groups/${encodeURIComponent(id)}`, {
724
+ signal: config.signal
725
+ });
726
+ }
727
+ /** Adds a target to a traffic group. */
728
+ addTarget(id, params, config = {}) {
729
+ return this.http.request("POST", `traffic-groups/${encodeURIComponent(id)}/targets`, {
730
+ body: params,
731
+ signal: config.signal
732
+ });
733
+ }
734
+ /** Removes a target from a traffic group. */
735
+ removeTarget(id, targetId, config = {}) {
736
+ return this.http.request(
737
+ "DELETE",
738
+ `traffic-groups/${encodeURIComponent(id)}/targets/${encodeURIComponent(targetId)}`,
739
+ { signal: config.signal }
740
+ );
741
+ }
742
+ /** Excludes a target (e.g. a procedure) from a traffic group. */
743
+ addExclusion(id, params, config = {}) {
744
+ return this.http.request("POST", `traffic-groups/${encodeURIComponent(id)}/exclusions`, {
745
+ body: params,
746
+ signal: config.signal
747
+ });
748
+ }
749
+ /** Removes a target exclusion from a traffic group. */
750
+ removeExclusion(id, targetId, config = {}) {
751
+ return this.http.request(
752
+ "DELETE",
753
+ `traffic-groups/${encodeURIComponent(id)}/exclusions/${encodeURIComponent(targetId)}`,
754
+ { signal: config.signal }
755
+ );
756
+ }
757
+ };
758
+
759
+ // src/resources/voice.ts
760
+ var Voice = class {
761
+ constructor(http) {
762
+ this.http = http;
763
+ }
764
+ http;
765
+ /**
766
+ * Retrieves the most recent call context for a given phone number. Throws an
767
+ * {@link ApiError} with status 404 if there have been no recent call events.
768
+ */
769
+ getLatestCallContext(phoneNumber, params = {}, config = {}) {
770
+ return this.http.request(
771
+ "GET",
772
+ `voice/latest-call-context/${encodeURIComponent(phoneNumber)}`,
773
+ {
774
+ query: {
775
+ lookback_seconds: params.lookback_seconds,
776
+ include_large_fields: params.include_large_fields
777
+ },
778
+ signal: config.signal
779
+ }
780
+ );
781
+ }
782
+ };
783
+
784
+ // src/webhooks/events.ts
785
+ var WebhookType = {
786
+ AgentMessage: "agent.message",
787
+ ConversationHandOff: "conversation.hand_off",
788
+ ConversationFinished: "conversation.finished",
789
+ ActionExecute: "action.execute",
790
+ ResourcePull: "resource.pull",
791
+ BackOfficeTaskComplete: "back-office-task.complete",
792
+ BackOfficeTaskHandOff: "back-office-task.hand-off",
793
+ BackOfficeTaskFail: "back-office-task.fail"
794
+ };
795
+
796
+ // src/webhooks/verifier.ts
797
+ var SIGNATURE_HEADER = "x-gradientlabs-signature";
798
+ var TOKEN_HEADER = "x-gradientlabs-token";
799
+ var DEFAULT_LEEWAY_MS = 5 * 60 * 1e3;
800
+ var InvalidWebhookSignatureError = class extends GradientLabsError {
801
+ constructor(message = "webhook signature is invalid") {
802
+ super(message);
803
+ this.name = "InvalidWebhookSignatureError";
804
+ }
805
+ };
806
+ var UnknownWebhookTypeError = class extends GradientLabsError {
807
+ type;
808
+ constructor(type) {
809
+ super(`unknown webhook event type received: ${type}`);
810
+ this.name = "UnknownWebhookTypeError";
811
+ this.type = type;
812
+ }
813
+ };
814
+ var WebhookVerifier = class {
815
+ signingKey;
816
+ leewayMs;
817
+ now;
818
+ constructor(config) {
819
+ this.signingKey = config.signingKey;
820
+ this.leewayMs = config.leewayMs ?? DEFAULT_LEEWAY_MS;
821
+ this.now = config.now ?? Date.now;
822
+ }
823
+ /**
824
+ * Verifies a webhook's signature and timestamp. Throws
825
+ * {@link InvalidWebhookSignatureError} if verification fails.
826
+ */
827
+ verify(args) {
828
+ const body = toBuffer(args.body);
829
+ const { timestamp, signatures } = parseSignatureHeader(args.signature);
830
+ if (Math.abs(this.now() - timestamp * 1e3) > this.leewayMs) {
831
+ throw new InvalidWebhookSignatureError("webhook timestamp is outside the allowed leeway");
832
+ }
833
+ const expected = this.computeSignature(timestamp, body);
834
+ for (const candidate of signatures) {
835
+ if (constantTimeEqual(expected, candidate)) {
836
+ return;
837
+ }
838
+ }
839
+ throw new InvalidWebhookSignatureError();
840
+ }
841
+ /**
842
+ * Verifies a webhook request, then parses it into a typed event. Returns the
843
+ * event along with the optional `X-GradientLabs-Token` passthrough.
844
+ *
845
+ * Verification always happens before the event type is inspected.
846
+ */
847
+ parse(args) {
848
+ const signature = getHeader(args.headers, SIGNATURE_HEADER);
849
+ this.verify({ body: args.body, signature });
850
+ const text = typeof args.body === "string" ? args.body : Buffer.from(args.body).toString("utf8");
851
+ const payload = JSON.parse(text);
852
+ if (!isKnownType(payload.type)) {
853
+ throw new UnknownWebhookTypeError(payload.type);
854
+ }
855
+ const event = {
856
+ id: payload.id,
857
+ type: payload.type,
858
+ sequence_number: payload.sequence_number,
859
+ timestamp: payload.timestamp,
860
+ data: payload.data
861
+ };
862
+ const token = getHeader(args.headers, TOKEN_HEADER) ?? void 0;
863
+ return { event, token };
864
+ }
865
+ computeSignature(timestamp, body) {
866
+ return createHmac("sha256", this.signingKey).update(`${timestamp}.`).update(body).digest();
867
+ }
868
+ };
869
+ function isKnownType(type) {
870
+ return Object.values(WebhookType).includes(type);
871
+ }
872
+ function parseSignatureHeader(header) {
873
+ if (!header) {
874
+ throw new InvalidWebhookSignatureError("missing signature header");
875
+ }
876
+ let timestamp;
877
+ const signatures = [];
878
+ for (const pair of header.split(",")) {
879
+ const idx = pair.indexOf("=");
880
+ if (idx === -1) {
881
+ throw new InvalidWebhookSignatureError("malformed signature header");
882
+ }
883
+ const key = pair.slice(0, idx);
884
+ const value = pair.slice(idx + 1);
885
+ if (key === "t") {
886
+ const parsed = Number.parseInt(value, 10);
887
+ if (!Number.isFinite(parsed)) {
888
+ throw new InvalidWebhookSignatureError("invalid timestamp component");
889
+ }
890
+ timestamp = parsed;
891
+ } else if (key === "v1") {
892
+ try {
893
+ signatures.push(Buffer.from(value, "hex"));
894
+ } catch {
895
+ throw new InvalidWebhookSignatureError("invalid signature component");
896
+ }
897
+ }
898
+ }
899
+ if (timestamp === void 0) {
900
+ throw new InvalidWebhookSignatureError("signature header contains no timestamp component");
901
+ }
902
+ if (signatures.length === 0) {
903
+ throw new InvalidWebhookSignatureError("signature header contains no v1 signature");
904
+ }
905
+ return { timestamp, signatures };
906
+ }
907
+ function constantTimeEqual(a, b) {
908
+ if (a.length !== b.length) {
909
+ return false;
910
+ }
911
+ return timingSafeEqual(a, b);
912
+ }
913
+ function toBuffer(body) {
914
+ return typeof body === "string" ? Buffer.from(body, "utf8") : Buffer.from(body);
915
+ }
916
+ function getHeader(headers, name) {
917
+ if (typeof headers.get === "function") {
918
+ return headers.get(name);
919
+ }
920
+ const record = headers;
921
+ const direct = record[name];
922
+ const value = direct ?? findCaseInsensitive(record, name);
923
+ if (value === void 0) {
924
+ return null;
925
+ }
926
+ return Array.isArray(value) ? value[0] ?? null : value;
927
+ }
928
+ function findCaseInsensitive(record, name) {
929
+ const lower = name.toLowerCase();
930
+ for (const key of Object.keys(record)) {
931
+ if (key.toLowerCase() === lower) {
932
+ return record[key];
933
+ }
934
+ }
935
+ return void 0;
936
+ }
937
+
938
+ // src/client.ts
939
+ var DEFAULT_BASE_URL = "https://api.gradient-labs.ai";
940
+ var defaultFetch = (input, init) => fetch(input, init);
941
+ var GradientLabs = class {
942
+ // Integration role (publicapi)
943
+ conversations;
944
+ outboundConversations;
945
+ backOfficeTasks;
946
+ voice;
947
+ // Management role (publicmanagementapi)
948
+ tools;
949
+ articles;
950
+ topics;
951
+ procedures;
952
+ handOffTargets;
953
+ resourceSources;
954
+ resourceTypes;
955
+ secrets;
956
+ notes;
957
+ terminologySubstitutions;
958
+ trafficGroups;
959
+ ipAddresses;
960
+ webhookVerifier;
961
+ constructor(config) {
962
+ if (!config.apiKey) {
963
+ throw new ConfigurationError("apiKey is required");
964
+ }
965
+ const http = new HttpClient({
966
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
967
+ apiKey: config.apiKey,
968
+ fetch: config.fetch ?? defaultFetch,
969
+ timeoutMs: config.timeoutMs
970
+ });
971
+ this.conversations = new Conversations(http);
972
+ this.outboundConversations = new OutboundConversations(http);
973
+ this.backOfficeTasks = new BackOfficeTasks(http);
974
+ this.voice = new Voice(http);
975
+ this.tools = new Tools(http);
976
+ this.articles = new Articles(http);
977
+ this.topics = new Topics(http);
978
+ this.procedures = new Procedures(http);
979
+ this.handOffTargets = new HandOffTargets(http);
980
+ this.resourceSources = new ResourceSources(http);
981
+ this.resourceTypes = new ResourceTypes(http);
982
+ this.secrets = new Secrets(http);
983
+ this.notes = new Notes(http);
984
+ this.terminologySubstitutions = new TerminologySubstitutions(http);
985
+ this.trafficGroups = new TrafficGroups(http);
986
+ this.ipAddresses = new IpAddressesResource(http);
987
+ if (config.webhookSigningKey) {
988
+ this.webhookVerifier = new WebhookVerifier({
989
+ signingKey: config.webhookSigningKey,
990
+ leewayMs: config.webhookLeewayMs
991
+ });
992
+ }
993
+ }
994
+ /**
995
+ * The webhook verifier. Throws {@link ConfigurationError} if the client was
996
+ * created without a `webhookSigningKey`.
997
+ */
998
+ get webhooks() {
999
+ if (!this.webhookVerifier) {
1000
+ throw new ConfigurationError(
1001
+ "webhookSigningKey is required to verify webhooks; pass it to the GradientLabs constructor"
1002
+ );
1003
+ }
1004
+ return this.webhookVerifier;
1005
+ }
1006
+ };
1007
+
1008
+ // src/models/enums.ts
1009
+ var ArticleStatus = {
1010
+ Draft: "draft",
1011
+ Published: "published",
1012
+ Deleted: "deleted",
1013
+ Excluded: "excluded",
1014
+ Unknown: "unknown"
1015
+ };
1016
+ var ArticleUsageStatus = {
1017
+ On: "on",
1018
+ Off: "off"
1019
+ };
1020
+ var ArticleVisibility = {
1021
+ Public: "public",
1022
+ Users: "users",
1023
+ Internal: "internal",
1024
+ Unknown: "unknown"
1025
+ };
1026
+ var AttachmentType = {
1027
+ Image: "image",
1028
+ File: "file"
1029
+ };
1030
+ var Channel = {
1031
+ Web: "web",
1032
+ Email: "email",
1033
+ Voice: "voice",
1034
+ Unmapped: "unmapped"
1035
+ };
1036
+ var CustomerSource = {
1037
+ Dixa: "dixa",
1038
+ Intercom: "intercom",
1039
+ Freshchat: "freshchat",
1040
+ Freshdesk: "freshdesk",
1041
+ PublicApi: "public-api",
1042
+ ChatSdk: "chat-sdk",
1043
+ Salesforce: "salesforce",
1044
+ Zendesk: "zendesk",
1045
+ Livekit: "livekit",
1046
+ Twilio: "twilio",
1047
+ Talkdesk: "talkdesk",
1048
+ IntercomVoice: "intercom-voice",
1049
+ Livechat: "livechat",
1050
+ WebApp: "web-app",
1051
+ Gmail: "gmail",
1052
+ File: "file"
1053
+ };
1054
+ var ParticipantType = {
1055
+ Customer: "Customer",
1056
+ Agent: "Agent",
1057
+ AIAgent: "AI Agent",
1058
+ Bot: "Bot"
1059
+ };
1060
+ var ConversationEventType = {
1061
+ Assigned: "assigned",
1062
+ Cancelled: "cancelled",
1063
+ Finished: "finished",
1064
+ Resumed: "resumed",
1065
+ InternalNote: "internal-note",
1066
+ Message: "message",
1067
+ Delivered: "delivered",
1068
+ Read: "read",
1069
+ Rated: "rated",
1070
+ Started: "started",
1071
+ Typing: "typing",
1072
+ AsyncToolResult: "async-tool-result"
1073
+ };
1074
+ var ProcedureStatus = {
1075
+ Unsaved: "unsaved",
1076
+ Draft: "draft",
1077
+ Live: "live",
1078
+ Archived: "archived"
1079
+ };
1080
+ var NoteStatus = {
1081
+ Draft: "draft",
1082
+ Live: "live",
1083
+ Deleted: "deleted"
1084
+ };
1085
+ var BackOfficeTaskStatus = {
1086
+ Pending: "pending",
1087
+ InProgress: "in-progress",
1088
+ Completed: "completed",
1089
+ Failed: "failed",
1090
+ HandedOff: "handed-off"
1091
+ };
1092
+ var BackOfficeTaskResultType = {
1093
+ Custom: "custom"
1094
+ };
1095
+ var AttributeCardinality = {
1096
+ One: "one",
1097
+ Many: "many"
1098
+ };
1099
+ var AttributeType = {
1100
+ String: "string",
1101
+ Date: "date",
1102
+ Timestamp: "timestamp",
1103
+ Boolean: "boolean",
1104
+ Number: "number",
1105
+ Array: "array",
1106
+ Complex: "complex"
1107
+ };
1108
+ var ResourceSourceRefreshStrategy = {
1109
+ Dynamic: "dynamic",
1110
+ Static: "static"
1111
+ };
1112
+ var ResourceSourceScope = {
1113
+ Global: "global",
1114
+ Local: "local"
1115
+ };
1116
+ var ResourceSourceType = {
1117
+ Http: "http",
1118
+ Internal: "internal",
1119
+ Webhook: "webhook"
1120
+ };
1121
+ var SchemaUpdateStrategy = {
1122
+ Replace: "replace",
1123
+ Merge: "merge"
1124
+ };
1125
+ var ResourceTypeRefreshStrategy = {
1126
+ Dynamic: "dynamic",
1127
+ Static: "static"
1128
+ };
1129
+ var ResourceTypeScope = {
1130
+ Global: "global",
1131
+ Local: "local"
1132
+ };
1133
+ var SupportPlatform = {
1134
+ Dixa: "dixa",
1135
+ Freshchat: "freshchat",
1136
+ Freshdesk: "freshdesk",
1137
+ Gmail: "gmail",
1138
+ Intercom: "intercom",
1139
+ Livechat: "livechat",
1140
+ PublicApi: "public-api",
1141
+ ChatSdk: "chat-sdk",
1142
+ Salesforce: "salesforce",
1143
+ Zendesk: "zendesk",
1144
+ Livekit: "livekit",
1145
+ Twilio: "twilio",
1146
+ Talkdesk: "talkdesk",
1147
+ IntercomVoice: "intercom-voice",
1148
+ ConversationSynthesizor: "conversation-synthesizor",
1149
+ WebApp: "web-app"
1150
+ };
1151
+ var BodyEncoding = {
1152
+ Json: "application/json",
1153
+ Form: "application/x-www-form-urlencoded"
1154
+ };
1155
+ var ParameterSource = {
1156
+ Llm: "llm",
1157
+ Literal: "literal",
1158
+ Resource: "resource"
1159
+ };
1160
+ var ParameterType = {
1161
+ String: "string",
1162
+ StringArray: "string_array",
1163
+ Integer: "integer",
1164
+ Float: "float",
1165
+ Boolean: "boolean",
1166
+ Date: "date",
1167
+ Timestamp: "timestamp",
1168
+ Duration: "duration"
1169
+ };
1170
+
1171
+ export { ApiError, ArticleStatus, ArticleUsageStatus, ArticleVisibility, Articles, AttachmentType, AttributeCardinality, AttributeType, BackOfficeTaskResultType, BackOfficeTaskStatus, BackOfficeTasks, BodyEncoding, Channel, ConfigurationError, ConversationEventType, Conversations, CustomerSource, ErrorCode, GradientLabs, GradientLabsError, HandOffTargets, InvalidWebhookSignatureError, IpAddressesResource, NoteStatus, Notes, OutboundConversations, ParameterSource, ParameterType, ParticipantType, ProcedureStatus, Procedures, ResourceSourceRefreshStrategy, ResourceSourceScope, ResourceSourceType, ResourceSources, ResourceTypeRefreshStrategy, ResourceTypeScope, ResourceTypes, SchemaUpdateStrategy, Secrets, SupportPlatform, TerminologySubstitutions, Tools, Topics, TrafficGroups, UnknownWebhookTypeError, Voice, WebhookType, WebhookVerifier };
1172
+ //# sourceMappingURL=index.mjs.map
1173
+ //# sourceMappingURL=index.mjs.map