@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.js
CHANGED
|
@@ -4,6 +4,46 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// src/ids.ts
|
|
8
|
+
var lastMs = -1;
|
|
9
|
+
var counter = 0;
|
|
10
|
+
function randomBytes(length) {
|
|
11
|
+
const bytes = new Uint8Array(length);
|
|
12
|
+
const webCrypto = globalThis.crypto;
|
|
13
|
+
if (webCrypto?.getRandomValues) {
|
|
14
|
+
webCrypto.getRandomValues(bytes);
|
|
15
|
+
} else {
|
|
16
|
+
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
17
|
+
}
|
|
18
|
+
return bytes;
|
|
19
|
+
}
|
|
20
|
+
function uuidv7(now = Date.now()) {
|
|
21
|
+
let ms = now;
|
|
22
|
+
if (ms <= lastMs) {
|
|
23
|
+
ms = lastMs;
|
|
24
|
+
counter = counter + 1 & 4095;
|
|
25
|
+
if (counter === 0) ms += 1;
|
|
26
|
+
} else {
|
|
27
|
+
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
28
|
+
}
|
|
29
|
+
lastMs = ms;
|
|
30
|
+
const bytes = randomBytes(16);
|
|
31
|
+
const high = Math.floor(ms / 2 ** 16);
|
|
32
|
+
const low = ms % 2 ** 16;
|
|
33
|
+
bytes[0] = high >>> 24 & 255;
|
|
34
|
+
bytes[1] = high >>> 16 & 255;
|
|
35
|
+
bytes[2] = high >>> 8 & 255;
|
|
36
|
+
bytes[3] = high & 255;
|
|
37
|
+
bytes[4] = low >>> 8 & 255;
|
|
38
|
+
bytes[5] = low & 255;
|
|
39
|
+
bytes[6] = 112 | counter >>> 8 & 15;
|
|
40
|
+
bytes[7] = counter & 255;
|
|
41
|
+
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
42
|
+
let hex2 = "";
|
|
43
|
+
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
44
|
+
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
7
47
|
// src/errors.ts
|
|
8
48
|
var NiadraError = class extends Error {
|
|
9
49
|
name = "NiadraError";
|
|
@@ -47,11 +87,12 @@ var NiadraAPIError = class extends NiadraError {
|
|
|
47
87
|
constructor(status, problem, requestId, retryAfterMs = null) {
|
|
48
88
|
const code = problem?.code ?? `http_${status}`;
|
|
49
89
|
const detail = problem?.detail ? `: ${problem.detail}` : "";
|
|
50
|
-
|
|
90
|
+
const id = requestId ?? problem?.request_id ?? null;
|
|
91
|
+
super(`${status} ${code}${detail}${id ? ` (request ${id})` : ""}`);
|
|
51
92
|
this.status = status;
|
|
52
93
|
this.code = code;
|
|
53
94
|
this.problem = problem;
|
|
54
|
-
this.requestId =
|
|
95
|
+
this.requestId = id;
|
|
55
96
|
this.retryAfterMs = retryAfterMs;
|
|
56
97
|
}
|
|
57
98
|
};
|
|
@@ -75,45 +116,11 @@ function toNiadraError(error) {
|
|
|
75
116
|
const message = error instanceof Error ? error.message : String(error);
|
|
76
117
|
return new NiadraError(message, { cause: error });
|
|
77
118
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const bytes = new Uint8Array(length);
|
|
84
|
-
const webCrypto = globalThis.crypto;
|
|
85
|
-
if (webCrypto?.getRandomValues) {
|
|
86
|
-
webCrypto.getRandomValues(bytes);
|
|
87
|
-
} else {
|
|
88
|
-
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
89
|
-
}
|
|
90
|
-
return bytes;
|
|
91
|
-
}
|
|
92
|
-
function uuidv7(now = Date.now()) {
|
|
93
|
-
let ms = now;
|
|
94
|
-
if (ms <= lastMs) {
|
|
95
|
-
ms = lastMs;
|
|
96
|
-
counter = counter + 1 & 4095;
|
|
97
|
-
if (counter === 0) ms += 1;
|
|
98
|
-
} else {
|
|
99
|
-
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
100
|
-
}
|
|
101
|
-
lastMs = ms;
|
|
102
|
-
const bytes = randomBytes(16);
|
|
103
|
-
const high = Math.floor(ms / 2 ** 16);
|
|
104
|
-
const low = ms % 2 ** 16;
|
|
105
|
-
bytes[0] = high >>> 24 & 255;
|
|
106
|
-
bytes[1] = high >>> 16 & 255;
|
|
107
|
-
bytes[2] = high >>> 8 & 255;
|
|
108
|
-
bytes[3] = high & 255;
|
|
109
|
-
bytes[4] = low >>> 8 & 255;
|
|
110
|
-
bytes[5] = low & 255;
|
|
111
|
-
bytes[6] = 112 | counter >>> 8 & 15;
|
|
112
|
-
bytes[7] = counter & 255;
|
|
113
|
-
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
114
|
-
let hex2 = "";
|
|
115
|
-
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
116
|
-
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
119
|
+
function explain(error) {
|
|
120
|
+
if (!(error instanceof NiadraAPIError)) return error instanceof Error ? error.name : String(error);
|
|
121
|
+
const detail = error.problem?.detail?.trim();
|
|
122
|
+
const said = `${error.status} ${error.code}${detail ? `: ${detail.slice(0, 300)}` : ""}`;
|
|
123
|
+
return error.requestId ? `${said} (request ${error.requestId})` : said;
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
// src/admin.ts
|
|
@@ -516,7 +523,7 @@ function internalRecord(span, ref, act) {
|
|
|
516
523
|
}
|
|
517
524
|
|
|
518
525
|
// src/version.ts
|
|
519
|
-
var VERSION = "0.
|
|
526
|
+
var VERSION = "0.10.3";
|
|
520
527
|
|
|
521
528
|
// src/transport.ts
|
|
522
529
|
var RETRYABLE_WRITE_STATUS = /* @__PURE__ */ new Set([408, 421, 429, 500, 502, 503, 504]);
|
|
@@ -529,8 +536,32 @@ var Transport = class {
|
|
|
529
536
|
}
|
|
530
537
|
config;
|
|
531
538
|
baseURL;
|
|
539
|
+
answeredAt;
|
|
540
|
+
/** When the allowance was first given since the last answer; it holds for the calls of that moment only. */
|
|
541
|
+
grantedAt;
|
|
542
|
+
/** When the client last sent a request of its own (`RequestSpec.activity`), in epoch milliseconds. */
|
|
543
|
+
lastActivityAt;
|
|
532
544
|
async request(spec) {
|
|
533
|
-
|
|
545
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
546
|
+
const budgeted = this.withAllowance(spec);
|
|
547
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
551
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
552
|
+
* outage costs the allowance once, not on every turn.
|
|
553
|
+
*/
|
|
554
|
+
withAllowance(spec) {
|
|
555
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
556
|
+
const { retry } = spec;
|
|
557
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
558
|
+
const now = Date.now();
|
|
559
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
560
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
561
|
+
this.grantedAt ??= now;
|
|
562
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
563
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
564
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
534
565
|
}
|
|
535
566
|
async read(spec, policy) {
|
|
536
567
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -601,7 +632,18 @@ var Transport = class {
|
|
|
601
632
|
headers["content-type"] = "application/json";
|
|
602
633
|
init.body = JSON.stringify(spec.body);
|
|
603
634
|
}
|
|
604
|
-
|
|
635
|
+
try {
|
|
636
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
637
|
+
this.answered();
|
|
638
|
+
return answer;
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
641
|
+
throw error;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
answered() {
|
|
645
|
+
this.answeredAt = Date.now();
|
|
646
|
+
this.grantedAt = void 0;
|
|
605
647
|
}
|
|
606
648
|
async exchange(url, init, deadline) {
|
|
607
649
|
let response;
|
|
@@ -610,6 +652,7 @@ var Transport = class {
|
|
|
610
652
|
} catch (error) {
|
|
611
653
|
throw deadline.explain(error);
|
|
612
654
|
}
|
|
655
|
+
warnIfDeprecated(this.config.logger, init.method ?? "GET", url, response.headers);
|
|
613
656
|
const requestId = response.headers.get("x-request-id");
|
|
614
657
|
let payload;
|
|
615
658
|
try {
|
|
@@ -679,6 +722,29 @@ var Deadline = class {
|
|
|
679
722
|
return new NiadraConnectionError(`connection failed: ${message}`, { cause: error });
|
|
680
723
|
}
|
|
681
724
|
};
|
|
725
|
+
var VERSIONING_DOCS = "https://docs.niadra.com/en/security/api-versioning";
|
|
726
|
+
var DEPRECATION_LINK = /<([^>]*)>[^,]*;\s*rel="?deprecation"?/gi;
|
|
727
|
+
var deprecationsSeen = /* @__PURE__ */ new Set();
|
|
728
|
+
function warnIfDeprecated(logger, method, url, headers) {
|
|
729
|
+
const since = headers.get("deprecation");
|
|
730
|
+
if (since === null) return;
|
|
731
|
+
const links = [...(headers.get("link") ?? "").matchAll(DEPRECATION_LINK)].map((match) => match[1] ?? "");
|
|
732
|
+
const key2 = `${method} ${links.join(" ") || (url.split("?")[0] ?? url)}`;
|
|
733
|
+
if (deprecationsSeen.has(key2)) return;
|
|
734
|
+
deprecationsSeen.add(key2);
|
|
735
|
+
const sunset = headers.get("sunset");
|
|
736
|
+
logger.warn(
|
|
737
|
+
`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}`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
function deprecatedSince(value) {
|
|
741
|
+
const seconds = Number(value.trim().replace(/^@/, ""));
|
|
742
|
+
return Number.isInteger(seconds) ? new Date(seconds * 1e3).toISOString().slice(0, 10) : value;
|
|
743
|
+
}
|
|
744
|
+
function sunsetDay(value) {
|
|
745
|
+
const time = Date.parse(value);
|
|
746
|
+
return Number.isNaN(time) ? value : new Date(time).toISOString().slice(0, 10);
|
|
747
|
+
}
|
|
682
748
|
async function readBody(response) {
|
|
683
749
|
const text2 = await response.text();
|
|
684
750
|
if (text2.length === 0) return null;
|
|
@@ -4018,8 +4084,7 @@ var TurnSender = class {
|
|
|
4018
4084
|
return true;
|
|
4019
4085
|
}
|
|
4020
4086
|
}
|
|
4021
|
-
|
|
4022
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4087
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4023
4088
|
return true;
|
|
4024
4089
|
}
|
|
4025
4090
|
accepted(batch, answer) {
|
|
@@ -4027,8 +4092,8 @@ var TurnSender = class {
|
|
|
4027
4092
|
this.resumeAt = 0;
|
|
4028
4093
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4029
4094
|
const again = [];
|
|
4030
|
-
const
|
|
4031
|
-
let
|
|
4095
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4096
|
+
let refused3 = 0;
|
|
4032
4097
|
for (const error of answer.errors ?? []) {
|
|
4033
4098
|
const frame = batch.frames[error.index];
|
|
4034
4099
|
if (frame === void 0) continue;
|
|
@@ -4037,12 +4102,12 @@ var TurnSender = class {
|
|
|
4037
4102
|
this.recorder.modeRefused();
|
|
4038
4103
|
again.push(frame);
|
|
4039
4104
|
} else {
|
|
4040
|
-
|
|
4041
|
-
|
|
4105
|
+
refused3++;
|
|
4106
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4042
4107
|
}
|
|
4043
4108
|
}
|
|
4044
4109
|
if (again.length > 0) this.queue.requeue(again);
|
|
4045
|
-
if (
|
|
4110
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4046
4111
|
}
|
|
4047
4112
|
};
|
|
4048
4113
|
|
|
@@ -4164,9 +4229,12 @@ var TurnRecorder = class {
|
|
|
4164
4229
|
this.accepted += accepted;
|
|
4165
4230
|
this.duplicates += duplicates;
|
|
4166
4231
|
}
|
|
4167
|
-
|
|
4232
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4233
|
+
rejected(count3, reasons) {
|
|
4168
4234
|
this.rejectedTurns += count3;
|
|
4169
|
-
|
|
4235
|
+
const shown = [...new Set(reasons)].sort();
|
|
4236
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4237
|
+
this.logger.warn(`${count3} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4170
4238
|
}
|
|
4171
4239
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4172
4240
|
notRecorded(count3, off) {
|
|
@@ -4236,6 +4304,10 @@ async function hex(text2) {
|
|
|
4236
4304
|
}
|
|
4237
4305
|
|
|
4238
4306
|
// src/coordination/client.ts
|
|
4307
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4308
|
+
function refused(error) {
|
|
4309
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4310
|
+
}
|
|
4239
4311
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4240
4312
|
var CHECK_BUDGET_MS = 200;
|
|
4241
4313
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4257,15 +4329,22 @@ function claimed(data, error) {
|
|
|
4257
4329
|
return { held: false, claim: null, error: code };
|
|
4258
4330
|
}
|
|
4259
4331
|
var Coordinator = class {
|
|
4260
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4332
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4261
4333
|
this.outbox = outbox;
|
|
4262
4334
|
this.suppressions = suppressions;
|
|
4263
4335
|
this.declareNow = declareNow;
|
|
4336
|
+
this.logger = logger;
|
|
4264
4337
|
}
|
|
4265
4338
|
outbox;
|
|
4266
4339
|
suppressions;
|
|
4267
4340
|
declareNow;
|
|
4341
|
+
logger;
|
|
4268
4342
|
async failed(request, failOpen, error) {
|
|
4343
|
+
if (refused(error)) {
|
|
4344
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4345
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4346
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4347
|
+
}
|
|
4269
4348
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4270
4349
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4271
4350
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4286,7 +4365,7 @@ var Coordinator = class {
|
|
|
4286
4365
|
if (who.subject) body.subject = who.subject;
|
|
4287
4366
|
if (who.object) body.object = who.object;
|
|
4288
4367
|
const key2 = uuidv7();
|
|
4289
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4368
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4290
4369
|
return key2;
|
|
4291
4370
|
}
|
|
4292
4371
|
};
|
|
@@ -4393,26 +4472,42 @@ async function suppressionKey(salt, canonical2) {
|
|
|
4393
4472
|
const key2 = await subtle.importKey("raw", saltBytes(salt), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
4394
4473
|
return toBase64url(new Uint8Array(await subtle.sign("HMAC", key2, new TextEncoder().encode(canonical2))));
|
|
4395
4474
|
}
|
|
4475
|
+
var AREA_CODES = new Set(
|
|
4476
|
+
"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(" ")
|
|
4477
|
+
);
|
|
4478
|
+
var FORMAT_CHARACTERS = /\p{Cf}/gu;
|
|
4396
4479
|
function phone(value) {
|
|
4397
|
-
|
|
4398
|
-
|
|
4480
|
+
let text2 = value.normalize("NFKC").replace(FORMAT_CHARACTERS, "").trim();
|
|
4481
|
+
if (text2.slice(0, 4).toLowerCase() === "tel:") text2 = text2.slice(4);
|
|
4482
|
+
const international = text2.startsWith("+") || text2.startsWith("00");
|
|
4483
|
+
if (international) text2 = text2.replaceAll("(0)", "");
|
|
4484
|
+
const raw = text2.replace(SEPARATORS2, "");
|
|
4399
4485
|
let digits = raw.startsWith("+") ? raw.slice(1) : international ? raw.slice(2) : raw;
|
|
4400
4486
|
if (!DIGITS2.test(digits)) throw new NiadraDestinationError("invalid_handle");
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4487
|
+
if (!international) {
|
|
4488
|
+
const national = digits.replace(/^0+/, "");
|
|
4489
|
+
if (isBrazilianNational(national)) {
|
|
4490
|
+
digits = `55${national}`;
|
|
4491
|
+
} else if (digits.startsWith("0") && (national.length === 12 || national.length === 13)) {
|
|
4492
|
+
if (isBrazilianNational(national.slice(2))) digits = `55${national.slice(2)}`;
|
|
4493
|
+
}
|
|
4404
4494
|
}
|
|
4405
4495
|
if (digits.length < 8 || digits.length > 15 || digits.startsWith("0")) {
|
|
4406
4496
|
throw new NiadraDestinationError("invalid_handle");
|
|
4407
4497
|
}
|
|
4408
4498
|
const rest = digits.slice(2);
|
|
4409
|
-
if (digits.startsWith("55") && rest.length === 10 &&
|
|
4499
|
+
if (digits.startsWith("55") && rest.length === 10 && AREA_CODES.has(rest.slice(0, 2)) && "6789".includes(rest.charAt(2))) {
|
|
4410
4500
|
digits = `55${rest.slice(0, 2)}9${rest.slice(2)}`;
|
|
4411
4501
|
}
|
|
4502
|
+
if (digits.length === 13 && (digits.startsWith("521") || digits.startsWith("549"))) {
|
|
4503
|
+
digits = digits.slice(0, 2) + digits.slice(3);
|
|
4504
|
+
}
|
|
4412
4505
|
return `phone:+${digits}`;
|
|
4413
4506
|
}
|
|
4414
|
-
function
|
|
4415
|
-
|
|
4507
|
+
function isBrazilianNational(national) {
|
|
4508
|
+
if (!AREA_CODES.has(national.slice(0, 2))) return false;
|
|
4509
|
+
if (national.length === 10) return "23456789".includes(national.charAt(2));
|
|
4510
|
+
return national.length === 11 && national.charAt(2) === "9";
|
|
4416
4511
|
}
|
|
4417
4512
|
function saltBytes(salt) {
|
|
4418
4513
|
const bytes = fromBase64url(salt);
|
|
@@ -4789,7 +4884,7 @@ var Outbox = class {
|
|
|
4789
4884
|
return false;
|
|
4790
4885
|
}
|
|
4791
4886
|
this.writes.shift();
|
|
4792
|
-
this.logger.warn(
|
|
4887
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4793
4888
|
this.settle(write, void 0, error);
|
|
4794
4889
|
return true;
|
|
4795
4890
|
}
|
|
@@ -5679,9 +5774,17 @@ var handles = {
|
|
|
5679
5774
|
appUserId: (value, options) => build("app_user_id", value, void 0, options),
|
|
5680
5775
|
/** The id of a person or organization in a system of record; `system` names that system, such as `crm`. */
|
|
5681
5776
|
systemId: (value, system, options) => build("system_id", value, system, options),
|
|
5682
|
-
/**
|
|
5777
|
+
/**
|
|
5778
|
+
* A person's national document, such as a CPF: `govIdHmac("529.982.247-25", "BR")`. Send the number
|
|
5779
|
+
* itself, never a hash of it: the server checks its check digits, then keeps only a keyed hash with your
|
|
5780
|
+
* space's secret and an encrypted copy, and shows it masked. `country` is its ISO 3166-1 alpha-2 code.
|
|
5781
|
+
*/
|
|
5683
5782
|
govIdHmac: (value, country, options) => build("gov_id_hmac", value, country, options),
|
|
5684
|
-
/**
|
|
5783
|
+
/**
|
|
5784
|
+
* A company's registry number, such as a CNPJ: `orgRegistryHmac("11.222.333/0001-81", "BR")`. As with
|
|
5785
|
+
* `govIdHmac`, send the number itself; the server checks it and keeps only a keyed hash. Identifies an
|
|
5786
|
+
* organization.
|
|
5787
|
+
*/
|
|
5685
5788
|
orgRegistryHmac: (value, country) => build("org_registry_hmac", value, country, { subjectKind: "account" }),
|
|
5686
5789
|
/** An e-mail domain, such as `acme.com`. Identifies an organization. */
|
|
5687
5790
|
emailDomain: (domain) => build("email_domain", domain, void 0, { subjectKind: "account" }),
|
|
@@ -6355,6 +6458,7 @@ var AgentSession = class {
|
|
|
6355
6458
|
try {
|
|
6356
6459
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6357
6460
|
} catch (error) {
|
|
6461
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6358
6462
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6359
6463
|
}
|
|
6360
6464
|
}
|
|
@@ -6966,6 +7070,7 @@ var SessionState = class {
|
|
|
6966
7070
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
6967
7071
|
guards = /* @__PURE__ */ new Map();
|
|
6968
7072
|
lastReport = null;
|
|
7073
|
+
unlinkedSaid = false;
|
|
6969
7074
|
/** After the first pack, every read also asks what changed since. */
|
|
6970
7075
|
get wantsDelta() {
|
|
6971
7076
|
return this.etag !== null;
|
|
@@ -6994,6 +7099,19 @@ var SessionState = class {
|
|
|
6994
7099
|
this.last = absorbed;
|
|
6995
7100
|
return absorbed;
|
|
6996
7101
|
}
|
|
7102
|
+
/**
|
|
7103
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7104
|
+
* on with the subject's own memory, never the organization's.
|
|
7105
|
+
*/
|
|
7106
|
+
sayUnlinked(result2, logger) {
|
|
7107
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7108
|
+
this.unlinkedSaid = true;
|
|
7109
|
+
logger.warn(
|
|
7110
|
+
"`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."
|
|
7111
|
+
);
|
|
7112
|
+
}
|
|
7113
|
+
return result2;
|
|
7114
|
+
}
|
|
6997
7115
|
/** The last backing check of an agent's answer. */
|
|
6998
7116
|
get lastBacking() {
|
|
6999
7117
|
return this.lastReport;
|
|
@@ -7241,7 +7359,7 @@ var Conversation = class {
|
|
|
7241
7359
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7242
7360
|
*/
|
|
7243
7361
|
async context(options = {}) {
|
|
7244
|
-
const { query, turn, format, explain, include, ...requestOptions } = options;
|
|
7362
|
+
const { query, turn, format, explain: explain2, include, ...requestOptions } = options;
|
|
7245
7363
|
const params = {
|
|
7246
7364
|
subject: this.subject,
|
|
7247
7365
|
view: this.view,
|
|
@@ -7250,7 +7368,7 @@ var Conversation = class {
|
|
|
7250
7368
|
...this.params.about ? { about: this.params.about } : {},
|
|
7251
7369
|
...this.params.target ? { target: this.params.target } : {},
|
|
7252
7370
|
...format === "json" ? { format } : {},
|
|
7253
|
-
...
|
|
7371
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7254
7372
|
};
|
|
7255
7373
|
if (query) params.query = query;
|
|
7256
7374
|
if (include?.length) params.include = include;
|
|
@@ -7258,7 +7376,7 @@ var Conversation = class {
|
|
|
7258
7376
|
params.turn = turn === void 0 ? this.turnText : turn;
|
|
7259
7377
|
const result2 = await this.client.context(params, requestOptions);
|
|
7260
7378
|
this.features.observe(result2);
|
|
7261
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7379
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7262
7380
|
}
|
|
7263
7381
|
/**
|
|
7264
7382
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7438,6 +7556,35 @@ function withHandles(base, extra) {
|
|
|
7438
7556
|
return all;
|
|
7439
7557
|
}
|
|
7440
7558
|
|
|
7559
|
+
// src/warm.ts
|
|
7560
|
+
var EVERY_MS = 1e5;
|
|
7561
|
+
var IDLE_MS = 9e4;
|
|
7562
|
+
var WARM_FOR_MS = 6e5;
|
|
7563
|
+
var KeepWarm = class {
|
|
7564
|
+
constructor(enabled) {
|
|
7565
|
+
this.enabled = enabled;
|
|
7566
|
+
}
|
|
7567
|
+
enabled;
|
|
7568
|
+
open = /* @__PURE__ */ new Map();
|
|
7569
|
+
openedAt = 0;
|
|
7570
|
+
/** Notes an open conversation or task. */
|
|
7571
|
+
add(scope, session, now) {
|
|
7572
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7573
|
+
this.open.set(scope, new WeakRef(session));
|
|
7574
|
+
this.openedAt = now;
|
|
7575
|
+
}
|
|
7576
|
+
/** The conversation or task ended. */
|
|
7577
|
+
end(scope) {
|
|
7578
|
+
this.open.delete(scope);
|
|
7579
|
+
}
|
|
7580
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7581
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7582
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7583
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7584
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7585
|
+
}
|
|
7586
|
+
};
|
|
7587
|
+
|
|
7441
7588
|
// src/exit.ts
|
|
7442
7589
|
var registered = /* @__PURE__ */ new Set();
|
|
7443
7590
|
var installedOn = null;
|
|
@@ -7568,8 +7715,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7568
7715
|
write: 5e3,
|
|
7569
7716
|
token: 2e3,
|
|
7570
7717
|
upload: 6e4,
|
|
7571
|
-
prefetch: 1e3
|
|
7718
|
+
prefetch: 1e3,
|
|
7719
|
+
connect: 1e3
|
|
7572
7720
|
};
|
|
7721
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7573
7722
|
var DEFAULT_CACHE = {
|
|
7574
7723
|
ttlMs: 1e4,
|
|
7575
7724
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7676,7 +7825,7 @@ var Task = class {
|
|
|
7676
7825
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7677
7826
|
*/
|
|
7678
7827
|
async context(options = {}) {
|
|
7679
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7828
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7680
7829
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7681
7830
|
const params = {
|
|
7682
7831
|
...target,
|
|
@@ -7686,18 +7835,18 @@ var Task = class {
|
|
|
7686
7835
|
...this.level ? { verification: this.level } : {},
|
|
7687
7836
|
...this.params.target ? { target: this.params.target } : {},
|
|
7688
7837
|
...format === "json" ? { format } : {},
|
|
7689
|
-
...
|
|
7838
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7690
7839
|
...include?.length ? { include } : {}
|
|
7691
7840
|
};
|
|
7692
7841
|
if (query) {
|
|
7693
7842
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7694
7843
|
this.features.observe(answered);
|
|
7695
|
-
return this.state.observe(answered);
|
|
7844
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7696
7845
|
}
|
|
7697
7846
|
if (this.state.wantsDelta) params.delta = true;
|
|
7698
7847
|
const result2 = await this.client.context(params, requestOptions);
|
|
7699
7848
|
this.features.observe(result2);
|
|
7700
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7849
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7701
7850
|
}
|
|
7702
7851
|
/**
|
|
7703
7852
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7827,14 +7976,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7827
7976
|
search: "search_agent_memory",
|
|
7828
7977
|
remember: "remember"
|
|
7829
7978
|
};
|
|
7830
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7979
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7831
7980
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7832
7981
|
var TOOL_DEFINITIONS = [
|
|
7833
7982
|
{
|
|
7834
7983
|
"type": "function",
|
|
7835
7984
|
"function": {
|
|
7836
7985
|
"name": "search_customer_history",
|
|
7837
|
-
"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 '
|
|
7986
|
+
"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.",
|
|
7838
7987
|
"parameters": {
|
|
7839
7988
|
"type": "object",
|
|
7840
7989
|
"properties": {
|
|
@@ -7878,7 +8027,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7878
8027
|
"open_item",
|
|
7879
8028
|
"action",
|
|
7880
8029
|
"object",
|
|
7881
|
-
"trait"
|
|
8030
|
+
"trait",
|
|
8031
|
+
"system_event"
|
|
7882
8032
|
]
|
|
7883
8033
|
}
|
|
7884
8034
|
},
|
|
@@ -7905,7 +8055,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7905
8055
|
"type": "function",
|
|
7906
8056
|
"function": {
|
|
7907
8057
|
"name": "get_customer_timeline",
|
|
7908
|
-
"description": "List this customer's conversations
|
|
8058
|
+
"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.",
|
|
7909
8059
|
"parameters": {
|
|
7910
8060
|
"type": "object",
|
|
7911
8061
|
"properties": {
|
|
@@ -7953,7 +8103,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7953
8103
|
"open_item",
|
|
7954
8104
|
"action",
|
|
7955
8105
|
"object",
|
|
7956
|
-
"trait"
|
|
8106
|
+
"trait",
|
|
8107
|
+
"system_event"
|
|
7957
8108
|
]
|
|
7958
8109
|
}
|
|
7959
8110
|
},
|
|
@@ -7972,7 +8123,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7972
8123
|
"type": "function",
|
|
7973
8124
|
"function": {
|
|
7974
8125
|
"name": "open_history_item",
|
|
7975
|
-
"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.",
|
|
8126
|
+
"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.",
|
|
7976
8127
|
"parameters": {
|
|
7977
8128
|
"type": "object",
|
|
7978
8129
|
"properties": {
|
|
@@ -8343,6 +8494,13 @@ function compose(body, read) {
|
|
|
8343
8494
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8344
8495
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8345
8496
|
}
|
|
8497
|
+
var RTT_MARGIN_MS = 50;
|
|
8498
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8499
|
+
const ms = Math.round(rttMs);
|
|
8500
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8501
|
+
(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`
|
|
8502
|
+
);
|
|
8503
|
+
}
|
|
8346
8504
|
function rttWarnings(rttMs, timeouts) {
|
|
8347
8505
|
const ms = Math.round(rttMs);
|
|
8348
8506
|
const found2 = [];
|
|
@@ -8380,6 +8538,12 @@ var Niadra = class _Niadra {
|
|
|
8380
8538
|
enabled;
|
|
8381
8539
|
core;
|
|
8382
8540
|
timeouts;
|
|
8541
|
+
keepWarm;
|
|
8542
|
+
warmTimer;
|
|
8543
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8544
|
+
defaultReads;
|
|
8545
|
+
voiceStarted = false;
|
|
8546
|
+
voiceWarned = false;
|
|
8383
8547
|
strict;
|
|
8384
8548
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8385
8549
|
logger;
|
|
@@ -8424,6 +8588,8 @@ var Niadra = class _Niadra {
|
|
|
8424
8588
|
this.strict = options.strict ?? false;
|
|
8425
8589
|
this.logger = options.logger ?? consoleLogger;
|
|
8426
8590
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8591
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8592
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8427
8593
|
this.voice = new VoiceLines(
|
|
8428
8594
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8429
8595
|
);
|
|
@@ -8449,13 +8615,14 @@ var Niadra = class _Niadra {
|
|
|
8449
8615
|
this.coordinator = new Coordinator(
|
|
8450
8616
|
this.outbox,
|
|
8451
8617
|
this.suppressions,
|
|
8452
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8618
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8619
|
+
this.logger
|
|
8453
8620
|
);
|
|
8454
8621
|
this.states = new AgentStates(
|
|
8455
8622
|
this.outbox,
|
|
8456
8623
|
{
|
|
8457
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8458
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8624
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8625
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8459
8626
|
},
|
|
8460
8627
|
this.logger
|
|
8461
8628
|
);
|
|
@@ -8482,6 +8649,7 @@ var Niadra = class _Niadra {
|
|
|
8482
8649
|
this.core = setup;
|
|
8483
8650
|
this.enabled = true;
|
|
8484
8651
|
this.disabledReason = null;
|
|
8652
|
+
this.probe(setup);
|
|
8485
8653
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8486
8654
|
}
|
|
8487
8655
|
setup(options) {
|
|
@@ -8503,7 +8671,10 @@ var Niadra = class _Niadra {
|
|
|
8503
8671
|
baseURL,
|
|
8504
8672
|
apiKey,
|
|
8505
8673
|
fetch: fetchImpl,
|
|
8506
|
-
defaultHeaders: options.defaultHeaders ?? {}
|
|
8674
|
+
defaultHeaders: options.defaultHeaders ?? {},
|
|
8675
|
+
logger: this.logger,
|
|
8676
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8677
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8507
8678
|
});
|
|
8508
8679
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8509
8680
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8559,7 +8730,7 @@ var Niadra = class _Niadra {
|
|
|
8559
8730
|
return result2;
|
|
8560
8731
|
}
|
|
8561
8732
|
readContext(core, params, request, options) {
|
|
8562
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8733
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8563
8734
|
const { query: own3, ...pinned2 } = request;
|
|
8564
8735
|
const query = turnText(own3 ?? params.turn);
|
|
8565
8736
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8575,7 +8746,7 @@ var Niadra = class _Niadra {
|
|
|
8575
8746
|
*/
|
|
8576
8747
|
async profile() {
|
|
8577
8748
|
if (!this.core) return null;
|
|
8578
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8749
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8579
8750
|
}
|
|
8580
8751
|
/**
|
|
8581
8752
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8600,7 +8771,7 @@ var Niadra = class _Niadra {
|
|
|
8600
8771
|
*/
|
|
8601
8772
|
async mayContact(handle, purpose, options = {}) {
|
|
8602
8773
|
if (this.core && this.suppressions.due()) {
|
|
8603
|
-
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8774
|
+
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8604
8775
|
if (!this.suppressions.held) await read;
|
|
8605
8776
|
}
|
|
8606
8777
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8610,8 +8781,8 @@ var Niadra = class _Niadra {
|
|
|
8610
8781
|
readSuppressions(budgetMs) {
|
|
8611
8782
|
return this.suppressions.read(
|
|
8612
8783
|
{
|
|
8613
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8614
|
-
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.
|
|
8784
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8785
|
+
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.readBudget("navigation") })
|
|
8615
8786
|
},
|
|
8616
8787
|
budgetMs
|
|
8617
8788
|
);
|
|
@@ -8690,6 +8861,7 @@ var Niadra = class _Niadra {
|
|
|
8690
8861
|
}
|
|
8691
8862
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8692
8863
|
get agentHost() {
|
|
8864
|
+
const client = this;
|
|
8693
8865
|
return {
|
|
8694
8866
|
recorder: this.turns,
|
|
8695
8867
|
coordinator: this.coordinator,
|
|
@@ -8703,7 +8875,10 @@ var Niadra = class _Niadra {
|
|
|
8703
8875
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8704
8876
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8705
8877
|
enabled: this.enabled,
|
|
8706
|
-
|
|
8878
|
+
strict: this.strict,
|
|
8879
|
+
get navigationMs() {
|
|
8880
|
+
return client.readBudget("navigation");
|
|
8881
|
+
}
|
|
8707
8882
|
};
|
|
8708
8883
|
}
|
|
8709
8884
|
/**
|
|
@@ -8731,7 +8906,7 @@ var Niadra = class _Niadra {
|
|
|
8731
8906
|
if (!cache) return false;
|
|
8732
8907
|
const key2 = cacheKey(request);
|
|
8733
8908
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8734
|
-
this.
|
|
8909
|
+
this.startVoice(core);
|
|
8735
8910
|
line.request = request;
|
|
8736
8911
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8737
8912
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8868,13 +9043,13 @@ var Niadra = class _Niadra {
|
|
|
8868
9043
|
if (!params.query || params.query.length > 2e3) {
|
|
8869
9044
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8870
9045
|
}
|
|
8871
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
9046
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8872
9047
|
});
|
|
8873
9048
|
}
|
|
8874
9049
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8875
9050
|
async timeline(params, options = {}) {
|
|
8876
9051
|
return this.navigate(
|
|
8877
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
9052
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8878
9053
|
);
|
|
8879
9054
|
}
|
|
8880
9055
|
/**
|
|
@@ -8887,9 +9062,10 @@ var Niadra = class _Niadra {
|
|
|
8887
9062
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8888
9063
|
const body = { item_id: id };
|
|
8889
9064
|
if (params.subject) body.subject = params.subject;
|
|
9065
|
+
if (params.about) body.about = params.about;
|
|
8890
9066
|
if (params.verification) body.verification = params.verification;
|
|
8891
9067
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8892
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9068
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8893
9069
|
});
|
|
8894
9070
|
}
|
|
8895
9071
|
/**
|
|
@@ -8901,7 +9077,7 @@ var Niadra = class _Niadra {
|
|
|
8901
9077
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8902
9078
|
*/
|
|
8903
9079
|
async objectState(object, options = {}) {
|
|
8904
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9080
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8905
9081
|
}
|
|
8906
9082
|
/**
|
|
8907
9083
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8914,7 +9090,7 @@ var Niadra = class _Niadra {
|
|
|
8914
9090
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8915
9091
|
}
|
|
8916
9092
|
const path = `${objectPath(object)}/timeline`;
|
|
8917
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9093
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8918
9094
|
spec.query = { cursor: params.cursor, limit: String(limit3) };
|
|
8919
9095
|
return spec;
|
|
8920
9096
|
});
|
|
@@ -8933,6 +9109,7 @@ var Niadra = class _Niadra {
|
|
|
8933
9109
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8934
9110
|
open: (id, customer, bound, voice) => {
|
|
8935
9111
|
const scope = { subject: customer };
|
|
9112
|
+
if (bound.about) scope.about = bound.about;
|
|
8936
9113
|
if (bound.verification) scope.verification = bound.verification;
|
|
8937
9114
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8938
9115
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -8967,7 +9144,7 @@ var Niadra = class _Niadra {
|
|
|
8967
9144
|
const key2 = AgentMemoryCache.key(params);
|
|
8968
9145
|
const fresh = cache?.fresh(key2);
|
|
8969
9146
|
if (fresh) return blockResult(fresh, "cache");
|
|
8970
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9147
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8971
9148
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
8972
9149
|
spec.query = {
|
|
8973
9150
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -9004,7 +9181,7 @@ var Niadra = class _Niadra {
|
|
|
9004
9181
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
9005
9182
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9006
9183
|
if (params.task_id) body.task_id = params.task_id;
|
|
9007
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9184
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9008
9185
|
});
|
|
9009
9186
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9010
9187
|
}
|
|
@@ -9098,6 +9275,41 @@ var Niadra = class _Niadra {
|
|
|
9098
9275
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9099
9276
|
}
|
|
9100
9277
|
}
|
|
9278
|
+
/**
|
|
9279
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9280
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9281
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9282
|
+
*/
|
|
9283
|
+
contextUse(params = {}, options = {}) {
|
|
9284
|
+
return this.navigate(() => {
|
|
9285
|
+
const { group_by: groups, ...filters } = params;
|
|
9286
|
+
const query = { ...filters };
|
|
9287
|
+
if (groups?.length) query.group_by = groups;
|
|
9288
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9289
|
+
});
|
|
9290
|
+
}
|
|
9291
|
+
/**
|
|
9292
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9293
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9294
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9295
|
+
*/
|
|
9296
|
+
link(params, options = {}) {
|
|
9297
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9298
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9299
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9300
|
+
}
|
|
9301
|
+
/**
|
|
9302
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9303
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9304
|
+
*/
|
|
9305
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9306
|
+
return this.navigate(() => {
|
|
9307
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9308
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9309
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9310
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9311
|
+
});
|
|
9312
|
+
}
|
|
9101
9313
|
/**
|
|
9102
9314
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9103
9315
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9190,16 +9402,20 @@ var Niadra = class _Niadra {
|
|
|
9190
9402
|
* emits `conversation.ended` when you call `end()`.
|
|
9191
9403
|
*/
|
|
9192
9404
|
conversation(params) {
|
|
9193
|
-
|
|
9405
|
+
const conversation = new Conversation(this, params, {
|
|
9194
9406
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9195
9407
|
});
|
|
9408
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9409
|
+
return conversation;
|
|
9196
9410
|
}
|
|
9197
9411
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9198
9412
|
task(params) {
|
|
9199
|
-
|
|
9413
|
+
const task = new Task(this, params, {
|
|
9200
9414
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9201
9415
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9202
9416
|
});
|
|
9417
|
+
this.warm(`task:${task.id}`, task);
|
|
9418
|
+
return task;
|
|
9203
9419
|
}
|
|
9204
9420
|
/**
|
|
9205
9421
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9219,6 +9435,8 @@ var Niadra = class _Niadra {
|
|
|
9219
9435
|
*/
|
|
9220
9436
|
async shutdown() {
|
|
9221
9437
|
this.unregisterExit();
|
|
9438
|
+
clearInterval(this.warmTimer);
|
|
9439
|
+
this.warmTimer = void 0;
|
|
9222
9440
|
if (!this.core) return;
|
|
9223
9441
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9224
9442
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9322,9 +9540,9 @@ var Niadra = class _Niadra {
|
|
|
9322
9540
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9323
9541
|
return normalizeContext(response.data);
|
|
9324
9542
|
} catch (error) {
|
|
9325
|
-
const
|
|
9543
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9326
9544
|
const left = timeout - (Date.now() - started);
|
|
9327
|
-
if (!request.include?.length || !
|
|
9545
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9328
9546
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9329
9547
|
const { include: _dropped, ...plain2 } = request;
|
|
9330
9548
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9412,9 +9630,32 @@ var Niadra = class _Niadra {
|
|
|
9412
9630
|
const rtt = Math.min(...samples);
|
|
9413
9631
|
this.voice.rtt = rtt;
|
|
9414
9632
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9415
|
-
|
|
9633
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9634
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9635
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9416
9636
|
})();
|
|
9417
9637
|
}
|
|
9638
|
+
/**
|
|
9639
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9640
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9641
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9642
|
+
*/
|
|
9643
|
+
readBudget(name) {
|
|
9644
|
+
const rtt = this.voice.rtt;
|
|
9645
|
+
return rtt !== null && this.defaultReads.has(name) ? this.timeouts[name] + rtt : this.timeouts[name];
|
|
9646
|
+
}
|
|
9647
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9648
|
+
startVoice(core) {
|
|
9649
|
+
this.voiceStarted = true;
|
|
9650
|
+
this.probe(core);
|
|
9651
|
+
this.warnVoice();
|
|
9652
|
+
}
|
|
9653
|
+
warnVoice() {
|
|
9654
|
+
const rtt = this.voice.rtt;
|
|
9655
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9656
|
+
this.voiceWarned = true;
|
|
9657
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9658
|
+
}
|
|
9418
9659
|
/**
|
|
9419
9660
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9420
9661
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9424,7 +9665,7 @@ var Niadra = class _Niadra {
|
|
|
9424
9665
|
const key2 = cacheKey(request);
|
|
9425
9666
|
const deadline = Date.now() + timeout;
|
|
9426
9667
|
const line = this.voice.line(scope);
|
|
9427
|
-
this.
|
|
9668
|
+
this.startVoice(core);
|
|
9428
9669
|
line.request = request;
|
|
9429
9670
|
const words2 = wordsOf(query);
|
|
9430
9671
|
const background = this.timeouts.prefetch;
|
|
@@ -9610,8 +9851,39 @@ var Niadra = class _Niadra {
|
|
|
9610
9851
|
core.queue.flushInBackground(true);
|
|
9611
9852
|
});
|
|
9612
9853
|
}
|
|
9854
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9855
|
+
warm(scope, session) {
|
|
9856
|
+
const core = this.core;
|
|
9857
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9858
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9859
|
+
if (this.warmTimer !== void 0) return;
|
|
9860
|
+
const timer = setInterval(() => {
|
|
9861
|
+
this.warmTick(core);
|
|
9862
|
+
}, EVERY_MS);
|
|
9863
|
+
timer.unref?.();
|
|
9864
|
+
this.warmTimer = timer;
|
|
9865
|
+
}
|
|
9866
|
+
warmTick(core) {
|
|
9867
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9868
|
+
if (step === "stop") {
|
|
9869
|
+
clearInterval(this.warmTimer);
|
|
9870
|
+
this.warmTimer = void 0;
|
|
9871
|
+
return;
|
|
9872
|
+
}
|
|
9873
|
+
if (step !== "ping") return;
|
|
9874
|
+
core.transport.request({
|
|
9875
|
+
method: "GET",
|
|
9876
|
+
path: "/healthz",
|
|
9877
|
+
timeoutMs: 2e3,
|
|
9878
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9879
|
+
activity: false
|
|
9880
|
+
}).catch((error) => {
|
|
9881
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9882
|
+
});
|
|
9883
|
+
}
|
|
9613
9884
|
async endScope(item, scope) {
|
|
9614
9885
|
this.forgetScope(scope);
|
|
9886
|
+
this.keepWarm.end(scope);
|
|
9615
9887
|
return this.sendNow(() => item);
|
|
9616
9888
|
}
|
|
9617
9889
|
async sendBatch(transport, items2, maxAttempts, backoff) {
|
|
@@ -11466,7 +11738,7 @@ var Replayer = class {
|
|
|
11466
11738
|
try {
|
|
11467
11739
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
11468
11740
|
} catch (error) {
|
|
11469
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
11741
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
11470
11742
|
throw error;
|
|
11471
11743
|
}
|
|
11472
11744
|
let run = runOf(answer);
|
|
@@ -11569,7 +11841,7 @@ var Replayer = class {
|
|
|
11569
11841
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
11570
11842
|
}
|
|
11571
11843
|
};
|
|
11572
|
-
function
|
|
11844
|
+
function refused2(scenarioIds, results) {
|
|
11573
11845
|
const scenarios = scenarioIds.map((id) => {
|
|
11574
11846
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
11575
11847
|
return {
|