@dialgem/sdk 0.1.0-beta.0 → 0.1.0-beta.2
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/CHANGELOG.md +36 -0
- package/README.md +182 -23
- package/dist/index.cjs +735 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2256 -150
- package/dist/index.d.ts +2256 -150
- package/dist/index.js +732 -54
- package/dist/index.js.map +1 -1
- package/examples/export-calls.ts +11 -14
- package/examples/webhook-express.ts +22 -8
- package/examples/webhook-fastify.ts +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -106,10 +106,10 @@ function isErrorBody(body) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// src/version.ts
|
|
109
|
-
var VERSION = "0.1.0-beta.
|
|
109
|
+
var VERSION = "0.1.0-beta.2";
|
|
110
110
|
|
|
111
111
|
// src/core/http.ts
|
|
112
|
-
var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "PATCH", "DELETE"]);
|
|
112
|
+
var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "PUT", "PATCH", "DELETE"]);
|
|
113
113
|
var MAX_RETRY_DELAY_MS = 6e4;
|
|
114
114
|
var HttpClient = class {
|
|
115
115
|
baseURL;
|
|
@@ -132,6 +132,19 @@ var HttpClient = class {
|
|
|
132
132
|
post(path2, body, options) {
|
|
133
133
|
return this.request("POST", path2, { body, options });
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* POST a `FormData` body — file uploads.
|
|
137
|
+
*
|
|
138
|
+
* The body is sent as-is and `Content-Type` is left unset, because only
|
|
139
|
+
* `fetch` knows the multipart boundary it is about to generate. Setting the
|
|
140
|
+
* header ourselves produces a request no server can parse.
|
|
141
|
+
*/
|
|
142
|
+
postForm(path2, form, options) {
|
|
143
|
+
return this.request("POST", path2, { body: form, options });
|
|
144
|
+
}
|
|
145
|
+
put(path2, body, options) {
|
|
146
|
+
return this.request("PUT", path2, { body, options });
|
|
147
|
+
}
|
|
135
148
|
patch(path2, body, options) {
|
|
136
149
|
return this.request("PATCH", path2, { body, options });
|
|
137
150
|
}
|
|
@@ -140,10 +153,11 @@ var HttpClient = class {
|
|
|
140
153
|
}
|
|
141
154
|
async request(method, path2, { query, body, options = {} }) {
|
|
142
155
|
const url = this.buildURL(path2, query);
|
|
143
|
-
const
|
|
144
|
-
const
|
|
156
|
+
const isForm = isFormData(body);
|
|
157
|
+
const headers = this.buildHeaders(body !== void 0 && !isForm, options.headers);
|
|
158
|
+
const payload = body === void 0 ? void 0 : isForm ? body : JSON.stringify(body);
|
|
145
159
|
const timeout = options.timeout ?? this.#timeout;
|
|
146
|
-
const maxRetries = options.maxRetries ?? (RETRYABLE_METHODS.has(method) ? this.#maxRetries : 0);
|
|
160
|
+
const maxRetries = isForm ? 0 : options.maxRetries ?? (RETRYABLE_METHODS.has(method) ? this.#maxRetries : 0);
|
|
147
161
|
assertTimeout(timeout);
|
|
148
162
|
assertMaxRetries(maxRetries);
|
|
149
163
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -197,7 +211,7 @@ var HttpClient = class {
|
|
|
197
211
|
const params = new URLSearchParams();
|
|
198
212
|
for (const [key, value] of Object.entries(query)) {
|
|
199
213
|
if (value === void 0 || value === null) continue;
|
|
200
|
-
params.append(key, String(
|
|
214
|
+
for (const item of Array.isArray(value) ? value : [value]) params.append(key, String(item));
|
|
201
215
|
}
|
|
202
216
|
const qs = params.toString();
|
|
203
217
|
return qs ? `${url}?${qs}` : url;
|
|
@@ -220,6 +234,9 @@ var HttpClient = class {
|
|
|
220
234
|
return Object.fromEntries(merged.values());
|
|
221
235
|
}
|
|
222
236
|
};
|
|
237
|
+
function isFormData(value) {
|
|
238
|
+
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
239
|
+
}
|
|
223
240
|
function assertTimeout(value) {
|
|
224
241
|
if (!Number.isInteger(value) || value <= 0) {
|
|
225
242
|
throw new DialgemError(`\`timeout\` must be a positive integer of milliseconds, got ${String(value)}.`);
|
|
@@ -316,11 +333,14 @@ var Agents = class extends APIResource {
|
|
|
316
333
|
knowledge;
|
|
317
334
|
/** Remote MCP servers whose tools are exposed to the agent. */
|
|
318
335
|
mcpServers;
|
|
336
|
+
/** Frozen snapshots of the agent that a number or a call can pin. */
|
|
337
|
+
versions;
|
|
319
338
|
constructor(client) {
|
|
320
339
|
super(client);
|
|
321
340
|
this.functions = new AgentFunctions(client);
|
|
322
341
|
this.knowledge = new AgentKnowledge(client);
|
|
323
342
|
this.mcpServers = new AgentMCPServers(client);
|
|
343
|
+
this.versions = new AgentVersions(client);
|
|
324
344
|
}
|
|
325
345
|
/** Create an agent. Only `name` is required; everything else has a server default. */
|
|
326
346
|
create(body, options) {
|
|
@@ -341,6 +361,48 @@ var Agents = class extends APIResource {
|
|
|
341
361
|
return this._client.delete(path`/v1/agents/${agentId}`, options);
|
|
342
362
|
}
|
|
343
363
|
};
|
|
364
|
+
var AgentVersions = class extends APIResource {
|
|
365
|
+
/** Freeze the agent as it is now. Returns the new version, snapshot included. */
|
|
366
|
+
publish(agentId, body = {}, options) {
|
|
367
|
+
return this._client.post(path`/v1/agents/${agentId}/versions`, body, options);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* One page of published versions, newest first, without their snapshots.
|
|
371
|
+
*
|
|
372
|
+
* Returns the array. When you need to page — an agent published on every
|
|
373
|
+
* deploy accumulates versions, and nothing prunes them — read `has_more` from
|
|
374
|
+
* {@link listPage} instead and pass the last `version` as `before_version`.
|
|
375
|
+
*/
|
|
376
|
+
async list(agentId, params, options) {
|
|
377
|
+
return (await this.listPage(agentId, params, options)).versions;
|
|
378
|
+
}
|
|
379
|
+
/** {@link list} with the `has_more` flag, for paging. */
|
|
380
|
+
listPage(agentId, params, options) {
|
|
381
|
+
return this._client.get(
|
|
382
|
+
path`/v1/agents/${agentId}/versions`,
|
|
383
|
+
// Spread into a plain query record: an `interface` has no index
|
|
384
|
+
// signature, so it does not structurally satisfy `Query`.
|
|
385
|
+
{ ...params },
|
|
386
|
+
options
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
/** One version with its full snapshot. Version 0 is the draft: use `agents.retrieve`. */
|
|
390
|
+
retrieve(agentId, version, options) {
|
|
391
|
+
return this._client.get(
|
|
392
|
+
path`/v1/agents/${agentId}/versions/${versionSegment(version)}`,
|
|
393
|
+
void 0,
|
|
394
|
+
options
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
function versionSegment(version) {
|
|
399
|
+
if (!Number.isInteger(version) || version < 1) {
|
|
400
|
+
throw new DialgemError(
|
|
401
|
+
`Expected a published version number (a positive integer), got ${JSON.stringify(version)}. Version 0 is the draft \u2014 read it with agents.retrieve().`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
return String(version);
|
|
405
|
+
}
|
|
344
406
|
var AgentFunctions = class extends APIResource {
|
|
345
407
|
list(agentId, options) {
|
|
346
408
|
return this._client.get(path`/v1/agents/${agentId}/functions`, void 0, options);
|
|
@@ -385,6 +447,13 @@ var AgentMCPServers = class extends APIResource {
|
|
|
385
447
|
return this._client.delete(path`/v1/agents/${agentId}/mcp-servers/${serverId}`, options);
|
|
386
448
|
}
|
|
387
449
|
/** Connect to the server now, list its tools, and store the result as `last_status`. */
|
|
450
|
+
/**
|
|
451
|
+
* What the server offers right now, with each tool's description and argument schema. Connects live like
|
|
452
|
+
* `test`, but stores nothing. An unreachable server is not an error: `tools` is empty and `error` says why.
|
|
453
|
+
*/
|
|
454
|
+
listTools(agentId, serverId, options) {
|
|
455
|
+
return this._client.get(path`/v1/agents/${agentId}/mcp-servers/${serverId}/tools`, void 0, options);
|
|
456
|
+
}
|
|
388
457
|
test(agentId, serverId, options) {
|
|
389
458
|
return this._client.post(path`/v1/agents/${agentId}/mcp-servers/${serverId}/test`, void 0, options);
|
|
390
459
|
}
|
|
@@ -393,35 +462,33 @@ var AgentMCPServers = class extends APIResource {
|
|
|
393
462
|
// src/core/pagination.ts
|
|
394
463
|
var Page = class _Page {
|
|
395
464
|
data;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
page_size;
|
|
465
|
+
has_more;
|
|
466
|
+
pagination_key;
|
|
399
467
|
#fetchPage;
|
|
400
468
|
constructor(data, fetchPage) {
|
|
401
469
|
this.data = data.data;
|
|
402
|
-
this.
|
|
403
|
-
this.
|
|
404
|
-
this.page_size = data.page_size;
|
|
470
|
+
this.has_more = data.has_more;
|
|
471
|
+
this.pagination_key = data.pagination_key;
|
|
405
472
|
this.#fetchPage = fetchPage;
|
|
406
473
|
}
|
|
407
474
|
hasNextPage() {
|
|
408
|
-
return this.
|
|
475
|
+
return this.has_more && this.pagination_key !== null;
|
|
409
476
|
}
|
|
410
477
|
async getNextPage() {
|
|
411
478
|
if (!this.hasNextPage()) throw new DialgemError("No next page; check hasNextPage() first.");
|
|
412
|
-
return new _Page(await this.#fetchPage(this.
|
|
479
|
+
return new _Page(await this.#fetchPage(this.pagination_key), this.#fetchPage);
|
|
413
480
|
}
|
|
414
481
|
};
|
|
415
482
|
var PagePromise = class {
|
|
416
483
|
#fetchPage;
|
|
417
|
-
#
|
|
484
|
+
#startKey;
|
|
418
485
|
#first;
|
|
419
|
-
constructor(fetchPage,
|
|
486
|
+
constructor(fetchPage, startKey = null) {
|
|
420
487
|
this.#fetchPage = fetchPage;
|
|
421
|
-
this.#
|
|
488
|
+
this.#startKey = startKey;
|
|
422
489
|
}
|
|
423
490
|
#firstPage() {
|
|
424
|
-
this.#first ??= this.#fetchPage(this.#
|
|
491
|
+
this.#first ??= this.#fetchPage(this.#startKey).then((data) => new Page(data, this.#fetchPage));
|
|
425
492
|
return this.#first;
|
|
426
493
|
}
|
|
427
494
|
then(onfulfilled, onrejected) {
|
|
@@ -446,24 +513,64 @@ var PagePromise = class {
|
|
|
446
513
|
// src/resources/calls.ts
|
|
447
514
|
var Calls = class extends APIResource {
|
|
448
515
|
/**
|
|
449
|
-
* Calls, newest first. Await for one page, or iterate for every call:
|
|
516
|
+
* Calls, newest first by default. Await for one page, or iterate for every call:
|
|
450
517
|
*
|
|
451
|
-
* const page = await dialgem.calls.list({
|
|
452
|
-
* for await (const call of dialgem.calls.list({ status: "completed" })) {
|
|
453
|
-
* if (call.started_at < cutoff) break // newest first: everything after is older
|
|
454
|
-
* }
|
|
518
|
+
* const page = await dialgem.calls.list({ limit: 50 })
|
|
519
|
+
* for await (const call of dialgem.calls.list({ status: "completed", start_after: since })) { ... }
|
|
455
520
|
*/
|
|
456
521
|
list(params = {}, options) {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
522
|
+
const query = toQuery(params);
|
|
523
|
+
const fetchPage = async (paginationKey) => {
|
|
524
|
+
const res = await this._client.get(
|
|
525
|
+
"/v1/calls",
|
|
526
|
+
{ ...query, pagination_key: paginationKey ?? void 0 },
|
|
527
|
+
options
|
|
528
|
+
);
|
|
529
|
+
return { data: res.calls, has_more: res.has_more, pagination_key: res.pagination_key };
|
|
460
530
|
};
|
|
461
|
-
return new PagePromise(fetchPage
|
|
531
|
+
return new PagePromise(fetchPage);
|
|
462
532
|
}
|
|
463
|
-
/** One call, including its transcript, analysis and recording URL. */
|
|
533
|
+
/** One call, including its transcript, analysis, latency and recording URL. */
|
|
464
534
|
retrieve(callId, options) {
|
|
465
535
|
return this._client.get(path`/v1/calls/${callId}`, void 0, options);
|
|
466
536
|
}
|
|
537
|
+
/** Replace the call's `metadata`. */
|
|
538
|
+
update(callId, body, options) {
|
|
539
|
+
return this._client.patch(path`/v1/calls/${callId}`, body, options);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Permanently delete a finished call: its record, transcript, analysis and
|
|
543
|
+
* recording. A call still in progress is refused with `ConflictError`; stop it first.
|
|
544
|
+
*/
|
|
545
|
+
delete(callId, options) {
|
|
546
|
+
return this._client.delete(path`/v1/calls/${callId}`, options);
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Run a finished call's post-call analysis again, with the agent's *current* analysis
|
|
550
|
+
* settings, and return the updated call. Use it after changing the fields or the model
|
|
551
|
+
* and wanting an older call read the new way.
|
|
552
|
+
*
|
|
553
|
+
* It replaces `analysis`; it does not bill the call again and does not send `call_analyzed`
|
|
554
|
+
* again, so read the result from this response or from `retrieve`. The model call can take
|
|
555
|
+
* a while. `ConflictError` with `errCode` `call_in_progress` until the call has ended,
|
|
556
|
+
* `no_transcript` when there is nothing to analyze, `agent_deleted` if the agent is gone.
|
|
557
|
+
* Not retried by default.
|
|
558
|
+
*/
|
|
559
|
+
rerunAnalysis(callId, options) {
|
|
560
|
+
return this._client.post(path`/v1/calls/${callId}/rerun-analysis`, void 0, options);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* End an ongoing call now, as if the agent had hung up. The call becomes
|
|
564
|
+
* `completed` with `disconnection_reason: "manual_stopped"` a moment later, and
|
|
565
|
+
* the usual `call_ended` webhook follows.
|
|
566
|
+
*
|
|
567
|
+
* `ConflictError` with `errCode` `call_not_ongoing` if it already ended, or
|
|
568
|
+
* `call_not_connected` if no agent is on the call yet (e.g. still ringing).
|
|
569
|
+
* Not retried by default.
|
|
570
|
+
*/
|
|
571
|
+
stop(callId, options) {
|
|
572
|
+
return this._client.post(path`/v1/calls/${callId}/stop`, void 0, options);
|
|
573
|
+
}
|
|
467
574
|
/**
|
|
468
575
|
* Dial `to` from one of your organization's numbers, with `agent_id` (or the
|
|
469
576
|
* number's outbound agent) on the line.
|
|
@@ -483,13 +590,76 @@ var Calls = class extends APIResource {
|
|
|
483
590
|
return this._client.post("/v1/calls/register", body, options);
|
|
484
591
|
}
|
|
485
592
|
};
|
|
593
|
+
function toQuery(params) {
|
|
594
|
+
const { metadata, ...rest } = params;
|
|
595
|
+
const query = { ...rest };
|
|
596
|
+
query["limit"] = params.limit ?? 10;
|
|
597
|
+
for (const [key, value] of Object.entries(metadata ?? {})) {
|
|
598
|
+
query[`metadata.${key}`] = String(value);
|
|
599
|
+
}
|
|
600
|
+
return query;
|
|
601
|
+
}
|
|
486
602
|
|
|
487
603
|
// src/resources/catalog.ts
|
|
488
604
|
var Voices = class extends APIResource {
|
|
489
|
-
/**
|
|
605
|
+
/**
|
|
606
|
+
* Voices available to agents: the shared catalog plus the ones your
|
|
607
|
+
* organization cloned, yours first. Put a voice's `provider_voice_id` in an
|
|
608
|
+
* agent's `voice_id`.
|
|
609
|
+
*/
|
|
490
610
|
list(params = {}, options) {
|
|
491
611
|
return this._client.get("/v1/voices", params, options);
|
|
492
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Clone a voice from audio you upload. The new voice is private to your
|
|
615
|
+
* organization and never appears in another one's catalog.
|
|
616
|
+
*
|
|
617
|
+
* Consent is required: you are asserting that you hold the rights to the
|
|
618
|
+
* audio and have the speaker's consent, and that assertion is recorded
|
|
619
|
+
* against your API identity.
|
|
620
|
+
*
|
|
621
|
+
* The upload is never retried, whatever `maxRetries` says — a retried
|
|
622
|
+
* multipart body can send truncated audio, and a clone built from half the
|
|
623
|
+
* recording succeeds and sounds wrong.
|
|
624
|
+
*/
|
|
625
|
+
clone(params, options) {
|
|
626
|
+
const { name, files, consent_confirmed, engine, description, language, filenames } = params;
|
|
627
|
+
if (!consent_confirmed) {
|
|
628
|
+
throw new DialgemError(
|
|
629
|
+
"consent_confirmed must be true: you are asserting that you hold the rights to this audio and have the speaker's consent to clone their voice."
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
if (!files?.length) {
|
|
633
|
+
throw new DialgemError("At least one audio file is required to clone a voice.");
|
|
634
|
+
}
|
|
635
|
+
const form = new FormData();
|
|
636
|
+
form.append("name", name);
|
|
637
|
+
form.append("consent_confirmed", "true");
|
|
638
|
+
if (engine) form.append("engine", engine);
|
|
639
|
+
if (description) form.append("description", description);
|
|
640
|
+
if (language) form.append("language", language);
|
|
641
|
+
files.forEach((file, i) => {
|
|
642
|
+
const fallback = file.name || `clip-${i + 1}.wav`;
|
|
643
|
+
form.append("files", file, filenames?.[i] ?? fallback);
|
|
644
|
+
});
|
|
645
|
+
return this._client.postForm("/v1/voices/clone", form, options);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Delete a voice your organization cloned, at the provider and here.
|
|
649
|
+
*
|
|
650
|
+
* Only your own cloned voices: the shared catalog is not deletable, and a
|
|
651
|
+
* voice an agent still uses is refused with a 409 rather than leaving that
|
|
652
|
+
* agent without one.
|
|
653
|
+
*/
|
|
654
|
+
delete(voiceId, options) {
|
|
655
|
+
return this._client.delete(path`/v1/voices/${voiceId}`, options);
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
var ExtractionModels = class extends APIResource {
|
|
659
|
+
/** Models an agent may use for post-call analysis, with an estimated cost per session. */
|
|
660
|
+
list(options) {
|
|
661
|
+
return this._client.get("/v1/models/extraction", void 0, options);
|
|
662
|
+
}
|
|
493
663
|
};
|
|
494
664
|
var FlowTemplates = class extends APIResource {
|
|
495
665
|
/** Built-in conversation-flow templates (summaries, without `flow_config`). */
|
|
@@ -510,6 +680,287 @@ var PromptTemplates = class extends APIResource {
|
|
|
510
680
|
}
|
|
511
681
|
};
|
|
512
682
|
|
|
683
|
+
// src/resources/account.ts
|
|
684
|
+
var ApiKey = class extends APIResource {
|
|
685
|
+
/**
|
|
686
|
+
* Who this key is: your organization and the key's name, prefix, scopes, allowed addresses
|
|
687
|
+
* and expiry. It answers for any valid key whatever its scopes, so an integration can check
|
|
688
|
+
* what it was given. Never returns the secret.
|
|
689
|
+
*
|
|
690
|
+
* ```ts
|
|
691
|
+
* const { key } = await dialgem.apiKey.retrieve()
|
|
692
|
+
* if (key && !key.full_access) console.log("scoped to", key.scopes)
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
retrieve(options) {
|
|
696
|
+
return this._client.get("/v1/api-key-info", void 0, options);
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
var Sessions = class extends APIResource {
|
|
700
|
+
/**
|
|
701
|
+
* The calls your organization has in progress right now, with a `count`.
|
|
702
|
+
*
|
|
703
|
+
* Live state read from the service's own registry — use it for a
|
|
704
|
+
* concurrency check before placing a call. For finished calls use
|
|
705
|
+
* `calls.list`; a call whose teardown never arrived is not counted here.
|
|
706
|
+
*/
|
|
707
|
+
list(options) {
|
|
708
|
+
return this._client.get("/v1/agent/sessions", void 0, options);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Change a live call's prompt and/or variables. It applies from the agent's **next turn**.
|
|
712
|
+
*
|
|
713
|
+
* `sessionId` is a session's `session_id` from `list`, or the call's own `call_id`. Only this call changes:
|
|
714
|
+
* the agent, its published versions and other calls are untouched, and the change is gone when the call ends.
|
|
715
|
+
* A turn already being spoken finishes as it was. `dynamic_variables` are merged over the call's variables;
|
|
716
|
+
* `system_prompt` replaces the prompt for the rest of the call.
|
|
717
|
+
*
|
|
718
|
+
* The call may be on any service instance. `NotFoundError` if it is not live (or is another organization's),
|
|
719
|
+
* `ConflictError` with `errCode` `session_ended` once it is over, `UnprocessableEntityError` with `errCode`
|
|
720
|
+
* `flow_agent_prompt` for a flow agent's prompt (its variables can still change). Not retried by default.
|
|
721
|
+
*
|
|
722
|
+
* ```ts
|
|
723
|
+
* await dialgem.sessions.update(callId, { dynamic_variables: { plan: "pro" } })
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
update(sessionId, body, options) {
|
|
727
|
+
return this._client.patch(path`/v1/agent/sessions/${sessionId}`, body, options);
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Make the agent say `text` on a live call, in its own voice. Answers at once; the line plays behind what the
|
|
731
|
+
* agent is saying, or now with `interrupt: true`. The agent's conversation records it as something it said.
|
|
732
|
+
*
|
|
733
|
+
* Off unless the deployment enables live control: `ForbiddenError` with `errCode` `live_control_disabled`.
|
|
734
|
+
* `ConflictError` `human_has_call` while a human has taken the call. Owners, admins and API keys only.
|
|
735
|
+
* Not retried by default.
|
|
736
|
+
*
|
|
737
|
+
* ```ts
|
|
738
|
+
* await dialgem.sessions.say(callId, { text: "One moment while I check that." })
|
|
739
|
+
* ```
|
|
740
|
+
*/
|
|
741
|
+
say(sessionId, body, options) {
|
|
742
|
+
return this._client.post(path`/v1/agent/sessions/${sessionId}/say`, body, options);
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Hand a taken-over call back to the agent, for when the socket that took it is gone. The agent is told a
|
|
746
|
+
* human had the call and that it did not hear what was said. Same availability and limits as `say`.
|
|
747
|
+
* Listening in and taking over run over a WebSocket (`/v1/agent/sessions/{id}/live`), which this SDK does not wrap.
|
|
748
|
+
*/
|
|
749
|
+
release(sessionId, options) {
|
|
750
|
+
return this._client.post(path`/v1/agent/sessions/${sessionId}/release`, void 0, options);
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
// src/resources/simulations.ts
|
|
755
|
+
var Simulations = class extends APIResource {
|
|
756
|
+
/** Make a suite for one of your agents. */
|
|
757
|
+
createSuite(body, options) {
|
|
758
|
+
return this._client.post("/v1/simulations/suites", body, options);
|
|
759
|
+
}
|
|
760
|
+
/** Your suites, newest first, each with its latest run. Pass `agent_id` for one agent's. */
|
|
761
|
+
listSuites(params = {}, options) {
|
|
762
|
+
return this._client.get("/v1/simulations/suites", params, options);
|
|
763
|
+
}
|
|
764
|
+
/** One suite with its cases. */
|
|
765
|
+
retrieveSuite(suiteId, options) {
|
|
766
|
+
return this._client.get(path`/v1/simulations/suites/${suiteId}`, void 0, options);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Rename a suite or set its publish gate: `off`, `warn` (publishing a version succeeds and lists the suites the draft
|
|
770
|
+
* has not passed) or `block` (publishing is refused with 409 `simulation_required` until it has).
|
|
771
|
+
*/
|
|
772
|
+
updateSuite(suiteId, body, options) {
|
|
773
|
+
return this._client.patch(path`/v1/simulations/suites/${suiteId}`, body, options);
|
|
774
|
+
}
|
|
775
|
+
/** Delete a suite and its cases. Past runs are deleted with it. */
|
|
776
|
+
deleteSuite(suiteId, options) {
|
|
777
|
+
return this._client.delete(path`/v1/simulations/suites/${suiteId}`, options);
|
|
778
|
+
}
|
|
779
|
+
/** Add a case (up to 50 per suite). 422 `invalid_case` says what to fix. */
|
|
780
|
+
createCase(suiteId, body, options) {
|
|
781
|
+
return this._client.post(path`/v1/simulations/suites/${suiteId}/cases`, body, options);
|
|
782
|
+
}
|
|
783
|
+
/** Change a case. Fields you leave out keep their value. */
|
|
784
|
+
updateCase(caseId, body, options) {
|
|
785
|
+
return this._client.patch(path`/v1/simulations/cases/${caseId}`, body, options);
|
|
786
|
+
}
|
|
787
|
+
deleteCase(caseId, options) {
|
|
788
|
+
return this._client.delete(path`/v1/simulations/cases/${caseId}`, options);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Run every case of a suite against the draft, a published version or `"latest_published"`. Answers at once
|
|
792
|
+
* (`202`); the run happens in the background, so poll `retrieveRun` until `status` is `completed`.
|
|
793
|
+
*
|
|
794
|
+
* 409 `suite_empty`, 422 `agent_type_not_supported` (multi-prompt agents), 429 `too_many_runs` or
|
|
795
|
+
* `daily_run_limit`.
|
|
796
|
+
*/
|
|
797
|
+
run(suiteId, body = {}, options) {
|
|
798
|
+
return this._client.post(path`/v1/simulations/suites/${suiteId}/runs`, body, options);
|
|
799
|
+
}
|
|
800
|
+
/** Runs, newest first. Pass `suite_id` for one suite's. */
|
|
801
|
+
listRuns(params = {}, options) {
|
|
802
|
+
return this._client.get("/v1/simulations/runs", params, options);
|
|
803
|
+
}
|
|
804
|
+
/** A run with every case's transcript, tool calls and check results. */
|
|
805
|
+
retrieveRun(runId, options) {
|
|
806
|
+
return this._client.get(path`/v1/simulations/runs/${runId}`, void 0, options);
|
|
807
|
+
}
|
|
808
|
+
/** Stop a run that has not finished. Cases already under way finish; the rest are skipped. */
|
|
809
|
+
cancelRun(runId, options) {
|
|
810
|
+
return this._client.post(path`/v1/simulations/runs/${runId}/cancel`, void 0, options);
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// src/resources/web-calls.ts
|
|
815
|
+
var WebCalls = class extends APIResource {
|
|
816
|
+
/**
|
|
817
|
+
* Create a call for one visitor and get its access token.
|
|
818
|
+
*
|
|
819
|
+
* const { access_token } = await dialgem.webCalls.create({ agent_id, metadata: { visitor: "v_123" } })
|
|
820
|
+
*
|
|
821
|
+
* `metadata` and `dialgem_llm_dynamic_variables` are fixed here, by you, and cannot be changed by the page.
|
|
822
|
+
* 409 `web_origins_not_configured` until an origin is allowed; 402 if the organization cannot pay for calls;
|
|
823
|
+
* 429 when more than `limits.tokens_per_minute` calls are created in a minute.
|
|
824
|
+
*/
|
|
825
|
+
create(body, options) {
|
|
826
|
+
return this._client.post("/v1/calls/web", body, options);
|
|
827
|
+
}
|
|
828
|
+
/** The origins browser calls may start from, and the limits that apply. */
|
|
829
|
+
retrieveSettings(options) {
|
|
830
|
+
return this._client.get("/v1/web-calls/settings", void 0, options);
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Replace the allowed origins. Each is `scheme://host[:port]`; `https://*.example.com` allows any subdomain
|
|
834
|
+
* (not the bare domain) and `*` allows any website. Tokens already issued keep the list they were issued with.
|
|
835
|
+
*/
|
|
836
|
+
updateSettings(body, options) {
|
|
837
|
+
return this._client.put("/v1/web-calls/settings", body, options);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
// src/resources/contacts.ts
|
|
842
|
+
var Contacts = class extends APIResource {
|
|
843
|
+
/** Bulk import from CSV, with a dry-run preview. */
|
|
844
|
+
imports;
|
|
845
|
+
constructor(client) {
|
|
846
|
+
super(client);
|
|
847
|
+
this.imports = new ContactImports(client);
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Add a contact. `phone` is any way of writing a valid number; without a leading `+`
|
|
851
|
+
* also pass `default_country` (`"US"`). A number that is already a contact answers 409
|
|
852
|
+
* with its `contact_id`.
|
|
853
|
+
*/
|
|
854
|
+
create(body, options) {
|
|
855
|
+
return this._client.post("/v1/contacts", body, options);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Contacts, newest first. Await for one page, or iterate for every contact:
|
|
859
|
+
*
|
|
860
|
+
*/
|
|
861
|
+
list(params = {}, options) {
|
|
862
|
+
const fetchPage = async (cursor) => {
|
|
863
|
+
const res = await this._client.get(
|
|
864
|
+
"/v1/contacts",
|
|
865
|
+
{ ...params, cursor: cursor ?? void 0 },
|
|
866
|
+
options
|
|
867
|
+
);
|
|
868
|
+
return { data: res.contacts, has_more: res.next_cursor !== null, pagination_key: res.next_cursor };
|
|
869
|
+
};
|
|
870
|
+
return new PagePromise(fetchPage);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* The contact with this phone number. The number is matched as E.164, so
|
|
874
|
+
* `("(212) 220-0001", { default_country: "US" })` finds `+12122200001`. Throws
|
|
875
|
+
* `NotFoundError` if you have no such contact, and `UnprocessableEntityError` if the text is
|
|
876
|
+
* not a phone number.
|
|
877
|
+
*/
|
|
878
|
+
retrieveByPhone(phone, params = {}, options) {
|
|
879
|
+
return this._client.get(path`/v1/contacts/by-phone/${phone}`, params, options);
|
|
880
|
+
}
|
|
881
|
+
/** One contact, with its lists and how many calls it has had. */
|
|
882
|
+
retrieve(contactId, options) {
|
|
883
|
+
return this._client.get(path`/v1/contacts/${contactId}`, void 0, options);
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Partial update: only the fields you send change, and `null` clears a field.
|
|
887
|
+
* `attributes` replaces the whole set, so send every field you want kept.
|
|
888
|
+
*/
|
|
889
|
+
update(contactId, body, options) {
|
|
890
|
+
return this._client.patch(path`/v1/contacts/${contactId}`, body, options);
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Erase a contact and its list memberships. Calls already made stay in the call log, and
|
|
894
|
+
* a do-not-call entry for the number stays too, so forgetting a person never makes a number
|
|
895
|
+
* that asked not to be called dialable again.
|
|
896
|
+
*/
|
|
897
|
+
delete(contactId, options) {
|
|
898
|
+
return this._client.delete(path`/v1/contacts/${contactId}`, options);
|
|
899
|
+
}
|
|
900
|
+
/** Calls to or from the contact's number, newest first. Summaries, not transcripts. */
|
|
901
|
+
calls(contactId, params = {}, options) {
|
|
902
|
+
return this._client.get(path`/v1/contacts/${contactId}/calls`, params, options);
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
var ContactImports = class extends APIResource {
|
|
906
|
+
/** The file's headers, a guessed column mapping and a few rows. Imports nothing. */
|
|
907
|
+
inspect(csv, options) {
|
|
908
|
+
return this._client.post("/v1/contacts/imports/inspect", { csv }, options);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Start an import. Without `mapping` the columns are guessed from the headers. With
|
|
912
|
+
* `dry_run: true` nothing is written: the import only validates and reports.
|
|
913
|
+
*/
|
|
914
|
+
create(body, options) {
|
|
915
|
+
return this._client.post("/v1/contacts/imports", body, options);
|
|
916
|
+
}
|
|
917
|
+
/** Recent imports, newest first. */
|
|
918
|
+
list(options) {
|
|
919
|
+
return this._client.get("/v1/contacts/imports", void 0, options);
|
|
920
|
+
}
|
|
921
|
+
/** Progress and counts. */
|
|
922
|
+
retrieve(importId, options) {
|
|
923
|
+
return this._client.get(path`/v1/contacts/imports/${importId}`, void 0, options);
|
|
924
|
+
}
|
|
925
|
+
/** The rows that were rejected, with the line, the reason and what each held. */
|
|
926
|
+
errors(importId, params = {}, options) {
|
|
927
|
+
return this._client.get(path`/v1/contacts/imports/${importId}/errors`, params, options);
|
|
928
|
+
}
|
|
929
|
+
/** Apply a previewed import: the file is read again, so what is applied is what was previewed. */
|
|
930
|
+
commit(importId, options) {
|
|
931
|
+
return this._client.post(path`/v1/contacts/imports/${importId}/commit`, void 0, options);
|
|
932
|
+
}
|
|
933
|
+
/** Stop an import that has not finished. Contacts a running import already wrote stay. */
|
|
934
|
+
cancel(importId, options) {
|
|
935
|
+
return this._client.delete(path`/v1/contacts/imports/${importId}`, options);
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
var ContactLists = class extends APIResource {
|
|
939
|
+
list(options) {
|
|
940
|
+
return this._client.get("/v1/contact-lists", void 0, options);
|
|
941
|
+
}
|
|
942
|
+
/** Names are unique within your organization, ignoring case. */
|
|
943
|
+
create(body, options) {
|
|
944
|
+
return this._client.post("/v1/contact-lists", body, options);
|
|
945
|
+
}
|
|
946
|
+
retrieve(listId, options) {
|
|
947
|
+
return this._client.get(path`/v1/contact-lists/${listId}`, void 0, options);
|
|
948
|
+
}
|
|
949
|
+
update(listId, body, options) {
|
|
950
|
+
return this._client.patch(path`/v1/contact-lists/${listId}`, body, options);
|
|
951
|
+
}
|
|
952
|
+
delete(listId, options) {
|
|
953
|
+
return this._client.delete(path`/v1/contact-lists/${listId}`, options);
|
|
954
|
+
}
|
|
955
|
+
/** Add up to 1000 of your contacts. Already-members are ignored; `added` says how many were new. */
|
|
956
|
+
addContacts(listId, contactIds, options) {
|
|
957
|
+
return this._client.post(path`/v1/contact-lists/${listId}/members`, { contact_ids: contactIds }, options);
|
|
958
|
+
}
|
|
959
|
+
removeContact(listId, contactId, options) {
|
|
960
|
+
return this._client.delete(path`/v1/contact-lists/${listId}/members/${contactId}`, options);
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
|
|
513
964
|
// src/resources/phone-numbers.ts
|
|
514
965
|
var PhoneNumbers = class extends APIResource {
|
|
515
966
|
/** Register a number you own (`phone_number` in E.164) with Dialgem. */
|
|
@@ -520,6 +971,20 @@ var PhoneNumbers = class extends APIResource {
|
|
|
520
971
|
list(options) {
|
|
521
972
|
return this._client.get("/v1/phone-numbers", void 0, options);
|
|
522
973
|
}
|
|
974
|
+
/**
|
|
975
|
+
* One number by its E.164 value, e.g. `+14155550123`.
|
|
976
|
+
*
|
|
977
|
+
* Keyed on the number rather than on `id`, because the number is what you
|
|
978
|
+
* have when you are asking whether it is registered at all. Throws
|
|
979
|
+
* `NotFoundError` when it is not.
|
|
980
|
+
*/
|
|
981
|
+
retrieve(phoneNumber, options) {
|
|
982
|
+
return this._client.get(
|
|
983
|
+
path`/v1/phone-numbers/by-number/${phoneNumber}`,
|
|
984
|
+
void 0,
|
|
985
|
+
options
|
|
986
|
+
);
|
|
987
|
+
}
|
|
523
988
|
/**
|
|
524
989
|
* Partial update.
|
|
525
990
|
*
|
|
@@ -535,61 +1000,237 @@ var PhoneNumbers = class extends APIResource {
|
|
|
535
1000
|
assign(phoneNumberId, body, options) {
|
|
536
1001
|
return this._client.patch(path`/v1/phone-numbers/${phoneNumberId}/assign`, body, options);
|
|
537
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Send one signed test request to the number's inbound routing webhook and report what a real call would have done
|
|
1005
|
+
* with your answer: the status, how long you took against the time budget, and any field that would be ignored.
|
|
1006
|
+
* Throws a `ConflictError` (`inbound_webhook_not_set`) when the number has no `inbound_webhook_url`.
|
|
1007
|
+
*/
|
|
1008
|
+
testInboundWebhook(phoneNumberId, options) {
|
|
1009
|
+
return this._client.post(path`/v1/phone-numbers/${phoneNumberId}/inbound-webhook/test`, void 0, options);
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Replace the inbound webhook's signing secret. The new secret is returned **once**, as `inbound_webhook_secret`
|
|
1013
|
+
* on the number; the old one stops working at once.
|
|
1014
|
+
*/
|
|
1015
|
+
rotateInboundWebhookSecret(phoneNumberId, options) {
|
|
1016
|
+
return this._client.post(path`/v1/phone-numbers/${phoneNumberId}/inbound-webhook/rotate-secret`, void 0, options);
|
|
1017
|
+
}
|
|
538
1018
|
/** Release the number from Dialgem. It stops routing calls and disappears from `list()`; your carrier account is untouched. */
|
|
539
1019
|
delete(phoneNumberId, options) {
|
|
540
1020
|
return this._client.delete(path`/v1/phone-numbers/${phoneNumberId}`, options);
|
|
541
1021
|
}
|
|
542
1022
|
};
|
|
543
1023
|
|
|
1024
|
+
// src/resources/chats.ts
|
|
1025
|
+
var Chats = class extends APIResource {
|
|
1026
|
+
/**
|
|
1027
|
+
* Start a chat. If the agent has a first message it is already in `messages`.
|
|
1028
|
+
*
|
|
1029
|
+
* `UnprocessableEntityError` with `errCode` `agent_type_not_supported` (flow agents) or `chat_requires_storage`.
|
|
1030
|
+
* Not retried by default.
|
|
1031
|
+
*/
|
|
1032
|
+
create(body, options) {
|
|
1033
|
+
return this._client.post("/v1/chats", body, options);
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Chats, newest first. Await for one page, or iterate for every chat. Entries leave out `messages`;
|
|
1037
|
+
* `retrieve` a chat for them.
|
|
1038
|
+
*/
|
|
1039
|
+
list(params = {}, options) {
|
|
1040
|
+
const fetchPage = async (paginationKey) => {
|
|
1041
|
+
const res = await this._client.get(
|
|
1042
|
+
"/v1/chats",
|
|
1043
|
+
{ ...params, pagination_key: paginationKey ?? void 0 },
|
|
1044
|
+
options
|
|
1045
|
+
);
|
|
1046
|
+
return { data: res.chats, has_more: res.has_more, pagination_key: res.pagination_key };
|
|
1047
|
+
};
|
|
1048
|
+
return new PagePromise(fetchPage);
|
|
1049
|
+
}
|
|
1050
|
+
/** One chat with all its messages. */
|
|
1051
|
+
retrieve(chatId, options) {
|
|
1052
|
+
return this._client.get(path`/v1/chats/${chatId}`, void 0, options);
|
|
1053
|
+
}
|
|
1054
|
+
/** Replace the chat's `metadata` (the whole object). */
|
|
1055
|
+
update(chatId, body, options) {
|
|
1056
|
+
return this._client.patch(path`/v1/chats/${chatId}`, body, options);
|
|
1057
|
+
}
|
|
1058
|
+
/** Delete a chat and its messages. */
|
|
1059
|
+
delete(chatId, options) {
|
|
1060
|
+
return this._client.delete(path`/v1/chats/${chatId}`, options);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Send the user's message and wait for the agent's reply. Returns only the new entries, in order: the user's
|
|
1064
|
+
* message, any tool use, the agent's reply.
|
|
1065
|
+
*
|
|
1066
|
+
* If the agent's model fails (`ServerError`, `errCode` `model_error`) nothing is stored and the same message
|
|
1067
|
+
* can be sent again. `ConflictError` with `errCode` `chat_busy` while another reply is in progress,
|
|
1068
|
+
* `chat_ended` after the end, `message_limit` at the cap. If the agent calls its end-call tool, the reply's
|
|
1069
|
+
* `chat_status` is `ended`.
|
|
1070
|
+
*
|
|
1071
|
+
* Not retried by default: a retry after an ambiguous failure could send the message twice.
|
|
1072
|
+
*/
|
|
1073
|
+
sendMessage(chatId, content, options) {
|
|
1074
|
+
return this._client.post(path`/v1/chats/${chatId}/messages`, { content }, options);
|
|
1075
|
+
}
|
|
1076
|
+
/** End the chat. Ending twice is fine. The analysis arrives a moment later; read it with `retrieve`. */
|
|
1077
|
+
end(chatId, options) {
|
|
1078
|
+
return this._client.post(path`/v1/chats/${chatId}/end`, void 0, options);
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Analyse an ended chat again with the agent's current analysis settings and return it. `ConflictError` with
|
|
1082
|
+
* `errCode` `chat_in_progress` until it has ended, `no_transcript` when it has no messages.
|
|
1083
|
+
*/
|
|
1084
|
+
rerunAnalysis(chatId, options) {
|
|
1085
|
+
return this._client.post(path`/v1/chats/${chatId}/rerun-analysis`, void 0, options);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
// src/resources/flows.ts
|
|
1090
|
+
var Flows = class extends APIResource {
|
|
1091
|
+
/** Validate and save a flow. `UnprocessableEntityError` (`errCode` `flow_invalid`) lists every problem. Not retried by default. */
|
|
1092
|
+
create(body, options) {
|
|
1093
|
+
return this._client.post("/v1/flows", body, options);
|
|
1094
|
+
}
|
|
1095
|
+
/** Flows, newest first. Await for one page, or iterate for every flow. Entries leave out `flow_config`. */
|
|
1096
|
+
list(params = {}, options) {
|
|
1097
|
+
const fetchPage = async (paginationKey) => {
|
|
1098
|
+
const res = await this._client.get(
|
|
1099
|
+
"/v1/flows",
|
|
1100
|
+
{ ...params, pagination_key: paginationKey ?? void 0 },
|
|
1101
|
+
options
|
|
1102
|
+
);
|
|
1103
|
+
return { data: res.flows, has_more: res.has_more, pagination_key: res.pagination_key };
|
|
1104
|
+
};
|
|
1105
|
+
return new PagePromise(fetchPage);
|
|
1106
|
+
}
|
|
1107
|
+
/** One flow with its `flow_config`. */
|
|
1108
|
+
retrieve(flowId, options) {
|
|
1109
|
+
return this._client.get(path`/v1/flows/${flowId}`, void 0, options);
|
|
1110
|
+
}
|
|
1111
|
+
/** Rename a flow and/or replace its `flow_config` (checked again). Agents it was attached to are not changed. */
|
|
1112
|
+
update(flowId, body, options) {
|
|
1113
|
+
return this._client.patch(path`/v1/flows/${flowId}`, body, options);
|
|
1114
|
+
}
|
|
1115
|
+
/** Delete a saved flow. Agents it was attached to keep their copy. */
|
|
1116
|
+
delete(flowId, options) {
|
|
1117
|
+
return this._client.delete(path`/v1/flows/${flowId}`, options);
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
// src/resources/llms.ts
|
|
1122
|
+
var LLMs = class extends APIResource {
|
|
1123
|
+
/** Save LLM settings. Anything left out takes the default a new agent gets. Not retried by default. */
|
|
1124
|
+
create(body, options) {
|
|
1125
|
+
return this._client.post("/v1/llms", body, options);
|
|
1126
|
+
}
|
|
1127
|
+
/** Saved LLMs, newest first. Await for one page, or iterate for every one. */
|
|
1128
|
+
list(params = {}, options) {
|
|
1129
|
+
const fetchPage = async (paginationKey) => {
|
|
1130
|
+
const res = await this._client.get(
|
|
1131
|
+
"/v1/llms",
|
|
1132
|
+
{ ...params, pagination_key: paginationKey ?? void 0 },
|
|
1133
|
+
options
|
|
1134
|
+
);
|
|
1135
|
+
return { data: res.llms, has_more: res.has_more, pagination_key: res.pagination_key };
|
|
1136
|
+
};
|
|
1137
|
+
return new PagePromise(fetchPage);
|
|
1138
|
+
}
|
|
1139
|
+
retrieve(llmId, options) {
|
|
1140
|
+
return this._client.get(path`/v1/llms/${llmId}`, void 0, options);
|
|
1141
|
+
}
|
|
1142
|
+
/** Change the fields you send. Agents it was attached to are not changed. */
|
|
1143
|
+
update(llmId, body, options) {
|
|
1144
|
+
return this._client.patch(path`/v1/llms/${llmId}`, body, options);
|
|
1145
|
+
}
|
|
1146
|
+
/** Delete saved LLM settings. Agents they were attached to keep their copy. */
|
|
1147
|
+
delete(llmId, options) {
|
|
1148
|
+
return this._client.delete(path`/v1/llms/${llmId}`, options);
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
|
|
544
1152
|
// src/webhooks.ts
|
|
545
1153
|
var WEBHOOK_SIGNATURE_HEADER = "x-dialgem-signature";
|
|
546
1154
|
var WEBHOOK_EVENT_HEADER = "x-dialgem-event";
|
|
1155
|
+
var WEBHOOK_PREVIOUS_SIGNATURE_HEADER = "x-dialgem-signature-previous";
|
|
1156
|
+
var WEBHOOK_DELIVERY_HEADER = "x-dialgem-delivery";
|
|
1157
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
547
1158
|
var encoder = new TextEncoder();
|
|
548
1159
|
var decoder = new TextDecoder();
|
|
1160
|
+
var SIGNATURE_RE = /^v=(\d{10,16}),d=([0-9a-f]{64})$/;
|
|
549
1161
|
var Webhooks = class {
|
|
550
1162
|
/**
|
|
551
|
-
* Returns whether `signature` is
|
|
552
|
-
* Constant-time. Returns `false` for a missing
|
|
553
|
-
* if `secret` is empty, which is a configuration
|
|
1163
|
+
* Returns whether `signature` is a current, valid signature of `payload` under
|
|
1164
|
+
* `secret`. Constant-time. Returns `false` for a missing, malformed, tampered or
|
|
1165
|
+
* stale signature; throws only if `secret` is empty, which is a configuration
|
|
1166
|
+
* error rather than a bad request.
|
|
554
1167
|
*/
|
|
555
|
-
async verify(payload, signature, secret) {
|
|
1168
|
+
async verify(payload, signature, secret, options = {}) {
|
|
556
1169
|
if (!secret) {
|
|
557
1170
|
throw new DialgemError(
|
|
558
1171
|
"Webhook secret is empty. Set the agent's `webhook_signing_secret` and pass the same value here."
|
|
559
1172
|
);
|
|
560
1173
|
}
|
|
561
|
-
const
|
|
562
|
-
if (!
|
|
563
|
-
const
|
|
1174
|
+
const match = SIGNATURE_RE.exec(firstHeader(signature)?.trim().toLowerCase() ?? "");
|
|
1175
|
+
if (!match) return false;
|
|
1176
|
+
const [, timestamp, received] = match;
|
|
1177
|
+
const now = options.now ?? Date.now();
|
|
1178
|
+
if (Math.abs(now - Number(timestamp)) > (options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS)) {
|
|
1179
|
+
return false;
|
|
1180
|
+
}
|
|
1181
|
+
const body = toBytes(payload);
|
|
1182
|
+
const message = new Uint8Array(body.length + timestamp.length);
|
|
1183
|
+
message.set(body, 0);
|
|
1184
|
+
message.set(encoder.encode(timestamp), body.length);
|
|
1185
|
+
const expected = await hmacSha256Hex(secret, message);
|
|
564
1186
|
return timingSafeEqual(expected, received);
|
|
565
1187
|
}
|
|
566
1188
|
/**
|
|
567
|
-
* Verifies
|
|
1189
|
+
* Verifies a webhook's signature, then parses its body.
|
|
1190
|
+
*
|
|
1191
|
+
* @throws WebhookVerificationError if the signature is missing, invalid or stale.
|
|
1192
|
+
*/
|
|
1193
|
+
async constructEvent(payload, signature, secret, options = {}) {
|
|
1194
|
+
const parsed = await this.#verifiedJson(payload, signature, secret, options);
|
|
1195
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.event !== "string") {
|
|
1196
|
+
throw new WebhookVerificationError("Webhook body has no `event` field.");
|
|
1197
|
+
}
|
|
1198
|
+
return parsed;
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Verifies a request Dialgem made to one of your custom functions, then parses
|
|
1202
|
+
* its body. For functions with `args_at_root`, the body is the arguments object
|
|
1203
|
+
* alone; use `verify` and parse it yourself.
|
|
568
1204
|
*
|
|
569
|
-
* @throws WebhookVerificationError if the signature is missing or
|
|
1205
|
+
* @throws WebhookVerificationError if the signature is missing, invalid or stale.
|
|
570
1206
|
*/
|
|
571
|
-
async
|
|
1207
|
+
async constructFunctionCall(payload, signature, secret, options = {}) {
|
|
1208
|
+
const parsed = await this.#verifiedJson(payload, signature, secret, options);
|
|
1209
|
+
const body = parsed;
|
|
1210
|
+
if (!body || typeof body.name !== "string" || typeof body.call !== "object" || body.call === null) {
|
|
1211
|
+
throw new WebhookVerificationError(
|
|
1212
|
+
"Function request body has no `name` and `call`. Does this function have args_at_root set?"
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
return body;
|
|
1216
|
+
}
|
|
1217
|
+
async #verifiedJson(payload, signature, secret, options) {
|
|
572
1218
|
if (!firstHeader(signature)) {
|
|
573
1219
|
throw new WebhookVerificationError(
|
|
574
1220
|
`Missing ${WEBHOOK_SIGNATURE_HEADER} header. Is a webhook_signing_secret set on the agent?`
|
|
575
1221
|
);
|
|
576
1222
|
}
|
|
577
|
-
if (!await this.verify(payload, signature, secret)) {
|
|
1223
|
+
if (!await this.verify(payload, signature, secret, options)) {
|
|
578
1224
|
throw new WebhookVerificationError(
|
|
579
|
-
"
|
|
1225
|
+
"Signature is invalid or older than the allowed tolerance. Check the secret, the server clock, and that the body was not re-serialized before verification."
|
|
580
1226
|
);
|
|
581
1227
|
}
|
|
582
1228
|
const text = typeof payload === "string" ? payload : decoder.decode(toBytes(payload));
|
|
583
|
-
let parsed;
|
|
584
1229
|
try {
|
|
585
|
-
|
|
1230
|
+
return JSON.parse(text);
|
|
586
1231
|
} catch {
|
|
587
|
-
throw new WebhookVerificationError("
|
|
588
|
-
}
|
|
589
|
-
if (typeof parsed !== "object" || parsed === null || typeof parsed.event !== "string") {
|
|
590
|
-
throw new WebhookVerificationError("Webhook body has no `event` field.");
|
|
1232
|
+
throw new WebhookVerificationError("Body is signed but is not valid JSON.");
|
|
591
1233
|
}
|
|
592
|
-
return parsed;
|
|
593
1234
|
}
|
|
594
1235
|
};
|
|
595
1236
|
var webhooks = new Webhooks();
|
|
@@ -619,22 +1260,50 @@ function timingSafeEqual(a, b) {
|
|
|
619
1260
|
}
|
|
620
1261
|
|
|
621
1262
|
// src/client.ts
|
|
622
|
-
var
|
|
1263
|
+
var BASE_URL = "https://api.dialgem.com";
|
|
623
1264
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
624
1265
|
var DEFAULT_MAX_RETRIES = 2;
|
|
625
1266
|
var Dialgem = class {
|
|
626
|
-
static
|
|
1267
|
+
static BASE_URL = BASE_URL;
|
|
627
1268
|
/** Webhook verification without a client instance: `Dialgem.webhooks.constructEvent(...)`. */
|
|
628
1269
|
static webhooks = webhooks;
|
|
629
1270
|
agents;
|
|
630
1271
|
calls;
|
|
631
1272
|
phoneNumbers;
|
|
632
1273
|
voices;
|
|
1274
|
+
/** Models available for post-call analysis. */
|
|
1275
|
+
extractionModels;
|
|
633
1276
|
flowTemplates;
|
|
634
1277
|
promptTemplates;
|
|
1278
|
+
/** Calls in progress right now. */
|
|
1279
|
+
sessions;
|
|
1280
|
+
/** Text conversations with an agent. */
|
|
1281
|
+
chats;
|
|
1282
|
+
flows;
|
|
1283
|
+
llms;
|
|
1284
|
+
/** What this API key is and may do. */
|
|
1285
|
+
apiKey;
|
|
1286
|
+
/**
|
|
1287
|
+
* Verify what Dialgem sends you: `dialgem.webhooks.constructEvent(rawBody, signatureHeader, secret)`.
|
|
1288
|
+
*/
|
|
635
1289
|
webhooks = webhooks;
|
|
636
|
-
|
|
1290
|
+
/** The organization's webhook destinations: create, update, rotate the signing secret, test. */
|
|
1291
|
+
/** The people you call, with CSV import under `contacts.imports`. */
|
|
1292
|
+
contacts;
|
|
1293
|
+
/** Named groups of contacts. */
|
|
1294
|
+
contactLists;
|
|
1295
|
+
/** Browser calls: tokens for `@dialgem/client`, and the origins allowed to start them. */
|
|
1296
|
+
webCalls;
|
|
1297
|
+
/** Saved test conversations for an agent, run in text, with an optional publish gate. */
|
|
1298
|
+
simulations;
|
|
1299
|
+
/** Always `https://api.dialgem.com`. */
|
|
1300
|
+
baseURL = BASE_URL;
|
|
637
1301
|
constructor(options = {}) {
|
|
1302
|
+
if ("baseURL" in options) {
|
|
1303
|
+
throw new DialgemError(
|
|
1304
|
+
"`baseURL` is not an option: the SDK always talks to https://api.dialgem.com. Remove it."
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
638
1307
|
if (isBrowser() && !options.dangerouslyAllowBrowser) {
|
|
639
1308
|
throw new DialgemError(
|
|
640
1309
|
"The Dialgem SDK is for server-side use: running it in a browser exposes your API key to every visitor. Call Dialgem from your backend instead, or pass `dangerouslyAllowBrowser: true` if you understand the risk."
|
|
@@ -651,10 +1320,9 @@ var Dialgem = class {
|
|
|
651
1320
|
assertTimeout(timeout);
|
|
652
1321
|
assertMaxRetries(maxRetries);
|
|
653
1322
|
const fetchImpl = options.fetch ?? defaultFetch();
|
|
654
|
-
this.baseURL = (options.baseURL ?? readEnv("DIALGEM_BASE_URL") ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
655
1323
|
const http = new HttpClient({
|
|
656
1324
|
apiKey,
|
|
657
|
-
baseURL:
|
|
1325
|
+
baseURL: BASE_URL,
|
|
658
1326
|
timeout,
|
|
659
1327
|
maxRetries,
|
|
660
1328
|
fetch: fetchImpl,
|
|
@@ -664,8 +1332,18 @@ var Dialgem = class {
|
|
|
664
1332
|
this.calls = new Calls(http);
|
|
665
1333
|
this.phoneNumbers = new PhoneNumbers(http);
|
|
666
1334
|
this.voices = new Voices(http);
|
|
1335
|
+
this.extractionModels = new ExtractionModels(http);
|
|
667
1336
|
this.flowTemplates = new FlowTemplates(http);
|
|
668
1337
|
this.promptTemplates = new PromptTemplates(http);
|
|
1338
|
+
this.sessions = new Sessions(http);
|
|
1339
|
+
this.chats = new Chats(http);
|
|
1340
|
+
this.flows = new Flows(http);
|
|
1341
|
+
this.llms = new LLMs(http);
|
|
1342
|
+
this.apiKey = new ApiKey(http);
|
|
1343
|
+
this.contacts = new Contacts(http);
|
|
1344
|
+
this.contactLists = new ContactLists(http);
|
|
1345
|
+
this.webCalls = new WebCalls(http);
|
|
1346
|
+
this.simulations = new Simulations(http);
|
|
669
1347
|
}
|
|
670
1348
|
};
|
|
671
1349
|
function readEnv(name) {
|
|
@@ -690,9 +1368,10 @@ exports.APIConnectionTimeoutError = APIConnectionTimeoutError;
|
|
|
690
1368
|
exports.APIError = APIError;
|
|
691
1369
|
exports.APIUserAbortError = APIUserAbortError;
|
|
692
1370
|
exports.AuthenticationError = AuthenticationError;
|
|
1371
|
+
exports.BASE_URL = BASE_URL;
|
|
693
1372
|
exports.BadRequestError = BadRequestError;
|
|
694
1373
|
exports.ConflictError = ConflictError;
|
|
695
|
-
exports.
|
|
1374
|
+
exports.DEFAULT_SIGNATURE_TOLERANCE_MS = DEFAULT_SIGNATURE_TOLERANCE_MS;
|
|
696
1375
|
exports.Dialgem = Dialgem;
|
|
697
1376
|
exports.DialgemError = DialgemError;
|
|
698
1377
|
exports.InternalServerError = InternalServerError;
|
|
@@ -703,7 +1382,9 @@ exports.PermissionDeniedError = PermissionDeniedError;
|
|
|
703
1382
|
exports.RateLimitError = RateLimitError;
|
|
704
1383
|
exports.UnprocessableEntityError = UnprocessableEntityError;
|
|
705
1384
|
exports.VERSION = VERSION;
|
|
1385
|
+
exports.WEBHOOK_DELIVERY_HEADER = WEBHOOK_DELIVERY_HEADER;
|
|
706
1386
|
exports.WEBHOOK_EVENT_HEADER = WEBHOOK_EVENT_HEADER;
|
|
1387
|
+
exports.WEBHOOK_PREVIOUS_SIGNATURE_HEADER = WEBHOOK_PREVIOUS_SIGNATURE_HEADER;
|
|
707
1388
|
exports.WEBHOOK_SIGNATURE_HEADER = WEBHOOK_SIGNATURE_HEADER;
|
|
708
1389
|
exports.WebhookVerificationError = WebhookVerificationError;
|
|
709
1390
|
exports.Webhooks = Webhooks;
|