@niadra/sdk 0.9.0 → 0.10.3
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 +71 -0
- package/README.md +40 -1
- package/dist/ai-sdk.d.cts +3 -3
- package/dist/ai-sdk.d.ts +3 -3
- package/dist/anthropic.d.cts +4 -4
- package/dist/anthropic.d.ts +4 -4
- package/dist/bedrock.d.cts +4 -4
- package/dist/bedrock.d.ts +4 -4
- package/dist/cli.js +368 -104
- package/dist/cli.js.map +1 -1
- package/dist/{client-B_ip8T2o.d.cts → client-rmWhZJOx.d.cts} +212 -21
- package/dist/{client-B_ip8T2o.d.ts → client-rmWhZJOx.d.ts} +212 -21
- package/dist/cloudflare-agents.d.cts +3 -3
- package/dist/cloudflare-agents.d.ts +3 -3
- package/dist/elevenlabs.cjs +7 -5
- package/dist/elevenlabs.cjs.map +1 -1
- package/dist/elevenlabs.d.cts +5 -5
- package/dist/elevenlabs.d.ts +5 -5
- package/dist/elevenlabs.js +7 -5
- package/dist/elevenlabs.js.map +1 -1
- package/dist/genkit.d.cts +3 -3
- package/dist/genkit.d.ts +3 -3
- package/dist/google-adk.cjs +7 -5
- package/dist/google-adk.cjs.map +1 -1
- package/dist/google-adk.d.cts +3 -3
- package/dist/google-adk.d.ts +3 -3
- package/dist/google-adk.js +7 -5
- package/dist/google-adk.js.map +1 -1
- package/dist/google-genai.d.cts +4 -4
- package/dist/google-genai.d.ts +4 -4
- package/dist/index.cjs +378 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +378 -106
- package/dist/index.js.map +1 -1
- package/dist/{intercept-CxmBLfoj.d.cts → intercept-C0XODsy9.d.cts} +1 -1
- package/dist/{intercept-CBaPK_-k.d.ts → intercept-veMDudKV.d.ts} +1 -1
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.d.cts +3 -3
- package/dist/langchain.d.ts +3 -3
- package/dist/langchain.js.map +1 -1
- package/dist/livekit.cjs +10 -2
- package/dist/livekit.cjs.map +1 -1
- package/dist/livekit.d.cts +3 -3
- package/dist/livekit.d.ts +3 -3
- package/dist/livekit.js +10 -2
- package/dist/livekit.js.map +1 -1
- package/dist/llamaindex.d.cts +3 -3
- package/dist/llamaindex.d.ts +3 -3
- package/dist/mastra.d.cts +3 -3
- package/dist/mastra.d.ts +3 -3
- package/dist/openai-agents.d.cts +3 -3
- package/dist/openai-agents.d.ts +3 -3
- package/dist/retell.cjs +7 -5
- package/dist/retell.cjs.map +1 -1
- package/dist/retell.d.cts +5 -5
- package/dist/retell.d.ts +5 -5
- package/dist/retell.js +7 -5
- package/dist/retell.js.map +1 -1
- package/dist/{shared-BLkAbvuf.d.ts → shared-Cs7h5C4Y.d.ts} +1 -1
- package/dist/{shared-DEkF2Y_z.d.cts → shared-zaTBum17.d.cts} +1 -1
- package/dist/strands.d.cts +3 -3
- package/dist/strands.d.ts +3 -3
- package/dist/twilio.cjs +10 -2
- package/dist/twilio.cjs.map +1 -1
- package/dist/twilio.d.cts +4 -4
- package/dist/twilio.d.ts +4 -4
- package/dist/twilio.js +10 -2
- package/dist/twilio.js.map +1 -1
- package/dist/vapi.cjs +7 -5
- package/dist/vapi.cjs.map +1 -1
- package/dist/vapi.d.cts +5 -5
- package/dist/vapi.d.ts +5 -5
- package/dist/vapi.js +7 -5
- package/dist/vapi.js.map +1 -1
- package/dist/voltagent.d.cts +3 -3
- package/dist/voltagent.d.ts +3 -3
- package/dist/{webhook-BaraTaA9.d.ts → webhook-2V5QgnKI.d.ts} +1 -1
- package/dist/{webhook-CuR0Wckd.d.cts → webhook-CX7nvQig.d.cts} +1 -1
- package/dist/whatsapp.cjs +10 -2
- package/dist/whatsapp.cjs.map +1 -1
- package/dist/whatsapp.d.cts +2 -2
- package/dist/whatsapp.d.ts +2 -2
- package/dist/whatsapp.js +10 -2
- package/dist/whatsapp.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,46 @@ var __export = (target, all) => {
|
|
|
6
6
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
// src/ids.ts
|
|
10
|
+
var lastMs = -1;
|
|
11
|
+
var counter = 0;
|
|
12
|
+
function randomBytes(length) {
|
|
13
|
+
const bytes = new Uint8Array(length);
|
|
14
|
+
const webCrypto = globalThis.crypto;
|
|
15
|
+
if (webCrypto?.getRandomValues) {
|
|
16
|
+
webCrypto.getRandomValues(bytes);
|
|
17
|
+
} else {
|
|
18
|
+
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
19
|
+
}
|
|
20
|
+
return bytes;
|
|
21
|
+
}
|
|
22
|
+
function uuidv7(now = Date.now()) {
|
|
23
|
+
let ms = now;
|
|
24
|
+
if (ms <= lastMs) {
|
|
25
|
+
ms = lastMs;
|
|
26
|
+
counter = counter + 1 & 4095;
|
|
27
|
+
if (counter === 0) ms += 1;
|
|
28
|
+
} else {
|
|
29
|
+
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
30
|
+
}
|
|
31
|
+
lastMs = ms;
|
|
32
|
+
const bytes = randomBytes(16);
|
|
33
|
+
const high = Math.floor(ms / 2 ** 16);
|
|
34
|
+
const low = ms % 2 ** 16;
|
|
35
|
+
bytes[0] = high >>> 24 & 255;
|
|
36
|
+
bytes[1] = high >>> 16 & 255;
|
|
37
|
+
bytes[2] = high >>> 8 & 255;
|
|
38
|
+
bytes[3] = high & 255;
|
|
39
|
+
bytes[4] = low >>> 8 & 255;
|
|
40
|
+
bytes[5] = low & 255;
|
|
41
|
+
bytes[6] = 112 | counter >>> 8 & 15;
|
|
42
|
+
bytes[7] = counter & 255;
|
|
43
|
+
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
44
|
+
let hex2 = "";
|
|
45
|
+
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
46
|
+
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
9
49
|
// src/errors.ts
|
|
10
50
|
var NiadraError = class extends Error {
|
|
11
51
|
name = "NiadraError";
|
|
@@ -49,11 +89,12 @@ var NiadraAPIError = class extends NiadraError {
|
|
|
49
89
|
constructor(status, problem, requestId, retryAfterMs = null) {
|
|
50
90
|
const code = problem?.code ?? `http_${status}`;
|
|
51
91
|
const detail = problem?.detail ? `: ${problem.detail}` : "";
|
|
52
|
-
|
|
92
|
+
const id = requestId ?? problem?.request_id ?? null;
|
|
93
|
+
super(`${status} ${code}${detail}${id ? ` (request ${id})` : ""}`);
|
|
53
94
|
this.status = status;
|
|
54
95
|
this.code = code;
|
|
55
96
|
this.problem = problem;
|
|
56
|
-
this.requestId =
|
|
97
|
+
this.requestId = id;
|
|
57
98
|
this.retryAfterMs = retryAfterMs;
|
|
58
99
|
}
|
|
59
100
|
};
|
|
@@ -77,45 +118,11 @@ function toNiadraError(error) {
|
|
|
77
118
|
const message = error instanceof Error ? error.message : String(error);
|
|
78
119
|
return new NiadraError(message, { cause: error });
|
|
79
120
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const bytes = new Uint8Array(length);
|
|
86
|
-
const webCrypto = globalThis.crypto;
|
|
87
|
-
if (webCrypto?.getRandomValues) {
|
|
88
|
-
webCrypto.getRandomValues(bytes);
|
|
89
|
-
} else {
|
|
90
|
-
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
91
|
-
}
|
|
92
|
-
return bytes;
|
|
93
|
-
}
|
|
94
|
-
function uuidv7(now = Date.now()) {
|
|
95
|
-
let ms = now;
|
|
96
|
-
if (ms <= lastMs) {
|
|
97
|
-
ms = lastMs;
|
|
98
|
-
counter = counter + 1 & 4095;
|
|
99
|
-
if (counter === 0) ms += 1;
|
|
100
|
-
} else {
|
|
101
|
-
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
102
|
-
}
|
|
103
|
-
lastMs = ms;
|
|
104
|
-
const bytes = randomBytes(16);
|
|
105
|
-
const high = Math.floor(ms / 2 ** 16);
|
|
106
|
-
const low = ms % 2 ** 16;
|
|
107
|
-
bytes[0] = high >>> 24 & 255;
|
|
108
|
-
bytes[1] = high >>> 16 & 255;
|
|
109
|
-
bytes[2] = high >>> 8 & 255;
|
|
110
|
-
bytes[3] = high & 255;
|
|
111
|
-
bytes[4] = low >>> 8 & 255;
|
|
112
|
-
bytes[5] = low & 255;
|
|
113
|
-
bytes[6] = 112 | counter >>> 8 & 15;
|
|
114
|
-
bytes[7] = counter & 255;
|
|
115
|
-
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
116
|
-
let hex2 = "";
|
|
117
|
-
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
118
|
-
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
121
|
+
function explain(error) {
|
|
122
|
+
if (!(error instanceof NiadraAPIError)) return error instanceof Error ? error.name : String(error);
|
|
123
|
+
const detail = error.problem?.detail?.trim();
|
|
124
|
+
const said = `${error.status} ${error.code}${detail ? `: ${detail.slice(0, 300)}` : ""}`;
|
|
125
|
+
return error.requestId ? `${said} (request ${error.requestId})` : said;
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
// src/admin.ts
|
|
@@ -518,7 +525,7 @@ function internalRecord(span, ref, act) {
|
|
|
518
525
|
}
|
|
519
526
|
|
|
520
527
|
// src/version.ts
|
|
521
|
-
var VERSION = "0.
|
|
528
|
+
var VERSION = "0.10.3";
|
|
522
529
|
|
|
523
530
|
// src/transport.ts
|
|
524
531
|
var RETRYABLE_WRITE_STATUS = /* @__PURE__ */ new Set([408, 421, 429, 500, 502, 503, 504]);
|
|
@@ -531,8 +538,32 @@ var Transport = class {
|
|
|
531
538
|
}
|
|
532
539
|
config;
|
|
533
540
|
baseURL;
|
|
541
|
+
answeredAt;
|
|
542
|
+
/** When the allowance was first given since the last answer; it holds for the calls of that moment only. */
|
|
543
|
+
grantedAt;
|
|
544
|
+
/** When the client last sent a request of its own (`RequestSpec.activity`), in epoch milliseconds. */
|
|
545
|
+
lastActivityAt;
|
|
534
546
|
async request(spec) {
|
|
535
|
-
|
|
547
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
548
|
+
const budgeted = this.withAllowance(spec);
|
|
549
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
553
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
554
|
+
* outage costs the allowance once, not on every turn.
|
|
555
|
+
*/
|
|
556
|
+
withAllowance(spec) {
|
|
557
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
558
|
+
const { retry } = spec;
|
|
559
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
560
|
+
const now = Date.now();
|
|
561
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
562
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
563
|
+
this.grantedAt ??= now;
|
|
564
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
565
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
566
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
536
567
|
}
|
|
537
568
|
async read(spec, policy) {
|
|
538
569
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -603,7 +634,18 @@ var Transport = class {
|
|
|
603
634
|
headers["content-type"] = "application/json";
|
|
604
635
|
init.body = JSON.stringify(spec.body);
|
|
605
636
|
}
|
|
606
|
-
|
|
637
|
+
try {
|
|
638
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
639
|
+
this.answered();
|
|
640
|
+
return answer;
|
|
641
|
+
} catch (error) {
|
|
642
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
643
|
+
throw error;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
answered() {
|
|
647
|
+
this.answeredAt = Date.now();
|
|
648
|
+
this.grantedAt = void 0;
|
|
607
649
|
}
|
|
608
650
|
async exchange(url, init, deadline) {
|
|
609
651
|
let response;
|
|
@@ -612,6 +654,7 @@ var Transport = class {
|
|
|
612
654
|
} catch (error) {
|
|
613
655
|
throw deadline.explain(error);
|
|
614
656
|
}
|
|
657
|
+
warnIfDeprecated(this.config.logger, init.method ?? "GET", url, response.headers);
|
|
615
658
|
const requestId = response.headers.get("x-request-id");
|
|
616
659
|
let payload;
|
|
617
660
|
try {
|
|
@@ -681,6 +724,29 @@ var Deadline = class {
|
|
|
681
724
|
return new NiadraConnectionError(`connection failed: ${message}`, { cause: error });
|
|
682
725
|
}
|
|
683
726
|
};
|
|
727
|
+
var VERSIONING_DOCS = "https://docs.niadra.com/en/security/api-versioning";
|
|
728
|
+
var DEPRECATION_LINK = /<([^>]*)>[^,]*;\s*rel="?deprecation"?/gi;
|
|
729
|
+
var deprecationsSeen = /* @__PURE__ */ new Set();
|
|
730
|
+
function warnIfDeprecated(logger, method, url, headers) {
|
|
731
|
+
const since = headers.get("deprecation");
|
|
732
|
+
if (since === null) return;
|
|
733
|
+
const links = [...(headers.get("link") ?? "").matchAll(DEPRECATION_LINK)].map((match) => match[1] ?? "");
|
|
734
|
+
const key2 = `${method} ${links.join(" ") || (url.split("?")[0] ?? url)}`;
|
|
735
|
+
if (deprecationsSeen.has(key2)) return;
|
|
736
|
+
deprecationsSeen.add(key2);
|
|
737
|
+
const sunset = headers.get("sunset");
|
|
738
|
+
logger.warn(
|
|
739
|
+
`the API deprecated a ${method} route this client calls, since ${deprecatedSince(since)}; it stops answering on ${sunset === null ? "a date not announced yet" : sunsetDay(sunset)}. See ${links.join(", ") || VERSIONING_DOCS}`
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
function deprecatedSince(value) {
|
|
743
|
+
const seconds = Number(value.trim().replace(/^@/, ""));
|
|
744
|
+
return Number.isInteger(seconds) ? new Date(seconds * 1e3).toISOString().slice(0, 10) : value;
|
|
745
|
+
}
|
|
746
|
+
function sunsetDay(value) {
|
|
747
|
+
const time = Date.parse(value);
|
|
748
|
+
return Number.isNaN(time) ? value : new Date(time).toISOString().slice(0, 10);
|
|
749
|
+
}
|
|
684
750
|
async function readBody(response) {
|
|
685
751
|
const text2 = await response.text();
|
|
686
752
|
if (text2.length === 0) return null;
|
|
@@ -4020,8 +4086,7 @@ var TurnSender = class {
|
|
|
4020
4086
|
return true;
|
|
4021
4087
|
}
|
|
4022
4088
|
}
|
|
4023
|
-
|
|
4024
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4089
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4025
4090
|
return true;
|
|
4026
4091
|
}
|
|
4027
4092
|
accepted(batch, answer) {
|
|
@@ -4029,8 +4094,8 @@ var TurnSender = class {
|
|
|
4029
4094
|
this.resumeAt = 0;
|
|
4030
4095
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4031
4096
|
const again = [];
|
|
4032
|
-
const
|
|
4033
|
-
let
|
|
4097
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4098
|
+
let refused3 = 0;
|
|
4034
4099
|
for (const error of answer.errors ?? []) {
|
|
4035
4100
|
const frame = batch.frames[error.index];
|
|
4036
4101
|
if (frame === void 0) continue;
|
|
@@ -4039,12 +4104,12 @@ var TurnSender = class {
|
|
|
4039
4104
|
this.recorder.modeRefused();
|
|
4040
4105
|
again.push(frame);
|
|
4041
4106
|
} else {
|
|
4042
|
-
|
|
4043
|
-
|
|
4107
|
+
refused3++;
|
|
4108
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4044
4109
|
}
|
|
4045
4110
|
}
|
|
4046
4111
|
if (again.length > 0) this.queue.requeue(again);
|
|
4047
|
-
if (
|
|
4112
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4048
4113
|
}
|
|
4049
4114
|
};
|
|
4050
4115
|
|
|
@@ -4166,9 +4231,12 @@ var TurnRecorder = class {
|
|
|
4166
4231
|
this.accepted += accepted;
|
|
4167
4232
|
this.duplicates += duplicates;
|
|
4168
4233
|
}
|
|
4169
|
-
|
|
4234
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4235
|
+
rejected(count3, reasons) {
|
|
4170
4236
|
this.rejectedTurns += count3;
|
|
4171
|
-
|
|
4237
|
+
const shown = [...new Set(reasons)].sort();
|
|
4238
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4239
|
+
this.logger.warn(`${count3} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4172
4240
|
}
|
|
4173
4241
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4174
4242
|
notRecorded(count3, off) {
|
|
@@ -4238,6 +4306,10 @@ async function hex(text2) {
|
|
|
4238
4306
|
}
|
|
4239
4307
|
|
|
4240
4308
|
// src/coordination/client.ts
|
|
4309
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4310
|
+
function refused(error) {
|
|
4311
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4312
|
+
}
|
|
4241
4313
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4242
4314
|
var CHECK_BUDGET_MS = 200;
|
|
4243
4315
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4259,15 +4331,22 @@ function claimed(data, error) {
|
|
|
4259
4331
|
return { held: false, claim: null, error: code };
|
|
4260
4332
|
}
|
|
4261
4333
|
var Coordinator = class {
|
|
4262
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4334
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4263
4335
|
this.outbox = outbox;
|
|
4264
4336
|
this.suppressions = suppressions;
|
|
4265
4337
|
this.declareNow = declareNow;
|
|
4338
|
+
this.logger = logger;
|
|
4266
4339
|
}
|
|
4267
4340
|
outbox;
|
|
4268
4341
|
suppressions;
|
|
4269
4342
|
declareNow;
|
|
4343
|
+
logger;
|
|
4270
4344
|
async failed(request, failOpen, error) {
|
|
4345
|
+
if (refused(error)) {
|
|
4346
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4347
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4348
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4349
|
+
}
|
|
4271
4350
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4272
4351
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4273
4352
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4288,7 +4367,7 @@ var Coordinator = class {
|
|
|
4288
4367
|
if (who.subject) body.subject = who.subject;
|
|
4289
4368
|
if (who.object) body.object = who.object;
|
|
4290
4369
|
const key2 = uuidv7();
|
|
4291
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4370
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4292
4371
|
return key2;
|
|
4293
4372
|
}
|
|
4294
4373
|
};
|
|
@@ -4395,26 +4474,42 @@ async function suppressionKey(salt, canonical2) {
|
|
|
4395
4474
|
const key2 = await subtle.importKey("raw", saltBytes(salt), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
4396
4475
|
return toBase64url(new Uint8Array(await subtle.sign("HMAC", key2, new TextEncoder().encode(canonical2))));
|
|
4397
4476
|
}
|
|
4477
|
+
var AREA_CODES = new Set(
|
|
4478
|
+
"11 12 13 14 15 16 17 18 19 21 22 24 27 28 31 32 33 34 35 37 38 41 42 43 44 45 46 47 48 49 51 53 54 55 61 62 63 64 65 66 67 68 69 71 73 74 75 77 79 81 82 83 84 85 86 87 88 89 91 92 93 94 95 96 97 98 99".split(" ")
|
|
4479
|
+
);
|
|
4480
|
+
var FORMAT_CHARACTERS = /\p{Cf}/gu;
|
|
4398
4481
|
function phone(value) {
|
|
4399
|
-
|
|
4400
|
-
|
|
4482
|
+
let text2 = value.normalize("NFKC").replace(FORMAT_CHARACTERS, "").trim();
|
|
4483
|
+
if (text2.slice(0, 4).toLowerCase() === "tel:") text2 = text2.slice(4);
|
|
4484
|
+
const international = text2.startsWith("+") || text2.startsWith("00");
|
|
4485
|
+
if (international) text2 = text2.replaceAll("(0)", "");
|
|
4486
|
+
const raw = text2.replace(SEPARATORS2, "");
|
|
4401
4487
|
let digits = raw.startsWith("+") ? raw.slice(1) : international ? raw.slice(2) : raw;
|
|
4402
4488
|
if (!DIGITS2.test(digits)) throw new NiadraDestinationError("invalid_handle");
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4489
|
+
if (!international) {
|
|
4490
|
+
const national = digits.replace(/^0+/, "");
|
|
4491
|
+
if (isBrazilianNational(national)) {
|
|
4492
|
+
digits = `55${national}`;
|
|
4493
|
+
} else if (digits.startsWith("0") && (national.length === 12 || national.length === 13)) {
|
|
4494
|
+
if (isBrazilianNational(national.slice(2))) digits = `55${national.slice(2)}`;
|
|
4495
|
+
}
|
|
4406
4496
|
}
|
|
4407
4497
|
if (digits.length < 8 || digits.length > 15 || digits.startsWith("0")) {
|
|
4408
4498
|
throw new NiadraDestinationError("invalid_handle");
|
|
4409
4499
|
}
|
|
4410
4500
|
const rest = digits.slice(2);
|
|
4411
|
-
if (digits.startsWith("55") && rest.length === 10 &&
|
|
4501
|
+
if (digits.startsWith("55") && rest.length === 10 && AREA_CODES.has(rest.slice(0, 2)) && "6789".includes(rest.charAt(2))) {
|
|
4412
4502
|
digits = `55${rest.slice(0, 2)}9${rest.slice(2)}`;
|
|
4413
4503
|
}
|
|
4504
|
+
if (digits.length === 13 && (digits.startsWith("521") || digits.startsWith("549"))) {
|
|
4505
|
+
digits = digits.slice(0, 2) + digits.slice(3);
|
|
4506
|
+
}
|
|
4414
4507
|
return `phone:+${digits}`;
|
|
4415
4508
|
}
|
|
4416
|
-
function
|
|
4417
|
-
|
|
4509
|
+
function isBrazilianNational(national) {
|
|
4510
|
+
if (!AREA_CODES.has(national.slice(0, 2))) return false;
|
|
4511
|
+
if (national.length === 10) return "23456789".includes(national.charAt(2));
|
|
4512
|
+
return national.length === 11 && national.charAt(2) === "9";
|
|
4418
4513
|
}
|
|
4419
4514
|
function saltBytes(salt) {
|
|
4420
4515
|
const bytes = fromBase64url(salt);
|
|
@@ -4791,7 +4886,7 @@ var Outbox = class {
|
|
|
4791
4886
|
return false;
|
|
4792
4887
|
}
|
|
4793
4888
|
this.writes.shift();
|
|
4794
|
-
this.logger.warn(
|
|
4889
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4795
4890
|
this.settle(write, void 0, error);
|
|
4796
4891
|
return true;
|
|
4797
4892
|
}
|
|
@@ -5681,9 +5776,17 @@ var handles = {
|
|
|
5681
5776
|
appUserId: (value, options) => build("app_user_id", value, void 0, options),
|
|
5682
5777
|
/** The id of a person or organization in a system of record; `system` names that system, such as `crm`. */
|
|
5683
5778
|
systemId: (value, system, options) => build("system_id", value, system, options),
|
|
5684
|
-
/**
|
|
5779
|
+
/**
|
|
5780
|
+
* A person's national document, such as a CPF: `govIdHmac("529.982.247-25", "BR")`. Send the number
|
|
5781
|
+
* itself, never a hash of it: the server checks its check digits, then keeps only a keyed hash with your
|
|
5782
|
+
* space's secret and an encrypted copy, and shows it masked. `country` is its ISO 3166-1 alpha-2 code.
|
|
5783
|
+
*/
|
|
5685
5784
|
govIdHmac: (value, country, options) => build("gov_id_hmac", value, country, options),
|
|
5686
|
-
/**
|
|
5785
|
+
/**
|
|
5786
|
+
* A company's registry number, such as a CNPJ: `orgRegistryHmac("11.222.333/0001-81", "BR")`. As with
|
|
5787
|
+
* `govIdHmac`, send the number itself; the server checks it and keeps only a keyed hash. Identifies an
|
|
5788
|
+
* organization.
|
|
5789
|
+
*/
|
|
5687
5790
|
orgRegistryHmac: (value, country) => build("org_registry_hmac", value, country, { subjectKind: "account" }),
|
|
5688
5791
|
/** An e-mail domain, such as `acme.com`. Identifies an organization. */
|
|
5689
5792
|
emailDomain: (domain) => build("email_domain", domain, void 0, { subjectKind: "account" }),
|
|
@@ -6357,6 +6460,7 @@ var AgentSession = class {
|
|
|
6357
6460
|
try {
|
|
6358
6461
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6359
6462
|
} catch (error) {
|
|
6463
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6360
6464
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6361
6465
|
}
|
|
6362
6466
|
}
|
|
@@ -6968,6 +7072,7 @@ var SessionState = class {
|
|
|
6968
7072
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
6969
7073
|
guards = /* @__PURE__ */ new Map();
|
|
6970
7074
|
lastReport = null;
|
|
7075
|
+
unlinkedSaid = false;
|
|
6971
7076
|
/** After the first pack, every read also asks what changed since. */
|
|
6972
7077
|
get wantsDelta() {
|
|
6973
7078
|
return this.etag !== null;
|
|
@@ -6996,6 +7101,19 @@ var SessionState = class {
|
|
|
6996
7101
|
this.last = absorbed;
|
|
6997
7102
|
return absorbed;
|
|
6998
7103
|
}
|
|
7104
|
+
/**
|
|
7105
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7106
|
+
* on with the subject's own memory, never the organization's.
|
|
7107
|
+
*/
|
|
7108
|
+
sayUnlinked(result2, logger) {
|
|
7109
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7110
|
+
this.unlinkedSaid = true;
|
|
7111
|
+
logger.warn(
|
|
7112
|
+
"`about` names an organization with no active link to the subject; the context is the subject's own, without that organization's memory. Create the link to read it."
|
|
7113
|
+
);
|
|
7114
|
+
}
|
|
7115
|
+
return result2;
|
|
7116
|
+
}
|
|
6999
7117
|
/** The last backing check of an agent's answer. */
|
|
7000
7118
|
get lastBacking() {
|
|
7001
7119
|
return this.lastReport;
|
|
@@ -7243,7 +7361,7 @@ var Conversation = class {
|
|
|
7243
7361
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7244
7362
|
*/
|
|
7245
7363
|
async context(options = {}) {
|
|
7246
|
-
const { query, turn, format, explain, include, ...requestOptions } = options;
|
|
7364
|
+
const { query, turn, format, explain: explain2, include, ...requestOptions } = options;
|
|
7247
7365
|
const params = {
|
|
7248
7366
|
subject: this.subject,
|
|
7249
7367
|
view: this.view,
|
|
@@ -7252,7 +7370,7 @@ var Conversation = class {
|
|
|
7252
7370
|
...this.params.about ? { about: this.params.about } : {},
|
|
7253
7371
|
...this.params.target ? { target: this.params.target } : {},
|
|
7254
7372
|
...format === "json" ? { format } : {},
|
|
7255
|
-
...
|
|
7373
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7256
7374
|
};
|
|
7257
7375
|
if (query) params.query = query;
|
|
7258
7376
|
if (include?.length) params.include = include;
|
|
@@ -7260,7 +7378,7 @@ var Conversation = class {
|
|
|
7260
7378
|
params.turn = turn === void 0 ? this.turnText : turn;
|
|
7261
7379
|
const result2 = await this.client.context(params, requestOptions);
|
|
7262
7380
|
this.features.observe(result2);
|
|
7263
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7381
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7264
7382
|
}
|
|
7265
7383
|
/**
|
|
7266
7384
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7440,6 +7558,35 @@ function withHandles(base, extra) {
|
|
|
7440
7558
|
return all;
|
|
7441
7559
|
}
|
|
7442
7560
|
|
|
7561
|
+
// src/warm.ts
|
|
7562
|
+
var EVERY_MS = 1e5;
|
|
7563
|
+
var IDLE_MS = 9e4;
|
|
7564
|
+
var WARM_FOR_MS = 6e5;
|
|
7565
|
+
var KeepWarm = class {
|
|
7566
|
+
constructor(enabled) {
|
|
7567
|
+
this.enabled = enabled;
|
|
7568
|
+
}
|
|
7569
|
+
enabled;
|
|
7570
|
+
open = /* @__PURE__ */ new Map();
|
|
7571
|
+
openedAt = 0;
|
|
7572
|
+
/** Notes an open conversation or task. */
|
|
7573
|
+
add(scope, session, now) {
|
|
7574
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7575
|
+
this.open.set(scope, new WeakRef(session));
|
|
7576
|
+
this.openedAt = now;
|
|
7577
|
+
}
|
|
7578
|
+
/** The conversation or task ended. */
|
|
7579
|
+
end(scope) {
|
|
7580
|
+
this.open.delete(scope);
|
|
7581
|
+
}
|
|
7582
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7583
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7584
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7585
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7586
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7587
|
+
}
|
|
7588
|
+
};
|
|
7589
|
+
|
|
7443
7590
|
// src/exit.ts
|
|
7444
7591
|
var registered = /* @__PURE__ */ new Set();
|
|
7445
7592
|
var installedOn = null;
|
|
@@ -7570,8 +7717,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7570
7717
|
write: 5e3,
|
|
7571
7718
|
token: 2e3,
|
|
7572
7719
|
upload: 6e4,
|
|
7573
|
-
prefetch: 1e3
|
|
7720
|
+
prefetch: 1e3,
|
|
7721
|
+
connect: 1e3
|
|
7574
7722
|
};
|
|
7723
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7575
7724
|
var DEFAULT_CACHE = {
|
|
7576
7725
|
ttlMs: 1e4,
|
|
7577
7726
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7678,7 +7827,7 @@ var Task = class {
|
|
|
7678
7827
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7679
7828
|
*/
|
|
7680
7829
|
async context(options = {}) {
|
|
7681
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7830
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7682
7831
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7683
7832
|
const params = {
|
|
7684
7833
|
...target,
|
|
@@ -7688,18 +7837,18 @@ var Task = class {
|
|
|
7688
7837
|
...this.level ? { verification: this.level } : {},
|
|
7689
7838
|
...this.params.target ? { target: this.params.target } : {},
|
|
7690
7839
|
...format === "json" ? { format } : {},
|
|
7691
|
-
...
|
|
7840
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7692
7841
|
...include?.length ? { include } : {}
|
|
7693
7842
|
};
|
|
7694
7843
|
if (query) {
|
|
7695
7844
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7696
7845
|
this.features.observe(answered);
|
|
7697
|
-
return this.state.observe(answered);
|
|
7846
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7698
7847
|
}
|
|
7699
7848
|
if (this.state.wantsDelta) params.delta = true;
|
|
7700
7849
|
const result2 = await this.client.context(params, requestOptions);
|
|
7701
7850
|
this.features.observe(result2);
|
|
7702
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7851
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7703
7852
|
}
|
|
7704
7853
|
/**
|
|
7705
7854
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7829,14 +7978,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7829
7978
|
search: "search_agent_memory",
|
|
7830
7979
|
remember: "remember"
|
|
7831
7980
|
};
|
|
7832
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7981
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7833
7982
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7834
7983
|
var TOOL_DEFINITIONS = [
|
|
7835
7984
|
{
|
|
7836
7985
|
"type": "function",
|
|
7837
7986
|
"function": {
|
|
7838
7987
|
"name": "search_customer_history",
|
|
7839
|
-
"description": "Search everything that already happened with this customer: past conversations, promises, agent actions, orders and invoices. Use it when the customer refers to something earlier or asks whether a problem happened before. Do not call it when the answer is already in the context block, including its '
|
|
7988
|
+
"description": "Search everything that already happened with this customer: past conversations, promises, agent actions, orders and invoices. Use it when the customer refers to something earlier or asks whether a problem happened before. Do not call it when the answer is already in the context block, including its 'Hist\xF3rico' line, which already counts recurrences. Returns short items with date, channel and outcome, plus a recurrence count. Open a conversation (`episode:`) or an object (`object:`) with open_history_item; the other items are complete as listed.",
|
|
7840
7989
|
"parameters": {
|
|
7841
7990
|
"type": "object",
|
|
7842
7991
|
"properties": {
|
|
@@ -7880,7 +8029,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7880
8029
|
"open_item",
|
|
7881
8030
|
"action",
|
|
7882
8031
|
"object",
|
|
7883
|
-
"trait"
|
|
8032
|
+
"trait",
|
|
8033
|
+
"system_event"
|
|
7884
8034
|
]
|
|
7885
8035
|
}
|
|
7886
8036
|
},
|
|
@@ -7907,7 +8057,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7907
8057
|
"type": "function",
|
|
7908
8058
|
"function": {
|
|
7909
8059
|
"name": "get_customer_timeline",
|
|
7910
|
-
"description": "List this customer's conversations
|
|
8060
|
+
"description": "List this customer's conversations, agent actions and system events in order, newest first, one line each. Use it to leaf through the history when you do not know what to search for; filters.item_kinds also lists the open items, facts, patterns or objects it names. Pass next_cursor to continue. Prefer search_customer_history for a specific question.",
|
|
7911
8061
|
"parameters": {
|
|
7912
8062
|
"type": "object",
|
|
7913
8063
|
"properties": {
|
|
@@ -7955,7 +8105,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7955
8105
|
"open_item",
|
|
7956
8106
|
"action",
|
|
7957
8107
|
"object",
|
|
7958
|
-
"trait"
|
|
8108
|
+
"trait",
|
|
8109
|
+
"system_event"
|
|
7959
8110
|
]
|
|
7960
8111
|
}
|
|
7961
8112
|
},
|
|
@@ -7974,7 +8125,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7974
8125
|
"type": "function",
|
|
7975
8126
|
"function": {
|
|
7976
8127
|
"name": "open_history_item",
|
|
7977
|
-
"description": "Open one conversation or business object returned by search_customer_history or get_customer_timeline: what was asked, what was promised and by whom, the outcome and what memory came from it. Use it only after a search or timeline pointed to the item.",
|
|
8128
|
+
"description": "Open one conversation (`episode:`) or business object (`object:`) returned by search_customer_history or get_customer_timeline: what was asked, what was promised and by whom, the outcome and what memory came from it. Actions, events, facts and promises are complete as listed and do not open. Use it only after a search or timeline pointed to the item.",
|
|
7978
8129
|
"parameters": {
|
|
7979
8130
|
"type": "object",
|
|
7980
8131
|
"properties": {
|
|
@@ -8345,6 +8496,13 @@ function compose(body, read) {
|
|
|
8345
8496
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8346
8497
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8347
8498
|
}
|
|
8499
|
+
var RTT_MARGIN_MS = 50;
|
|
8500
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8501
|
+
const ms = Math.round(rttMs);
|
|
8502
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8503
|
+
(name) => `timeouts.${name} (${timeouts[name]} ms) is shorter than the round trip to the region (${ms} ms) plus ${RTT_MARGIN_MS} ms for the API: its reads will run out of time. Leave it at its default, which adds the measured round trip, or raise it`
|
|
8504
|
+
);
|
|
8505
|
+
}
|
|
8348
8506
|
function rttWarnings(rttMs, timeouts) {
|
|
8349
8507
|
const ms = Math.round(rttMs);
|
|
8350
8508
|
const found2 = [];
|
|
@@ -8382,6 +8540,12 @@ var Niadra = class _Niadra {
|
|
|
8382
8540
|
enabled;
|
|
8383
8541
|
core;
|
|
8384
8542
|
timeouts;
|
|
8543
|
+
keepWarm;
|
|
8544
|
+
warmTimer;
|
|
8545
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8546
|
+
defaultReads;
|
|
8547
|
+
voiceStarted = false;
|
|
8548
|
+
voiceWarned = false;
|
|
8385
8549
|
strict;
|
|
8386
8550
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8387
8551
|
logger;
|
|
@@ -8426,6 +8590,8 @@ var Niadra = class _Niadra {
|
|
|
8426
8590
|
this.strict = options.strict ?? false;
|
|
8427
8591
|
this.logger = options.logger ?? consoleLogger;
|
|
8428
8592
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8593
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8594
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8429
8595
|
this.voice = new VoiceLines(
|
|
8430
8596
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8431
8597
|
);
|
|
@@ -8451,13 +8617,14 @@ var Niadra = class _Niadra {
|
|
|
8451
8617
|
this.coordinator = new Coordinator(
|
|
8452
8618
|
this.outbox,
|
|
8453
8619
|
this.suppressions,
|
|
8454
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8620
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8621
|
+
this.logger
|
|
8455
8622
|
);
|
|
8456
8623
|
this.states = new AgentStates(
|
|
8457
8624
|
this.outbox,
|
|
8458
8625
|
{
|
|
8459
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8460
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8626
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8627
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8461
8628
|
},
|
|
8462
8629
|
this.logger
|
|
8463
8630
|
);
|
|
@@ -8484,6 +8651,7 @@ var Niadra = class _Niadra {
|
|
|
8484
8651
|
this.core = setup;
|
|
8485
8652
|
this.enabled = true;
|
|
8486
8653
|
this.disabledReason = null;
|
|
8654
|
+
this.probe(setup);
|
|
8487
8655
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8488
8656
|
}
|
|
8489
8657
|
setup(options) {
|
|
@@ -8505,7 +8673,10 @@ var Niadra = class _Niadra {
|
|
|
8505
8673
|
baseURL,
|
|
8506
8674
|
apiKey,
|
|
8507
8675
|
fetch: fetchImpl,
|
|
8508
|
-
defaultHeaders: options.defaultHeaders ?? {}
|
|
8676
|
+
defaultHeaders: options.defaultHeaders ?? {},
|
|
8677
|
+
logger: this.logger,
|
|
8678
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8679
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8509
8680
|
});
|
|
8510
8681
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8511
8682
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8561,7 +8732,7 @@ var Niadra = class _Niadra {
|
|
|
8561
8732
|
return result2;
|
|
8562
8733
|
}
|
|
8563
8734
|
readContext(core, params, request, options) {
|
|
8564
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8735
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8565
8736
|
const { query: own3, ...pinned2 } = request;
|
|
8566
8737
|
const query = turnText(own3 ?? params.turn);
|
|
8567
8738
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8577,7 +8748,7 @@ var Niadra = class _Niadra {
|
|
|
8577
8748
|
*/
|
|
8578
8749
|
async profile() {
|
|
8579
8750
|
if (!this.core) return null;
|
|
8580
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8751
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8581
8752
|
}
|
|
8582
8753
|
/**
|
|
8583
8754
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8602,7 +8773,7 @@ var Niadra = class _Niadra {
|
|
|
8602
8773
|
*/
|
|
8603
8774
|
async mayContact(handle, purpose, options = {}) {
|
|
8604
8775
|
if (this.core && this.suppressions.due()) {
|
|
8605
|
-
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8776
|
+
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8606
8777
|
if (!this.suppressions.held) await read;
|
|
8607
8778
|
}
|
|
8608
8779
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8612,8 +8783,8 @@ var Niadra = class _Niadra {
|
|
|
8612
8783
|
readSuppressions(budgetMs) {
|
|
8613
8784
|
return this.suppressions.read(
|
|
8614
8785
|
{
|
|
8615
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8616
|
-
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.
|
|
8786
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8787
|
+
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.readBudget("navigation") })
|
|
8617
8788
|
},
|
|
8618
8789
|
budgetMs
|
|
8619
8790
|
);
|
|
@@ -8692,6 +8863,7 @@ var Niadra = class _Niadra {
|
|
|
8692
8863
|
}
|
|
8693
8864
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8694
8865
|
get agentHost() {
|
|
8866
|
+
const client = this;
|
|
8695
8867
|
return {
|
|
8696
8868
|
recorder: this.turns,
|
|
8697
8869
|
coordinator: this.coordinator,
|
|
@@ -8705,7 +8877,10 @@ var Niadra = class _Niadra {
|
|
|
8705
8877
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8706
8878
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8707
8879
|
enabled: this.enabled,
|
|
8708
|
-
|
|
8880
|
+
strict: this.strict,
|
|
8881
|
+
get navigationMs() {
|
|
8882
|
+
return client.readBudget("navigation");
|
|
8883
|
+
}
|
|
8709
8884
|
};
|
|
8710
8885
|
}
|
|
8711
8886
|
/**
|
|
@@ -8733,7 +8908,7 @@ var Niadra = class _Niadra {
|
|
|
8733
8908
|
if (!cache) return false;
|
|
8734
8909
|
const key2 = cacheKey(request);
|
|
8735
8910
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8736
|
-
this.
|
|
8911
|
+
this.startVoice(core);
|
|
8737
8912
|
line.request = request;
|
|
8738
8913
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8739
8914
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8870,13 +9045,13 @@ var Niadra = class _Niadra {
|
|
|
8870
9045
|
if (!params.query || params.query.length > 2e3) {
|
|
8871
9046
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8872
9047
|
}
|
|
8873
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
9048
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8874
9049
|
});
|
|
8875
9050
|
}
|
|
8876
9051
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8877
9052
|
async timeline(params, options = {}) {
|
|
8878
9053
|
return this.navigate(
|
|
8879
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
9054
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8880
9055
|
);
|
|
8881
9056
|
}
|
|
8882
9057
|
/**
|
|
@@ -8889,9 +9064,10 @@ var Niadra = class _Niadra {
|
|
|
8889
9064
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8890
9065
|
const body = { item_id: id };
|
|
8891
9066
|
if (params.subject) body.subject = params.subject;
|
|
9067
|
+
if (params.about) body.about = params.about;
|
|
8892
9068
|
if (params.verification) body.verification = params.verification;
|
|
8893
9069
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8894
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9070
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8895
9071
|
});
|
|
8896
9072
|
}
|
|
8897
9073
|
/**
|
|
@@ -8903,7 +9079,7 @@ var Niadra = class _Niadra {
|
|
|
8903
9079
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8904
9080
|
*/
|
|
8905
9081
|
async objectState(object, options = {}) {
|
|
8906
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9082
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8907
9083
|
}
|
|
8908
9084
|
/**
|
|
8909
9085
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8916,7 +9092,7 @@ var Niadra = class _Niadra {
|
|
|
8916
9092
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8917
9093
|
}
|
|
8918
9094
|
const path = `${objectPath(object)}/timeline`;
|
|
8919
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9095
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8920
9096
|
spec.query = { cursor: params.cursor, limit: String(limit3) };
|
|
8921
9097
|
return spec;
|
|
8922
9098
|
});
|
|
@@ -8935,6 +9111,7 @@ var Niadra = class _Niadra {
|
|
|
8935
9111
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8936
9112
|
open: (id, customer, bound, voice) => {
|
|
8937
9113
|
const scope = { subject: customer };
|
|
9114
|
+
if (bound.about) scope.about = bound.about;
|
|
8938
9115
|
if (bound.verification) scope.verification = bound.verification;
|
|
8939
9116
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8940
9117
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -8969,7 +9146,7 @@ var Niadra = class _Niadra {
|
|
|
8969
9146
|
const key2 = AgentMemoryCache.key(params);
|
|
8970
9147
|
const fresh = cache?.fresh(key2);
|
|
8971
9148
|
if (fresh) return blockResult(fresh, "cache");
|
|
8972
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9149
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8973
9150
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
8974
9151
|
spec.query = {
|
|
8975
9152
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -9006,7 +9183,7 @@ var Niadra = class _Niadra {
|
|
|
9006
9183
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
9007
9184
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9008
9185
|
if (params.task_id) body.task_id = params.task_id;
|
|
9009
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9186
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9010
9187
|
});
|
|
9011
9188
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9012
9189
|
}
|
|
@@ -9100,6 +9277,41 @@ var Niadra = class _Niadra {
|
|
|
9100
9277
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9101
9278
|
}
|
|
9102
9279
|
}
|
|
9280
|
+
/**
|
|
9281
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9282
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9283
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9284
|
+
*/
|
|
9285
|
+
contextUse(params = {}, options = {}) {
|
|
9286
|
+
return this.navigate(() => {
|
|
9287
|
+
const { group_by: groups, ...filters } = params;
|
|
9288
|
+
const query = { ...filters };
|
|
9289
|
+
if (groups?.length) query.group_by = groups;
|
|
9290
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9291
|
+
});
|
|
9292
|
+
}
|
|
9293
|
+
/**
|
|
9294
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9295
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9296
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9297
|
+
*/
|
|
9298
|
+
link(params, options = {}) {
|
|
9299
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9300
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9301
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9302
|
+
}
|
|
9303
|
+
/**
|
|
9304
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9305
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9306
|
+
*/
|
|
9307
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9308
|
+
return this.navigate(() => {
|
|
9309
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9310
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9311
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9312
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9313
|
+
});
|
|
9314
|
+
}
|
|
9103
9315
|
/**
|
|
9104
9316
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9105
9317
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9192,16 +9404,20 @@ var Niadra = class _Niadra {
|
|
|
9192
9404
|
* emits `conversation.ended` when you call `end()`.
|
|
9193
9405
|
*/
|
|
9194
9406
|
conversation(params) {
|
|
9195
|
-
|
|
9407
|
+
const conversation = new Conversation(this, params, {
|
|
9196
9408
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9197
9409
|
});
|
|
9410
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9411
|
+
return conversation;
|
|
9198
9412
|
}
|
|
9199
9413
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9200
9414
|
task(params) {
|
|
9201
|
-
|
|
9415
|
+
const task = new Task(this, params, {
|
|
9202
9416
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9203
9417
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9204
9418
|
});
|
|
9419
|
+
this.warm(`task:${task.id}`, task);
|
|
9420
|
+
return task;
|
|
9205
9421
|
}
|
|
9206
9422
|
/**
|
|
9207
9423
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9221,6 +9437,8 @@ var Niadra = class _Niadra {
|
|
|
9221
9437
|
*/
|
|
9222
9438
|
async shutdown() {
|
|
9223
9439
|
this.unregisterExit();
|
|
9440
|
+
clearInterval(this.warmTimer);
|
|
9441
|
+
this.warmTimer = void 0;
|
|
9224
9442
|
if (!this.core) return;
|
|
9225
9443
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9226
9444
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9324,9 +9542,9 @@ var Niadra = class _Niadra {
|
|
|
9324
9542
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9325
9543
|
return normalizeContext(response.data);
|
|
9326
9544
|
} catch (error) {
|
|
9327
|
-
const
|
|
9545
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9328
9546
|
const left = timeout - (Date.now() - started);
|
|
9329
|
-
if (!request.include?.length || !
|
|
9547
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9330
9548
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9331
9549
|
const { include: _dropped, ...plain2 } = request;
|
|
9332
9550
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9414,9 +9632,32 @@ var Niadra = class _Niadra {
|
|
|
9414
9632
|
const rtt = Math.min(...samples);
|
|
9415
9633
|
this.voice.rtt = rtt;
|
|
9416
9634
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9417
|
-
|
|
9635
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9636
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9637
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9418
9638
|
})();
|
|
9419
9639
|
}
|
|
9640
|
+
/**
|
|
9641
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9642
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9643
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9644
|
+
*/
|
|
9645
|
+
readBudget(name) {
|
|
9646
|
+
const rtt = this.voice.rtt;
|
|
9647
|
+
return rtt !== null && this.defaultReads.has(name) ? this.timeouts[name] + rtt : this.timeouts[name];
|
|
9648
|
+
}
|
|
9649
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9650
|
+
startVoice(core) {
|
|
9651
|
+
this.voiceStarted = true;
|
|
9652
|
+
this.probe(core);
|
|
9653
|
+
this.warnVoice();
|
|
9654
|
+
}
|
|
9655
|
+
warnVoice() {
|
|
9656
|
+
const rtt = this.voice.rtt;
|
|
9657
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9658
|
+
this.voiceWarned = true;
|
|
9659
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9660
|
+
}
|
|
9420
9661
|
/**
|
|
9421
9662
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9422
9663
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9426,7 +9667,7 @@ var Niadra = class _Niadra {
|
|
|
9426
9667
|
const key2 = cacheKey(request);
|
|
9427
9668
|
const deadline = Date.now() + timeout;
|
|
9428
9669
|
const line = this.voice.line(scope);
|
|
9429
|
-
this.
|
|
9670
|
+
this.startVoice(core);
|
|
9430
9671
|
line.request = request;
|
|
9431
9672
|
const words2 = wordsOf(query);
|
|
9432
9673
|
const background = this.timeouts.prefetch;
|
|
@@ -9612,8 +9853,39 @@ var Niadra = class _Niadra {
|
|
|
9612
9853
|
core.queue.flushInBackground(true);
|
|
9613
9854
|
});
|
|
9614
9855
|
}
|
|
9856
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9857
|
+
warm(scope, session) {
|
|
9858
|
+
const core = this.core;
|
|
9859
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9860
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9861
|
+
if (this.warmTimer !== void 0) return;
|
|
9862
|
+
const timer = setInterval(() => {
|
|
9863
|
+
this.warmTick(core);
|
|
9864
|
+
}, EVERY_MS);
|
|
9865
|
+
timer.unref?.();
|
|
9866
|
+
this.warmTimer = timer;
|
|
9867
|
+
}
|
|
9868
|
+
warmTick(core) {
|
|
9869
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9870
|
+
if (step === "stop") {
|
|
9871
|
+
clearInterval(this.warmTimer);
|
|
9872
|
+
this.warmTimer = void 0;
|
|
9873
|
+
return;
|
|
9874
|
+
}
|
|
9875
|
+
if (step !== "ping") return;
|
|
9876
|
+
core.transport.request({
|
|
9877
|
+
method: "GET",
|
|
9878
|
+
path: "/healthz",
|
|
9879
|
+
timeoutMs: 2e3,
|
|
9880
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9881
|
+
activity: false
|
|
9882
|
+
}).catch((error) => {
|
|
9883
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9884
|
+
});
|
|
9885
|
+
}
|
|
9615
9886
|
async endScope(item, scope) {
|
|
9616
9887
|
this.forgetScope(scope);
|
|
9888
|
+
this.keepWarm.end(scope);
|
|
9617
9889
|
return this.sendNow(() => item);
|
|
9618
9890
|
}
|
|
9619
9891
|
async sendBatch(transport, items2, maxAttempts, backoff) {
|
|
@@ -11468,7 +11740,7 @@ var Replayer = class {
|
|
|
11468
11740
|
try {
|
|
11469
11741
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
11470
11742
|
} catch (error) {
|
|
11471
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
11743
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
11472
11744
|
throw error;
|
|
11473
11745
|
}
|
|
11474
11746
|
let run = runOf(answer);
|
|
@@ -11571,7 +11843,7 @@ var Replayer = class {
|
|
|
11571
11843
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
11572
11844
|
}
|
|
11573
11845
|
};
|
|
11574
|
-
function
|
|
11846
|
+
function refused2(scenarioIds, results) {
|
|
11575
11847
|
const scenarios = scenarioIds.map((id) => {
|
|
11576
11848
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
11577
11849
|
return {
|