@niadra/sdk 0.9.1 → 0.10.4
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 +69 -0
- 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 +331 -96
- package/dist/cli.js.map +1 -1
- package/dist/{client-CWMxmJHs.d.cts → client-7mkrdRI0.d.cts} +192 -7
- package/dist/{client-CWMxmJHs.d.ts → client-7mkrdRI0.d.ts} +192 -7
- 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 +341 -98
- 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 +341 -98
- package/dist/index.js.map +1 -1
- package/dist/{intercept-CGngQI8B.d.cts → intercept-CGQhwAom.d.cts} +1 -1
- package/dist/{intercept-Bv0nLpSw.d.ts → intercept-CT0vglsd.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-B4Ef2Wpg.d.ts → shared-Bf84jr3Y.d.ts} +1 -1
- package/dist/{shared-Cq8mz-7m.d.cts → shared-CxAwOcZI.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-jTx_SegV.d.cts → webhook-DE3HsGkb.d.cts} +1 -1
- package/dist/{webhook-z2TjUzjv.d.ts → webhook-QxYgxE6_.d.ts} +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 +1 -1
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.4";
|
|
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,36 @@ 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;
|
|
544
|
+
/** Likely: an answer came within `keepAliveMs`. When not, the next budgeted call gets `coldAllowanceMs`. */
|
|
545
|
+
connectionOpen() {
|
|
546
|
+
return this.answeredAt !== void 0 && Date.now() - this.answeredAt <= (this.config.keepAliveMs ?? 0);
|
|
547
|
+
}
|
|
532
548
|
async request(spec) {
|
|
533
|
-
|
|
549
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
550
|
+
const budgeted = this.withAllowance(spec);
|
|
551
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
555
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
556
|
+
* outage costs the allowance once, not on every turn.
|
|
557
|
+
*/
|
|
558
|
+
withAllowance(spec) {
|
|
559
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
560
|
+
const { retry } = spec;
|
|
561
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
562
|
+
const now = Date.now();
|
|
563
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
564
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
565
|
+
this.grantedAt ??= now;
|
|
566
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
567
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
568
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
534
569
|
}
|
|
535
570
|
async read(spec, policy) {
|
|
536
571
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -601,7 +636,18 @@ var Transport = class {
|
|
|
601
636
|
headers["content-type"] = "application/json";
|
|
602
637
|
init.body = JSON.stringify(spec.body);
|
|
603
638
|
}
|
|
604
|
-
|
|
639
|
+
try {
|
|
640
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
641
|
+
this.answered();
|
|
642
|
+
return answer;
|
|
643
|
+
} catch (error) {
|
|
644
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
645
|
+
throw error;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
answered() {
|
|
649
|
+
this.answeredAt = Date.now();
|
|
650
|
+
this.grantedAt = void 0;
|
|
605
651
|
}
|
|
606
652
|
async exchange(url, init, deadline) {
|
|
607
653
|
let response;
|
|
@@ -4042,8 +4088,7 @@ var TurnSender = class {
|
|
|
4042
4088
|
return true;
|
|
4043
4089
|
}
|
|
4044
4090
|
}
|
|
4045
|
-
|
|
4046
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4091
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4047
4092
|
return true;
|
|
4048
4093
|
}
|
|
4049
4094
|
accepted(batch, answer) {
|
|
@@ -4051,8 +4096,8 @@ var TurnSender = class {
|
|
|
4051
4096
|
this.resumeAt = 0;
|
|
4052
4097
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4053
4098
|
const again = [];
|
|
4054
|
-
const
|
|
4055
|
-
let
|
|
4099
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4100
|
+
let refused3 = 0;
|
|
4056
4101
|
for (const error of answer.errors ?? []) {
|
|
4057
4102
|
const frame = batch.frames[error.index];
|
|
4058
4103
|
if (frame === void 0) continue;
|
|
@@ -4061,12 +4106,12 @@ var TurnSender = class {
|
|
|
4061
4106
|
this.recorder.modeRefused();
|
|
4062
4107
|
again.push(frame);
|
|
4063
4108
|
} else {
|
|
4064
|
-
|
|
4065
|
-
|
|
4109
|
+
refused3++;
|
|
4110
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4066
4111
|
}
|
|
4067
4112
|
}
|
|
4068
4113
|
if (again.length > 0) this.queue.requeue(again);
|
|
4069
|
-
if (
|
|
4114
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4070
4115
|
}
|
|
4071
4116
|
};
|
|
4072
4117
|
|
|
@@ -4188,9 +4233,12 @@ var TurnRecorder = class {
|
|
|
4188
4233
|
this.accepted += accepted;
|
|
4189
4234
|
this.duplicates += duplicates;
|
|
4190
4235
|
}
|
|
4191
|
-
|
|
4236
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4237
|
+
rejected(count3, reasons) {
|
|
4192
4238
|
this.rejectedTurns += count3;
|
|
4193
|
-
|
|
4239
|
+
const shown = [...new Set(reasons)].sort();
|
|
4240
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4241
|
+
this.logger.warn(`${count3} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4194
4242
|
}
|
|
4195
4243
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4196
4244
|
notRecorded(count3, off) {
|
|
@@ -4260,6 +4308,10 @@ async function hex(text2) {
|
|
|
4260
4308
|
}
|
|
4261
4309
|
|
|
4262
4310
|
// src/coordination/client.ts
|
|
4311
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4312
|
+
function refused(error) {
|
|
4313
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4314
|
+
}
|
|
4263
4315
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4264
4316
|
var CHECK_BUDGET_MS = 200;
|
|
4265
4317
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4281,15 +4333,22 @@ function claimed(data, error) {
|
|
|
4281
4333
|
return { held: false, claim: null, error: code };
|
|
4282
4334
|
}
|
|
4283
4335
|
var Coordinator = class {
|
|
4284
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4336
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4285
4337
|
this.outbox = outbox;
|
|
4286
4338
|
this.suppressions = suppressions;
|
|
4287
4339
|
this.declareNow = declareNow;
|
|
4340
|
+
this.logger = logger;
|
|
4288
4341
|
}
|
|
4289
4342
|
outbox;
|
|
4290
4343
|
suppressions;
|
|
4291
4344
|
declareNow;
|
|
4345
|
+
logger;
|
|
4292
4346
|
async failed(request, failOpen, error) {
|
|
4347
|
+
if (refused(error)) {
|
|
4348
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4349
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4350
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4351
|
+
}
|
|
4293
4352
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4294
4353
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4295
4354
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4310,7 +4369,7 @@ var Coordinator = class {
|
|
|
4310
4369
|
if (who.subject) body.subject = who.subject;
|
|
4311
4370
|
if (who.object) body.object = who.object;
|
|
4312
4371
|
const key2 = uuidv7();
|
|
4313
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4372
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4314
4373
|
return key2;
|
|
4315
4374
|
}
|
|
4316
4375
|
};
|
|
@@ -4829,7 +4888,7 @@ var Outbox = class {
|
|
|
4829
4888
|
return false;
|
|
4830
4889
|
}
|
|
4831
4890
|
this.writes.shift();
|
|
4832
|
-
this.logger.warn(
|
|
4891
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4833
4892
|
this.settle(write, void 0, error);
|
|
4834
4893
|
return true;
|
|
4835
4894
|
}
|
|
@@ -5719,9 +5778,17 @@ var handles = {
|
|
|
5719
5778
|
appUserId: (value, options) => build("app_user_id", value, void 0, options),
|
|
5720
5779
|
/** The id of a person or organization in a system of record; `system` names that system, such as `crm`. */
|
|
5721
5780
|
systemId: (value, system, options) => build("system_id", value, system, options),
|
|
5722
|
-
/**
|
|
5781
|
+
/**
|
|
5782
|
+
* A person's national document, such as a CPF: `govIdHmac("529.982.247-25", "BR")`. Send the number
|
|
5783
|
+
* itself, never a hash of it: the server checks its check digits, then keeps only a keyed hash with your
|
|
5784
|
+
* space's secret and an encrypted copy, and shows it masked. `country` is its ISO 3166-1 alpha-2 code.
|
|
5785
|
+
*/
|
|
5723
5786
|
govIdHmac: (value, country, options) => build("gov_id_hmac", value, country, options),
|
|
5724
|
-
/**
|
|
5787
|
+
/**
|
|
5788
|
+
* A company's registry number, such as a CNPJ: `orgRegistryHmac("11.222.333/0001-81", "BR")`. As with
|
|
5789
|
+
* `govIdHmac`, send the number itself; the server checks it and keeps only a keyed hash. Identifies an
|
|
5790
|
+
* organization.
|
|
5791
|
+
*/
|
|
5725
5792
|
orgRegistryHmac: (value, country) => build("org_registry_hmac", value, country, { subjectKind: "account" }),
|
|
5726
5793
|
/** An e-mail domain, such as `acme.com`. Identifies an organization. */
|
|
5727
5794
|
emailDomain: (domain) => build("email_domain", domain, void 0, { subjectKind: "account" }),
|
|
@@ -6395,6 +6462,7 @@ var AgentSession = class {
|
|
|
6395
6462
|
try {
|
|
6396
6463
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6397
6464
|
} catch (error) {
|
|
6465
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6398
6466
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6399
6467
|
}
|
|
6400
6468
|
}
|
|
@@ -7006,6 +7074,7 @@ var SessionState = class {
|
|
|
7006
7074
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
7007
7075
|
guards = /* @__PURE__ */ new Map();
|
|
7008
7076
|
lastReport = null;
|
|
7077
|
+
unlinkedSaid = false;
|
|
7009
7078
|
/** After the first pack, every read also asks what changed since. */
|
|
7010
7079
|
get wantsDelta() {
|
|
7011
7080
|
return this.etag !== null;
|
|
@@ -7034,6 +7103,19 @@ var SessionState = class {
|
|
|
7034
7103
|
this.last = absorbed;
|
|
7035
7104
|
return absorbed;
|
|
7036
7105
|
}
|
|
7106
|
+
/**
|
|
7107
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7108
|
+
* on with the subject's own memory, never the organization's.
|
|
7109
|
+
*/
|
|
7110
|
+
sayUnlinked(result2, logger) {
|
|
7111
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7112
|
+
this.unlinkedSaid = true;
|
|
7113
|
+
logger.warn(
|
|
7114
|
+
"`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."
|
|
7115
|
+
);
|
|
7116
|
+
}
|
|
7117
|
+
return result2;
|
|
7118
|
+
}
|
|
7037
7119
|
/** The last backing check of an agent's answer. */
|
|
7038
7120
|
get lastBacking() {
|
|
7039
7121
|
return this.lastReport;
|
|
@@ -7281,7 +7363,7 @@ var Conversation = class {
|
|
|
7281
7363
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7282
7364
|
*/
|
|
7283
7365
|
async context(options = {}) {
|
|
7284
|
-
const { query, turn, format, explain, include, ...requestOptions } = options;
|
|
7366
|
+
const { query, turn, format, explain: explain2, include, ...requestOptions } = options;
|
|
7285
7367
|
const params = {
|
|
7286
7368
|
subject: this.subject,
|
|
7287
7369
|
view: this.view,
|
|
@@ -7290,7 +7372,7 @@ var Conversation = class {
|
|
|
7290
7372
|
...this.params.about ? { about: this.params.about } : {},
|
|
7291
7373
|
...this.params.target ? { target: this.params.target } : {},
|
|
7292
7374
|
...format === "json" ? { format } : {},
|
|
7293
|
-
...
|
|
7375
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7294
7376
|
};
|
|
7295
7377
|
if (query) params.query = query;
|
|
7296
7378
|
if (include?.length) params.include = include;
|
|
@@ -7298,7 +7380,7 @@ var Conversation = class {
|
|
|
7298
7380
|
params.turn = turn === void 0 ? this.turnText : turn;
|
|
7299
7381
|
const result2 = await this.client.context(params, requestOptions);
|
|
7300
7382
|
this.features.observe(result2);
|
|
7301
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7383
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7302
7384
|
}
|
|
7303
7385
|
/**
|
|
7304
7386
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7478,6 +7560,35 @@ function withHandles(base, extra) {
|
|
|
7478
7560
|
return all;
|
|
7479
7561
|
}
|
|
7480
7562
|
|
|
7563
|
+
// src/warm.ts
|
|
7564
|
+
var EVERY_MS = 1e5;
|
|
7565
|
+
var IDLE_MS = 9e4;
|
|
7566
|
+
var WARM_FOR_MS = 6e5;
|
|
7567
|
+
var KeepWarm = class {
|
|
7568
|
+
constructor(enabled) {
|
|
7569
|
+
this.enabled = enabled;
|
|
7570
|
+
}
|
|
7571
|
+
enabled;
|
|
7572
|
+
open = /* @__PURE__ */ new Map();
|
|
7573
|
+
openedAt = 0;
|
|
7574
|
+
/** Notes an open conversation or task. */
|
|
7575
|
+
add(scope, session, now) {
|
|
7576
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7577
|
+
this.open.set(scope, new WeakRef(session));
|
|
7578
|
+
this.openedAt = now;
|
|
7579
|
+
}
|
|
7580
|
+
/** The conversation or task ended. */
|
|
7581
|
+
end(scope) {
|
|
7582
|
+
this.open.delete(scope);
|
|
7583
|
+
}
|
|
7584
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7585
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7586
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7587
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7588
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7589
|
+
}
|
|
7590
|
+
};
|
|
7591
|
+
|
|
7481
7592
|
// src/exit.ts
|
|
7482
7593
|
var registered = /* @__PURE__ */ new Set();
|
|
7483
7594
|
var installedOn = null;
|
|
@@ -7608,8 +7719,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7608
7719
|
write: 5e3,
|
|
7609
7720
|
token: 2e3,
|
|
7610
7721
|
upload: 6e4,
|
|
7611
|
-
prefetch: 1e3
|
|
7722
|
+
prefetch: 1e3,
|
|
7723
|
+
connect: 1e3
|
|
7612
7724
|
};
|
|
7725
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7613
7726
|
var DEFAULT_CACHE = {
|
|
7614
7727
|
ttlMs: 1e4,
|
|
7615
7728
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7716,7 +7829,7 @@ var Task = class {
|
|
|
7716
7829
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7717
7830
|
*/
|
|
7718
7831
|
async context(options = {}) {
|
|
7719
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7832
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7720
7833
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7721
7834
|
const params = {
|
|
7722
7835
|
...target,
|
|
@@ -7726,18 +7839,18 @@ var Task = class {
|
|
|
7726
7839
|
...this.level ? { verification: this.level } : {},
|
|
7727
7840
|
...this.params.target ? { target: this.params.target } : {},
|
|
7728
7841
|
...format === "json" ? { format } : {},
|
|
7729
|
-
...
|
|
7842
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7730
7843
|
...include?.length ? { include } : {}
|
|
7731
7844
|
};
|
|
7732
7845
|
if (query) {
|
|
7733
7846
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7734
7847
|
this.features.observe(answered);
|
|
7735
|
-
return this.state.observe(answered);
|
|
7848
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7736
7849
|
}
|
|
7737
7850
|
if (this.state.wantsDelta) params.delta = true;
|
|
7738
7851
|
const result2 = await this.client.context(params, requestOptions);
|
|
7739
7852
|
this.features.observe(result2);
|
|
7740
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7853
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7741
7854
|
}
|
|
7742
7855
|
/**
|
|
7743
7856
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7867,14 +7980,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7867
7980
|
search: "search_agent_memory",
|
|
7868
7981
|
remember: "remember"
|
|
7869
7982
|
};
|
|
7870
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7983
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7871
7984
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7872
7985
|
var TOOL_DEFINITIONS = [
|
|
7873
7986
|
{
|
|
7874
7987
|
"type": "function",
|
|
7875
7988
|
"function": {
|
|
7876
7989
|
"name": "search_customer_history",
|
|
7877
|
-
"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 '
|
|
7990
|
+
"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.",
|
|
7878
7991
|
"parameters": {
|
|
7879
7992
|
"type": "object",
|
|
7880
7993
|
"properties": {
|
|
@@ -7918,7 +8031,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7918
8031
|
"open_item",
|
|
7919
8032
|
"action",
|
|
7920
8033
|
"object",
|
|
7921
|
-
"trait"
|
|
8034
|
+
"trait",
|
|
8035
|
+
"system_event"
|
|
7922
8036
|
]
|
|
7923
8037
|
}
|
|
7924
8038
|
},
|
|
@@ -7945,7 +8059,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7945
8059
|
"type": "function",
|
|
7946
8060
|
"function": {
|
|
7947
8061
|
"name": "get_customer_timeline",
|
|
7948
|
-
"description": "List this customer's conversations
|
|
8062
|
+
"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.",
|
|
7949
8063
|
"parameters": {
|
|
7950
8064
|
"type": "object",
|
|
7951
8065
|
"properties": {
|
|
@@ -7993,7 +8107,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7993
8107
|
"open_item",
|
|
7994
8108
|
"action",
|
|
7995
8109
|
"object",
|
|
7996
|
-
"trait"
|
|
8110
|
+
"trait",
|
|
8111
|
+
"system_event"
|
|
7997
8112
|
]
|
|
7998
8113
|
}
|
|
7999
8114
|
},
|
|
@@ -8012,7 +8127,7 @@ var TOOL_DEFINITIONS = [
|
|
|
8012
8127
|
"type": "function",
|
|
8013
8128
|
"function": {
|
|
8014
8129
|
"name": "open_history_item",
|
|
8015
|
-
"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.",
|
|
8130
|
+
"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.",
|
|
8016
8131
|
"parameters": {
|
|
8017
8132
|
"type": "object",
|
|
8018
8133
|
"properties": {
|
|
@@ -8383,6 +8498,13 @@ function compose(body, read) {
|
|
|
8383
8498
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8384
8499
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8385
8500
|
}
|
|
8501
|
+
var RTT_MARGIN_MS = 50;
|
|
8502
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8503
|
+
const ms = Math.round(rttMs);
|
|
8504
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8505
|
+
(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`
|
|
8506
|
+
);
|
|
8507
|
+
}
|
|
8386
8508
|
function rttWarnings(rttMs, timeouts) {
|
|
8387
8509
|
const ms = Math.round(rttMs);
|
|
8388
8510
|
const found2 = [];
|
|
@@ -8420,6 +8542,14 @@ var Niadra = class _Niadra {
|
|
|
8420
8542
|
enabled;
|
|
8421
8543
|
core;
|
|
8422
8544
|
timeouts;
|
|
8545
|
+
keepWarm;
|
|
8546
|
+
warmTimer;
|
|
8547
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8548
|
+
defaultReads;
|
|
8549
|
+
voiceStarted = false;
|
|
8550
|
+
/** A probe is on its way: until it ends, default read budgets get `timeouts.connect` on top. */
|
|
8551
|
+
measuring = false;
|
|
8552
|
+
voiceWarned = false;
|
|
8423
8553
|
strict;
|
|
8424
8554
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8425
8555
|
logger;
|
|
@@ -8464,6 +8594,8 @@ var Niadra = class _Niadra {
|
|
|
8464
8594
|
this.strict = options.strict ?? false;
|
|
8465
8595
|
this.logger = options.logger ?? consoleLogger;
|
|
8466
8596
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8597
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8598
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8467
8599
|
this.voice = new VoiceLines(
|
|
8468
8600
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8469
8601
|
);
|
|
@@ -8489,13 +8621,14 @@ var Niadra = class _Niadra {
|
|
|
8489
8621
|
this.coordinator = new Coordinator(
|
|
8490
8622
|
this.outbox,
|
|
8491
8623
|
this.suppressions,
|
|
8492
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8624
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8625
|
+
this.logger
|
|
8493
8626
|
);
|
|
8494
8627
|
this.states = new AgentStates(
|
|
8495
8628
|
this.outbox,
|
|
8496
8629
|
{
|
|
8497
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8498
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8630
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8631
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8499
8632
|
},
|
|
8500
8633
|
this.logger
|
|
8501
8634
|
);
|
|
@@ -8522,6 +8655,7 @@ var Niadra = class _Niadra {
|
|
|
8522
8655
|
this.core = setup;
|
|
8523
8656
|
this.enabled = true;
|
|
8524
8657
|
this.disabledReason = null;
|
|
8658
|
+
this.probe(setup);
|
|
8525
8659
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8526
8660
|
}
|
|
8527
8661
|
setup(options) {
|
|
@@ -8544,7 +8678,9 @@ var Niadra = class _Niadra {
|
|
|
8544
8678
|
apiKey,
|
|
8545
8679
|
fetch: fetchImpl,
|
|
8546
8680
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
8547
|
-
logger: this.logger
|
|
8681
|
+
logger: this.logger,
|
|
8682
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8683
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8548
8684
|
});
|
|
8549
8685
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8550
8686
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8600,7 +8736,7 @@ var Niadra = class _Niadra {
|
|
|
8600
8736
|
return result2;
|
|
8601
8737
|
}
|
|
8602
8738
|
readContext(core, params, request, options) {
|
|
8603
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8739
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8604
8740
|
const { query: own3, ...pinned2 } = request;
|
|
8605
8741
|
const query = turnText(own3 ?? params.turn);
|
|
8606
8742
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8616,7 +8752,7 @@ var Niadra = class _Niadra {
|
|
|
8616
8752
|
*/
|
|
8617
8753
|
async profile() {
|
|
8618
8754
|
if (!this.core) return null;
|
|
8619
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8755
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8620
8756
|
}
|
|
8621
8757
|
/**
|
|
8622
8758
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8641,7 +8777,7 @@ var Niadra = class _Niadra {
|
|
|
8641
8777
|
*/
|
|
8642
8778
|
async mayContact(handle, purpose, options = {}) {
|
|
8643
8779
|
if (this.core && this.suppressions.due()) {
|
|
8644
|
-
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8780
|
+
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8645
8781
|
if (!this.suppressions.held) await read;
|
|
8646
8782
|
}
|
|
8647
8783
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8651,8 +8787,8 @@ var Niadra = class _Niadra {
|
|
|
8651
8787
|
readSuppressions(budgetMs) {
|
|
8652
8788
|
return this.suppressions.read(
|
|
8653
8789
|
{
|
|
8654
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8655
|
-
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.
|
|
8790
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8791
|
+
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.readBudget("navigation") })
|
|
8656
8792
|
},
|
|
8657
8793
|
budgetMs
|
|
8658
8794
|
);
|
|
@@ -8731,6 +8867,7 @@ var Niadra = class _Niadra {
|
|
|
8731
8867
|
}
|
|
8732
8868
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8733
8869
|
get agentHost() {
|
|
8870
|
+
const client = this;
|
|
8734
8871
|
return {
|
|
8735
8872
|
recorder: this.turns,
|
|
8736
8873
|
coordinator: this.coordinator,
|
|
@@ -8744,7 +8881,10 @@ var Niadra = class _Niadra {
|
|
|
8744
8881
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8745
8882
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8746
8883
|
enabled: this.enabled,
|
|
8747
|
-
|
|
8884
|
+
strict: this.strict,
|
|
8885
|
+
get navigationMs() {
|
|
8886
|
+
return client.readBudget("navigation");
|
|
8887
|
+
}
|
|
8748
8888
|
};
|
|
8749
8889
|
}
|
|
8750
8890
|
/**
|
|
@@ -8772,7 +8912,7 @@ var Niadra = class _Niadra {
|
|
|
8772
8912
|
if (!cache) return false;
|
|
8773
8913
|
const key2 = cacheKey(request);
|
|
8774
8914
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8775
|
-
this.
|
|
8915
|
+
this.startVoice(core);
|
|
8776
8916
|
line.request = request;
|
|
8777
8917
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8778
8918
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8909,13 +9049,13 @@ var Niadra = class _Niadra {
|
|
|
8909
9049
|
if (!params.query || params.query.length > 2e3) {
|
|
8910
9050
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8911
9051
|
}
|
|
8912
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
9052
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8913
9053
|
});
|
|
8914
9054
|
}
|
|
8915
9055
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8916
9056
|
async timeline(params, options = {}) {
|
|
8917
9057
|
return this.navigate(
|
|
8918
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
9058
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8919
9059
|
);
|
|
8920
9060
|
}
|
|
8921
9061
|
/**
|
|
@@ -8928,9 +9068,10 @@ var Niadra = class _Niadra {
|
|
|
8928
9068
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8929
9069
|
const body = { item_id: id };
|
|
8930
9070
|
if (params.subject) body.subject = params.subject;
|
|
9071
|
+
if (params.about) body.about = params.about;
|
|
8931
9072
|
if (params.verification) body.verification = params.verification;
|
|
8932
9073
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8933
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9074
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8934
9075
|
});
|
|
8935
9076
|
}
|
|
8936
9077
|
/**
|
|
@@ -8942,7 +9083,7 @@ var Niadra = class _Niadra {
|
|
|
8942
9083
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8943
9084
|
*/
|
|
8944
9085
|
async objectState(object, options = {}) {
|
|
8945
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9086
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8946
9087
|
}
|
|
8947
9088
|
/**
|
|
8948
9089
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8955,7 +9096,7 @@ var Niadra = class _Niadra {
|
|
|
8955
9096
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8956
9097
|
}
|
|
8957
9098
|
const path = `${objectPath(object)}/timeline`;
|
|
8958
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9099
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8959
9100
|
spec.query = { cursor: params.cursor, limit: String(limit3) };
|
|
8960
9101
|
return spec;
|
|
8961
9102
|
});
|
|
@@ -8974,6 +9115,7 @@ var Niadra = class _Niadra {
|
|
|
8974
9115
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8975
9116
|
open: (id, customer, bound, voice) => {
|
|
8976
9117
|
const scope = { subject: customer };
|
|
9118
|
+
if (bound.about) scope.about = bound.about;
|
|
8977
9119
|
if (bound.verification) scope.verification = bound.verification;
|
|
8978
9120
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8979
9121
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -9008,7 +9150,7 @@ var Niadra = class _Niadra {
|
|
|
9008
9150
|
const key2 = AgentMemoryCache.key(params);
|
|
9009
9151
|
const fresh = cache?.fresh(key2);
|
|
9010
9152
|
if (fresh) return blockResult(fresh, "cache");
|
|
9011
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9153
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
9012
9154
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
9013
9155
|
spec.query = {
|
|
9014
9156
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -9045,7 +9187,7 @@ var Niadra = class _Niadra {
|
|
|
9045
9187
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
9046
9188
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9047
9189
|
if (params.task_id) body.task_id = params.task_id;
|
|
9048
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9190
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9049
9191
|
});
|
|
9050
9192
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9051
9193
|
}
|
|
@@ -9139,6 +9281,41 @@ var Niadra = class _Niadra {
|
|
|
9139
9281
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9140
9282
|
}
|
|
9141
9283
|
}
|
|
9284
|
+
/**
|
|
9285
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9286
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9287
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9288
|
+
*/
|
|
9289
|
+
contextUse(params = {}, options = {}) {
|
|
9290
|
+
return this.navigate(() => {
|
|
9291
|
+
const { group_by: groups, ...filters } = params;
|
|
9292
|
+
const query = { ...filters };
|
|
9293
|
+
if (groups?.length) query.group_by = groups;
|
|
9294
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9295
|
+
});
|
|
9296
|
+
}
|
|
9297
|
+
/**
|
|
9298
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9299
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9300
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9301
|
+
*/
|
|
9302
|
+
link(params, options = {}) {
|
|
9303
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9304
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9305
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9306
|
+
}
|
|
9307
|
+
/**
|
|
9308
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9309
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9310
|
+
*/
|
|
9311
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9312
|
+
return this.navigate(() => {
|
|
9313
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9314
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9315
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9316
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9317
|
+
});
|
|
9318
|
+
}
|
|
9142
9319
|
/**
|
|
9143
9320
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9144
9321
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9231,16 +9408,20 @@ var Niadra = class _Niadra {
|
|
|
9231
9408
|
* emits `conversation.ended` when you call `end()`.
|
|
9232
9409
|
*/
|
|
9233
9410
|
conversation(params) {
|
|
9234
|
-
|
|
9411
|
+
const conversation = new Conversation(this, params, {
|
|
9235
9412
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9236
9413
|
});
|
|
9414
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9415
|
+
return conversation;
|
|
9237
9416
|
}
|
|
9238
9417
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9239
9418
|
task(params) {
|
|
9240
|
-
|
|
9419
|
+
const task = new Task(this, params, {
|
|
9241
9420
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9242
9421
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9243
9422
|
});
|
|
9423
|
+
this.warm(`task:${task.id}`, task);
|
|
9424
|
+
return task;
|
|
9244
9425
|
}
|
|
9245
9426
|
/**
|
|
9246
9427
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9260,6 +9441,8 @@ var Niadra = class _Niadra {
|
|
|
9260
9441
|
*/
|
|
9261
9442
|
async shutdown() {
|
|
9262
9443
|
this.unregisterExit();
|
|
9444
|
+
clearInterval(this.warmTimer);
|
|
9445
|
+
this.warmTimer = void 0;
|
|
9263
9446
|
if (!this.core) return;
|
|
9264
9447
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9265
9448
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9363,9 +9546,9 @@ var Niadra = class _Niadra {
|
|
|
9363
9546
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9364
9547
|
return normalizeContext(response.data);
|
|
9365
9548
|
} catch (error) {
|
|
9366
|
-
const
|
|
9549
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9367
9550
|
const left = timeout - (Date.now() - started);
|
|
9368
|
-
if (!request.include?.length || !
|
|
9551
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9369
9552
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9370
9553
|
const { include: _dropped, ...plain2 } = request;
|
|
9371
9554
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9433,6 +9616,7 @@ var Niadra = class _Niadra {
|
|
|
9433
9616
|
/** Measures the round trip to the region once per client, in the background. */
|
|
9434
9617
|
probe(core) {
|
|
9435
9618
|
if (!this.voice.claimProbe()) return;
|
|
9619
|
+
this.measuring = true;
|
|
9436
9620
|
void (async () => {
|
|
9437
9621
|
const samples = [];
|
|
9438
9622
|
for (let i = 0; i < 2; i++) {
|
|
@@ -9446,16 +9630,44 @@ var Niadra = class _Niadra {
|
|
|
9446
9630
|
});
|
|
9447
9631
|
} catch (error) {
|
|
9448
9632
|
this.logger.debug(`round trip probe failed: ${describe(toNiadraError(error))}`);
|
|
9633
|
+
this.measuring = false;
|
|
9449
9634
|
return;
|
|
9450
9635
|
}
|
|
9451
9636
|
samples.push(Date.now() - started);
|
|
9452
9637
|
}
|
|
9453
9638
|
const rtt = Math.min(...samples);
|
|
9454
9639
|
this.voice.rtt = rtt;
|
|
9640
|
+
this.measuring = false;
|
|
9455
9641
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9456
|
-
|
|
9642
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9643
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9644
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9457
9645
|
})();
|
|
9458
9646
|
}
|
|
9647
|
+
/**
|
|
9648
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9649
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9650
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9651
|
+
*/
|
|
9652
|
+
readBudget(name) {
|
|
9653
|
+
if (!this.defaultReads.has(name)) return this.timeouts[name];
|
|
9654
|
+
const rtt = this.voice.rtt;
|
|
9655
|
+
if (rtt !== null) return this.timeouts[name] + rtt;
|
|
9656
|
+
const open = this.core?.transport.connectionOpen() ?? false;
|
|
9657
|
+
return this.measuring && open ? this.timeouts[name] + this.timeouts.connect : this.timeouts[name];
|
|
9658
|
+
}
|
|
9659
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9660
|
+
startVoice(core) {
|
|
9661
|
+
this.voiceStarted = true;
|
|
9662
|
+
this.probe(core);
|
|
9663
|
+
this.warnVoice();
|
|
9664
|
+
}
|
|
9665
|
+
warnVoice() {
|
|
9666
|
+
const rtt = this.voice.rtt;
|
|
9667
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9668
|
+
this.voiceWarned = true;
|
|
9669
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9670
|
+
}
|
|
9459
9671
|
/**
|
|
9460
9672
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9461
9673
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9465,7 +9677,7 @@ var Niadra = class _Niadra {
|
|
|
9465
9677
|
const key2 = cacheKey(request);
|
|
9466
9678
|
const deadline = Date.now() + timeout;
|
|
9467
9679
|
const line = this.voice.line(scope);
|
|
9468
|
-
this.
|
|
9680
|
+
this.startVoice(core);
|
|
9469
9681
|
line.request = request;
|
|
9470
9682
|
const words2 = wordsOf(query);
|
|
9471
9683
|
const background = this.timeouts.prefetch;
|
|
@@ -9651,8 +9863,39 @@ var Niadra = class _Niadra {
|
|
|
9651
9863
|
core.queue.flushInBackground(true);
|
|
9652
9864
|
});
|
|
9653
9865
|
}
|
|
9866
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9867
|
+
warm(scope, session) {
|
|
9868
|
+
const core = this.core;
|
|
9869
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9870
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9871
|
+
if (this.warmTimer !== void 0) return;
|
|
9872
|
+
const timer = setInterval(() => {
|
|
9873
|
+
this.warmTick(core);
|
|
9874
|
+
}, EVERY_MS);
|
|
9875
|
+
timer.unref?.();
|
|
9876
|
+
this.warmTimer = timer;
|
|
9877
|
+
}
|
|
9878
|
+
warmTick(core) {
|
|
9879
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9880
|
+
if (step === "stop") {
|
|
9881
|
+
clearInterval(this.warmTimer);
|
|
9882
|
+
this.warmTimer = void 0;
|
|
9883
|
+
return;
|
|
9884
|
+
}
|
|
9885
|
+
if (step !== "ping") return;
|
|
9886
|
+
core.transport.request({
|
|
9887
|
+
method: "GET",
|
|
9888
|
+
path: "/healthz",
|
|
9889
|
+
timeoutMs: 2e3,
|
|
9890
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9891
|
+
activity: false
|
|
9892
|
+
}).catch((error) => {
|
|
9893
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9894
|
+
});
|
|
9895
|
+
}
|
|
9654
9896
|
async endScope(item, scope) {
|
|
9655
9897
|
this.forgetScope(scope);
|
|
9898
|
+
this.keepWarm.end(scope);
|
|
9656
9899
|
return this.sendNow(() => item);
|
|
9657
9900
|
}
|
|
9658
9901
|
async sendBatch(transport, items2, maxAttempts, backoff) {
|
|
@@ -11507,7 +11750,7 @@ var Replayer = class {
|
|
|
11507
11750
|
try {
|
|
11508
11751
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
11509
11752
|
} catch (error) {
|
|
11510
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
11753
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
11511
11754
|
throw error;
|
|
11512
11755
|
}
|
|
11513
11756
|
let run = runOf(answer);
|
|
@@ -11610,7 +11853,7 @@ var Replayer = class {
|
|
|
11610
11853
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
11611
11854
|
}
|
|
11612
11855
|
};
|
|
11613
|
-
function
|
|
11856
|
+
function refused2(scenarioIds, results) {
|
|
11614
11857
|
const scenarios = scenarioIds.map((id) => {
|
|
11615
11858
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
11616
11859
|
return {
|