@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.cjs
CHANGED
|
@@ -6,6 +6,46 @@ var __export = (target, all) => {
|
|
|
6
6
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
// src/ids.ts
|
|
10
|
+
var lastMs = -1;
|
|
11
|
+
var counter = 0;
|
|
12
|
+
function randomBytes(length) {
|
|
13
|
+
const bytes = new Uint8Array(length);
|
|
14
|
+
const webCrypto = globalThis.crypto;
|
|
15
|
+
if (webCrypto?.getRandomValues) {
|
|
16
|
+
webCrypto.getRandomValues(bytes);
|
|
17
|
+
} else {
|
|
18
|
+
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
19
|
+
}
|
|
20
|
+
return bytes;
|
|
21
|
+
}
|
|
22
|
+
function uuidv7(now = Date.now()) {
|
|
23
|
+
let ms = now;
|
|
24
|
+
if (ms <= lastMs) {
|
|
25
|
+
ms = lastMs;
|
|
26
|
+
counter = counter + 1 & 4095;
|
|
27
|
+
if (counter === 0) ms += 1;
|
|
28
|
+
} else {
|
|
29
|
+
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
30
|
+
}
|
|
31
|
+
lastMs = ms;
|
|
32
|
+
const bytes = randomBytes(16);
|
|
33
|
+
const high = Math.floor(ms / 2 ** 16);
|
|
34
|
+
const low = ms % 2 ** 16;
|
|
35
|
+
bytes[0] = high >>> 24 & 255;
|
|
36
|
+
bytes[1] = high >>> 16 & 255;
|
|
37
|
+
bytes[2] = high >>> 8 & 255;
|
|
38
|
+
bytes[3] = high & 255;
|
|
39
|
+
bytes[4] = low >>> 8 & 255;
|
|
40
|
+
bytes[5] = low & 255;
|
|
41
|
+
bytes[6] = 112 | counter >>> 8 & 15;
|
|
42
|
+
bytes[7] = counter & 255;
|
|
43
|
+
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
44
|
+
let hex2 = "";
|
|
45
|
+
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
46
|
+
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
9
49
|
// src/errors.ts
|
|
10
50
|
var NiadraError = class extends Error {
|
|
11
51
|
name = "NiadraError";
|
|
@@ -49,11 +89,12 @@ var NiadraAPIError = class extends NiadraError {
|
|
|
49
89
|
constructor(status, problem, requestId, retryAfterMs = null) {
|
|
50
90
|
const code = problem?.code ?? `http_${status}`;
|
|
51
91
|
const detail = problem?.detail ? `: ${problem.detail}` : "";
|
|
52
|
-
|
|
92
|
+
const id = requestId ?? problem?.request_id ?? null;
|
|
93
|
+
super(`${status} ${code}${detail}${id ? ` (request ${id})` : ""}`);
|
|
53
94
|
this.status = status;
|
|
54
95
|
this.code = code;
|
|
55
96
|
this.problem = problem;
|
|
56
|
-
this.requestId =
|
|
97
|
+
this.requestId = id;
|
|
57
98
|
this.retryAfterMs = retryAfterMs;
|
|
58
99
|
}
|
|
59
100
|
};
|
|
@@ -77,45 +118,11 @@ function toNiadraError(error) {
|
|
|
77
118
|
const message = error instanceof Error ? error.message : String(error);
|
|
78
119
|
return new NiadraError(message, { cause: error });
|
|
79
120
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const bytes = new Uint8Array(length);
|
|
86
|
-
const webCrypto = globalThis.crypto;
|
|
87
|
-
if (webCrypto?.getRandomValues) {
|
|
88
|
-
webCrypto.getRandomValues(bytes);
|
|
89
|
-
} else {
|
|
90
|
-
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
91
|
-
}
|
|
92
|
-
return bytes;
|
|
93
|
-
}
|
|
94
|
-
function uuidv7(now = Date.now()) {
|
|
95
|
-
let ms = now;
|
|
96
|
-
if (ms <= lastMs) {
|
|
97
|
-
ms = lastMs;
|
|
98
|
-
counter = counter + 1 & 4095;
|
|
99
|
-
if (counter === 0) ms += 1;
|
|
100
|
-
} else {
|
|
101
|
-
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
102
|
-
}
|
|
103
|
-
lastMs = ms;
|
|
104
|
-
const bytes = randomBytes(16);
|
|
105
|
-
const high = Math.floor(ms / 2 ** 16);
|
|
106
|
-
const low = ms % 2 ** 16;
|
|
107
|
-
bytes[0] = high >>> 24 & 255;
|
|
108
|
-
bytes[1] = high >>> 16 & 255;
|
|
109
|
-
bytes[2] = high >>> 8 & 255;
|
|
110
|
-
bytes[3] = high & 255;
|
|
111
|
-
bytes[4] = low >>> 8 & 255;
|
|
112
|
-
bytes[5] = low & 255;
|
|
113
|
-
bytes[6] = 112 | counter >>> 8 & 15;
|
|
114
|
-
bytes[7] = counter & 255;
|
|
115
|
-
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
116
|
-
let hex2 = "";
|
|
117
|
-
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
118
|
-
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
121
|
+
function explain(error) {
|
|
122
|
+
if (!(error instanceof NiadraAPIError)) return error instanceof Error ? error.name : String(error);
|
|
123
|
+
const detail = error.problem?.detail?.trim();
|
|
124
|
+
const said = `${error.status} ${error.code}${detail ? `: ${detail.slice(0, 300)}` : ""}`;
|
|
125
|
+
return error.requestId ? `${said} (request ${error.requestId})` : said;
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
// src/admin.ts
|
|
@@ -518,7 +525,7 @@ function internalRecord(span, ref, act) {
|
|
|
518
525
|
}
|
|
519
526
|
|
|
520
527
|
// src/version.ts
|
|
521
|
-
var VERSION = "0.
|
|
528
|
+
var VERSION = "0.10.4";
|
|
522
529
|
|
|
523
530
|
// src/transport.ts
|
|
524
531
|
var RETRYABLE_WRITE_STATUS = /* @__PURE__ */ new Set([408, 421, 429, 500, 502, 503, 504]);
|
|
@@ -531,8 +538,36 @@ var Transport = class {
|
|
|
531
538
|
}
|
|
532
539
|
config;
|
|
533
540
|
baseURL;
|
|
541
|
+
answeredAt;
|
|
542
|
+
/** When the allowance was first given since the last answer; it holds for the calls of that moment only. */
|
|
543
|
+
grantedAt;
|
|
544
|
+
/** When the client last sent a request of its own (`RequestSpec.activity`), in epoch milliseconds. */
|
|
545
|
+
lastActivityAt;
|
|
546
|
+
/** Likely: an answer came within `keepAliveMs`. When not, the next budgeted call gets `coldAllowanceMs`. */
|
|
547
|
+
connectionOpen() {
|
|
548
|
+
return this.answeredAt !== void 0 && Date.now() - this.answeredAt <= (this.config.keepAliveMs ?? 0);
|
|
549
|
+
}
|
|
534
550
|
async request(spec) {
|
|
535
|
-
|
|
551
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
552
|
+
const budgeted = this.withAllowance(spec);
|
|
553
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
557
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
558
|
+
* outage costs the allowance once, not on every turn.
|
|
559
|
+
*/
|
|
560
|
+
withAllowance(spec) {
|
|
561
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
562
|
+
const { retry } = spec;
|
|
563
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
564
|
+
const now = Date.now();
|
|
565
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
566
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
567
|
+
this.grantedAt ??= now;
|
|
568
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
569
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
570
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
536
571
|
}
|
|
537
572
|
async read(spec, policy) {
|
|
538
573
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -603,7 +638,18 @@ var Transport = class {
|
|
|
603
638
|
headers["content-type"] = "application/json";
|
|
604
639
|
init.body = JSON.stringify(spec.body);
|
|
605
640
|
}
|
|
606
|
-
|
|
641
|
+
try {
|
|
642
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
643
|
+
this.answered();
|
|
644
|
+
return answer;
|
|
645
|
+
} catch (error) {
|
|
646
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
answered() {
|
|
651
|
+
this.answeredAt = Date.now();
|
|
652
|
+
this.grantedAt = void 0;
|
|
607
653
|
}
|
|
608
654
|
async exchange(url, init, deadline) {
|
|
609
655
|
let response;
|
|
@@ -4044,8 +4090,7 @@ var TurnSender = class {
|
|
|
4044
4090
|
return true;
|
|
4045
4091
|
}
|
|
4046
4092
|
}
|
|
4047
|
-
|
|
4048
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4093
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4049
4094
|
return true;
|
|
4050
4095
|
}
|
|
4051
4096
|
accepted(batch, answer) {
|
|
@@ -4053,8 +4098,8 @@ var TurnSender = class {
|
|
|
4053
4098
|
this.resumeAt = 0;
|
|
4054
4099
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4055
4100
|
const again = [];
|
|
4056
|
-
const
|
|
4057
|
-
let
|
|
4101
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4102
|
+
let refused3 = 0;
|
|
4058
4103
|
for (const error of answer.errors ?? []) {
|
|
4059
4104
|
const frame = batch.frames[error.index];
|
|
4060
4105
|
if (frame === void 0) continue;
|
|
@@ -4063,12 +4108,12 @@ var TurnSender = class {
|
|
|
4063
4108
|
this.recorder.modeRefused();
|
|
4064
4109
|
again.push(frame);
|
|
4065
4110
|
} else {
|
|
4066
|
-
|
|
4067
|
-
|
|
4111
|
+
refused3++;
|
|
4112
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4068
4113
|
}
|
|
4069
4114
|
}
|
|
4070
4115
|
if (again.length > 0) this.queue.requeue(again);
|
|
4071
|
-
if (
|
|
4116
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4072
4117
|
}
|
|
4073
4118
|
};
|
|
4074
4119
|
|
|
@@ -4190,9 +4235,12 @@ var TurnRecorder = class {
|
|
|
4190
4235
|
this.accepted += accepted;
|
|
4191
4236
|
this.duplicates += duplicates;
|
|
4192
4237
|
}
|
|
4193
|
-
|
|
4238
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4239
|
+
rejected(count3, reasons) {
|
|
4194
4240
|
this.rejectedTurns += count3;
|
|
4195
|
-
|
|
4241
|
+
const shown = [...new Set(reasons)].sort();
|
|
4242
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4243
|
+
this.logger.warn(`${count3} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4196
4244
|
}
|
|
4197
4245
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4198
4246
|
notRecorded(count3, off) {
|
|
@@ -4262,6 +4310,10 @@ async function hex(text2) {
|
|
|
4262
4310
|
}
|
|
4263
4311
|
|
|
4264
4312
|
// src/coordination/client.ts
|
|
4313
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4314
|
+
function refused(error) {
|
|
4315
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4316
|
+
}
|
|
4265
4317
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4266
4318
|
var CHECK_BUDGET_MS = 200;
|
|
4267
4319
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4283,15 +4335,22 @@ function claimed(data, error) {
|
|
|
4283
4335
|
return { held: false, claim: null, error: code };
|
|
4284
4336
|
}
|
|
4285
4337
|
var Coordinator = class {
|
|
4286
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4338
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4287
4339
|
this.outbox = outbox;
|
|
4288
4340
|
this.suppressions = suppressions;
|
|
4289
4341
|
this.declareNow = declareNow;
|
|
4342
|
+
this.logger = logger;
|
|
4290
4343
|
}
|
|
4291
4344
|
outbox;
|
|
4292
4345
|
suppressions;
|
|
4293
4346
|
declareNow;
|
|
4347
|
+
logger;
|
|
4294
4348
|
async failed(request, failOpen, error) {
|
|
4349
|
+
if (refused(error)) {
|
|
4350
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4351
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4352
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4353
|
+
}
|
|
4295
4354
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4296
4355
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4297
4356
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4312,7 +4371,7 @@ var Coordinator = class {
|
|
|
4312
4371
|
if (who.subject) body.subject = who.subject;
|
|
4313
4372
|
if (who.object) body.object = who.object;
|
|
4314
4373
|
const key2 = uuidv7();
|
|
4315
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4374
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4316
4375
|
return key2;
|
|
4317
4376
|
}
|
|
4318
4377
|
};
|
|
@@ -4831,7 +4890,7 @@ var Outbox = class {
|
|
|
4831
4890
|
return false;
|
|
4832
4891
|
}
|
|
4833
4892
|
this.writes.shift();
|
|
4834
|
-
this.logger.warn(
|
|
4893
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4835
4894
|
this.settle(write, void 0, error);
|
|
4836
4895
|
return true;
|
|
4837
4896
|
}
|
|
@@ -5721,9 +5780,17 @@ var handles = {
|
|
|
5721
5780
|
appUserId: (value, options) => build("app_user_id", value, void 0, options),
|
|
5722
5781
|
/** The id of a person or organization in a system of record; `system` names that system, such as `crm`. */
|
|
5723
5782
|
systemId: (value, system, options) => build("system_id", value, system, options),
|
|
5724
|
-
/**
|
|
5783
|
+
/**
|
|
5784
|
+
* A person's national document, such as a CPF: `govIdHmac("529.982.247-25", "BR")`. Send the number
|
|
5785
|
+
* itself, never a hash of it: the server checks its check digits, then keeps only a keyed hash with your
|
|
5786
|
+
* space's secret and an encrypted copy, and shows it masked. `country` is its ISO 3166-1 alpha-2 code.
|
|
5787
|
+
*/
|
|
5725
5788
|
govIdHmac: (value, country, options) => build("gov_id_hmac", value, country, options),
|
|
5726
|
-
/**
|
|
5789
|
+
/**
|
|
5790
|
+
* A company's registry number, such as a CNPJ: `orgRegistryHmac("11.222.333/0001-81", "BR")`. As with
|
|
5791
|
+
* `govIdHmac`, send the number itself; the server checks it and keeps only a keyed hash. Identifies an
|
|
5792
|
+
* organization.
|
|
5793
|
+
*/
|
|
5727
5794
|
orgRegistryHmac: (value, country) => build("org_registry_hmac", value, country, { subjectKind: "account" }),
|
|
5728
5795
|
/** An e-mail domain, such as `acme.com`. Identifies an organization. */
|
|
5729
5796
|
emailDomain: (domain) => build("email_domain", domain, void 0, { subjectKind: "account" }),
|
|
@@ -6397,6 +6464,7 @@ var AgentSession = class {
|
|
|
6397
6464
|
try {
|
|
6398
6465
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6399
6466
|
} catch (error) {
|
|
6467
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6400
6468
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6401
6469
|
}
|
|
6402
6470
|
}
|
|
@@ -7008,6 +7076,7 @@ var SessionState = class {
|
|
|
7008
7076
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
7009
7077
|
guards = /* @__PURE__ */ new Map();
|
|
7010
7078
|
lastReport = null;
|
|
7079
|
+
unlinkedSaid = false;
|
|
7011
7080
|
/** After the first pack, every read also asks what changed since. */
|
|
7012
7081
|
get wantsDelta() {
|
|
7013
7082
|
return this.etag !== null;
|
|
@@ -7036,6 +7105,19 @@ var SessionState = class {
|
|
|
7036
7105
|
this.last = absorbed;
|
|
7037
7106
|
return absorbed;
|
|
7038
7107
|
}
|
|
7108
|
+
/**
|
|
7109
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7110
|
+
* on with the subject's own memory, never the organization's.
|
|
7111
|
+
*/
|
|
7112
|
+
sayUnlinked(result2, logger) {
|
|
7113
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7114
|
+
this.unlinkedSaid = true;
|
|
7115
|
+
logger.warn(
|
|
7116
|
+
"`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."
|
|
7117
|
+
);
|
|
7118
|
+
}
|
|
7119
|
+
return result2;
|
|
7120
|
+
}
|
|
7039
7121
|
/** The last backing check of an agent's answer. */
|
|
7040
7122
|
get lastBacking() {
|
|
7041
7123
|
return this.lastReport;
|
|
@@ -7283,7 +7365,7 @@ var Conversation = class {
|
|
|
7283
7365
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7284
7366
|
*/
|
|
7285
7367
|
async context(options = {}) {
|
|
7286
|
-
const { query, turn, format, explain, include, ...requestOptions } = options;
|
|
7368
|
+
const { query, turn, format, explain: explain2, include, ...requestOptions } = options;
|
|
7287
7369
|
const params = {
|
|
7288
7370
|
subject: this.subject,
|
|
7289
7371
|
view: this.view,
|
|
@@ -7292,7 +7374,7 @@ var Conversation = class {
|
|
|
7292
7374
|
...this.params.about ? { about: this.params.about } : {},
|
|
7293
7375
|
...this.params.target ? { target: this.params.target } : {},
|
|
7294
7376
|
...format === "json" ? { format } : {},
|
|
7295
|
-
...
|
|
7377
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7296
7378
|
};
|
|
7297
7379
|
if (query) params.query = query;
|
|
7298
7380
|
if (include?.length) params.include = include;
|
|
@@ -7300,7 +7382,7 @@ var Conversation = class {
|
|
|
7300
7382
|
params.turn = turn === void 0 ? this.turnText : turn;
|
|
7301
7383
|
const result2 = await this.client.context(params, requestOptions);
|
|
7302
7384
|
this.features.observe(result2);
|
|
7303
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7385
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7304
7386
|
}
|
|
7305
7387
|
/**
|
|
7306
7388
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7480,6 +7562,35 @@ function withHandles(base, extra) {
|
|
|
7480
7562
|
return all;
|
|
7481
7563
|
}
|
|
7482
7564
|
|
|
7565
|
+
// src/warm.ts
|
|
7566
|
+
var EVERY_MS = 1e5;
|
|
7567
|
+
var IDLE_MS = 9e4;
|
|
7568
|
+
var WARM_FOR_MS = 6e5;
|
|
7569
|
+
var KeepWarm = class {
|
|
7570
|
+
constructor(enabled) {
|
|
7571
|
+
this.enabled = enabled;
|
|
7572
|
+
}
|
|
7573
|
+
enabled;
|
|
7574
|
+
open = /* @__PURE__ */ new Map();
|
|
7575
|
+
openedAt = 0;
|
|
7576
|
+
/** Notes an open conversation or task. */
|
|
7577
|
+
add(scope, session, now) {
|
|
7578
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7579
|
+
this.open.set(scope, new WeakRef(session));
|
|
7580
|
+
this.openedAt = now;
|
|
7581
|
+
}
|
|
7582
|
+
/** The conversation or task ended. */
|
|
7583
|
+
end(scope) {
|
|
7584
|
+
this.open.delete(scope);
|
|
7585
|
+
}
|
|
7586
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7587
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7588
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7589
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7590
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7591
|
+
}
|
|
7592
|
+
};
|
|
7593
|
+
|
|
7483
7594
|
// src/exit.ts
|
|
7484
7595
|
var registered = /* @__PURE__ */ new Set();
|
|
7485
7596
|
var installedOn = null;
|
|
@@ -7610,8 +7721,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7610
7721
|
write: 5e3,
|
|
7611
7722
|
token: 2e3,
|
|
7612
7723
|
upload: 6e4,
|
|
7613
|
-
prefetch: 1e3
|
|
7724
|
+
prefetch: 1e3,
|
|
7725
|
+
connect: 1e3
|
|
7614
7726
|
};
|
|
7727
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7615
7728
|
var DEFAULT_CACHE = {
|
|
7616
7729
|
ttlMs: 1e4,
|
|
7617
7730
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7718,7 +7831,7 @@ var Task = class {
|
|
|
7718
7831
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7719
7832
|
*/
|
|
7720
7833
|
async context(options = {}) {
|
|
7721
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7834
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7722
7835
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7723
7836
|
const params = {
|
|
7724
7837
|
...target,
|
|
@@ -7728,18 +7841,18 @@ var Task = class {
|
|
|
7728
7841
|
...this.level ? { verification: this.level } : {},
|
|
7729
7842
|
...this.params.target ? { target: this.params.target } : {},
|
|
7730
7843
|
...format === "json" ? { format } : {},
|
|
7731
|
-
...
|
|
7844
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7732
7845
|
...include?.length ? { include } : {}
|
|
7733
7846
|
};
|
|
7734
7847
|
if (query) {
|
|
7735
7848
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7736
7849
|
this.features.observe(answered);
|
|
7737
|
-
return this.state.observe(answered);
|
|
7850
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7738
7851
|
}
|
|
7739
7852
|
if (this.state.wantsDelta) params.delta = true;
|
|
7740
7853
|
const result2 = await this.client.context(params, requestOptions);
|
|
7741
7854
|
this.features.observe(result2);
|
|
7742
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7855
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7743
7856
|
}
|
|
7744
7857
|
/**
|
|
7745
7858
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7869,14 +7982,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7869
7982
|
search: "search_agent_memory",
|
|
7870
7983
|
remember: "remember"
|
|
7871
7984
|
};
|
|
7872
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7985
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7873
7986
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7874
7987
|
var TOOL_DEFINITIONS = [
|
|
7875
7988
|
{
|
|
7876
7989
|
"type": "function",
|
|
7877
7990
|
"function": {
|
|
7878
7991
|
"name": "search_customer_history",
|
|
7879
|
-
"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 '
|
|
7992
|
+
"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.",
|
|
7880
7993
|
"parameters": {
|
|
7881
7994
|
"type": "object",
|
|
7882
7995
|
"properties": {
|
|
@@ -7920,7 +8033,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7920
8033
|
"open_item",
|
|
7921
8034
|
"action",
|
|
7922
8035
|
"object",
|
|
7923
|
-
"trait"
|
|
8036
|
+
"trait",
|
|
8037
|
+
"system_event"
|
|
7924
8038
|
]
|
|
7925
8039
|
}
|
|
7926
8040
|
},
|
|
@@ -7947,7 +8061,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7947
8061
|
"type": "function",
|
|
7948
8062
|
"function": {
|
|
7949
8063
|
"name": "get_customer_timeline",
|
|
7950
|
-
"description": "List this customer's conversations
|
|
8064
|
+
"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.",
|
|
7951
8065
|
"parameters": {
|
|
7952
8066
|
"type": "object",
|
|
7953
8067
|
"properties": {
|
|
@@ -7995,7 +8109,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7995
8109
|
"open_item",
|
|
7996
8110
|
"action",
|
|
7997
8111
|
"object",
|
|
7998
|
-
"trait"
|
|
8112
|
+
"trait",
|
|
8113
|
+
"system_event"
|
|
7999
8114
|
]
|
|
8000
8115
|
}
|
|
8001
8116
|
},
|
|
@@ -8014,7 +8129,7 @@ var TOOL_DEFINITIONS = [
|
|
|
8014
8129
|
"type": "function",
|
|
8015
8130
|
"function": {
|
|
8016
8131
|
"name": "open_history_item",
|
|
8017
|
-
"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.",
|
|
8132
|
+
"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.",
|
|
8018
8133
|
"parameters": {
|
|
8019
8134
|
"type": "object",
|
|
8020
8135
|
"properties": {
|
|
@@ -8385,6 +8500,13 @@ function compose(body, read) {
|
|
|
8385
8500
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8386
8501
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8387
8502
|
}
|
|
8503
|
+
var RTT_MARGIN_MS = 50;
|
|
8504
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8505
|
+
const ms = Math.round(rttMs);
|
|
8506
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8507
|
+
(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`
|
|
8508
|
+
);
|
|
8509
|
+
}
|
|
8388
8510
|
function rttWarnings(rttMs, timeouts) {
|
|
8389
8511
|
const ms = Math.round(rttMs);
|
|
8390
8512
|
const found2 = [];
|
|
@@ -8422,6 +8544,14 @@ var Niadra = class _Niadra {
|
|
|
8422
8544
|
enabled;
|
|
8423
8545
|
core;
|
|
8424
8546
|
timeouts;
|
|
8547
|
+
keepWarm;
|
|
8548
|
+
warmTimer;
|
|
8549
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8550
|
+
defaultReads;
|
|
8551
|
+
voiceStarted = false;
|
|
8552
|
+
/** A probe is on its way: until it ends, default read budgets get `timeouts.connect` on top. */
|
|
8553
|
+
measuring = false;
|
|
8554
|
+
voiceWarned = false;
|
|
8425
8555
|
strict;
|
|
8426
8556
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8427
8557
|
logger;
|
|
@@ -8466,6 +8596,8 @@ var Niadra = class _Niadra {
|
|
|
8466
8596
|
this.strict = options.strict ?? false;
|
|
8467
8597
|
this.logger = options.logger ?? consoleLogger;
|
|
8468
8598
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8599
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8600
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8469
8601
|
this.voice = new VoiceLines(
|
|
8470
8602
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8471
8603
|
);
|
|
@@ -8491,13 +8623,14 @@ var Niadra = class _Niadra {
|
|
|
8491
8623
|
this.coordinator = new Coordinator(
|
|
8492
8624
|
this.outbox,
|
|
8493
8625
|
this.suppressions,
|
|
8494
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8626
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8627
|
+
this.logger
|
|
8495
8628
|
);
|
|
8496
8629
|
this.states = new AgentStates(
|
|
8497
8630
|
this.outbox,
|
|
8498
8631
|
{
|
|
8499
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8500
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8632
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8633
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8501
8634
|
},
|
|
8502
8635
|
this.logger
|
|
8503
8636
|
);
|
|
@@ -8524,6 +8657,7 @@ var Niadra = class _Niadra {
|
|
|
8524
8657
|
this.core = setup;
|
|
8525
8658
|
this.enabled = true;
|
|
8526
8659
|
this.disabledReason = null;
|
|
8660
|
+
this.probe(setup);
|
|
8527
8661
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8528
8662
|
}
|
|
8529
8663
|
setup(options) {
|
|
@@ -8546,7 +8680,9 @@ var Niadra = class _Niadra {
|
|
|
8546
8680
|
apiKey,
|
|
8547
8681
|
fetch: fetchImpl,
|
|
8548
8682
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
8549
|
-
logger: this.logger
|
|
8683
|
+
logger: this.logger,
|
|
8684
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8685
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8550
8686
|
});
|
|
8551
8687
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8552
8688
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8602,7 +8738,7 @@ var Niadra = class _Niadra {
|
|
|
8602
8738
|
return result2;
|
|
8603
8739
|
}
|
|
8604
8740
|
readContext(core, params, request, options) {
|
|
8605
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8741
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8606
8742
|
const { query: own3, ...pinned2 } = request;
|
|
8607
8743
|
const query = turnText(own3 ?? params.turn);
|
|
8608
8744
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8618,7 +8754,7 @@ var Niadra = class _Niadra {
|
|
|
8618
8754
|
*/
|
|
8619
8755
|
async profile() {
|
|
8620
8756
|
if (!this.core) return null;
|
|
8621
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8757
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8622
8758
|
}
|
|
8623
8759
|
/**
|
|
8624
8760
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8643,7 +8779,7 @@ var Niadra = class _Niadra {
|
|
|
8643
8779
|
*/
|
|
8644
8780
|
async mayContact(handle, purpose, options = {}) {
|
|
8645
8781
|
if (this.core && this.suppressions.due()) {
|
|
8646
|
-
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8782
|
+
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8647
8783
|
if (!this.suppressions.held) await read;
|
|
8648
8784
|
}
|
|
8649
8785
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8653,8 +8789,8 @@ var Niadra = class _Niadra {
|
|
|
8653
8789
|
readSuppressions(budgetMs) {
|
|
8654
8790
|
return this.suppressions.read(
|
|
8655
8791
|
{
|
|
8656
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8657
|
-
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.
|
|
8792
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8793
|
+
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.readBudget("navigation") })
|
|
8658
8794
|
},
|
|
8659
8795
|
budgetMs
|
|
8660
8796
|
);
|
|
@@ -8733,6 +8869,7 @@ var Niadra = class _Niadra {
|
|
|
8733
8869
|
}
|
|
8734
8870
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8735
8871
|
get agentHost() {
|
|
8872
|
+
const client = this;
|
|
8736
8873
|
return {
|
|
8737
8874
|
recorder: this.turns,
|
|
8738
8875
|
coordinator: this.coordinator,
|
|
@@ -8746,7 +8883,10 @@ var Niadra = class _Niadra {
|
|
|
8746
8883
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8747
8884
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8748
8885
|
enabled: this.enabled,
|
|
8749
|
-
|
|
8886
|
+
strict: this.strict,
|
|
8887
|
+
get navigationMs() {
|
|
8888
|
+
return client.readBudget("navigation");
|
|
8889
|
+
}
|
|
8750
8890
|
};
|
|
8751
8891
|
}
|
|
8752
8892
|
/**
|
|
@@ -8774,7 +8914,7 @@ var Niadra = class _Niadra {
|
|
|
8774
8914
|
if (!cache) return false;
|
|
8775
8915
|
const key2 = cacheKey(request);
|
|
8776
8916
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8777
|
-
this.
|
|
8917
|
+
this.startVoice(core);
|
|
8778
8918
|
line.request = request;
|
|
8779
8919
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8780
8920
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8911,13 +9051,13 @@ var Niadra = class _Niadra {
|
|
|
8911
9051
|
if (!params.query || params.query.length > 2e3) {
|
|
8912
9052
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8913
9053
|
}
|
|
8914
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
9054
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8915
9055
|
});
|
|
8916
9056
|
}
|
|
8917
9057
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8918
9058
|
async timeline(params, options = {}) {
|
|
8919
9059
|
return this.navigate(
|
|
8920
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
9060
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8921
9061
|
);
|
|
8922
9062
|
}
|
|
8923
9063
|
/**
|
|
@@ -8930,9 +9070,10 @@ var Niadra = class _Niadra {
|
|
|
8930
9070
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8931
9071
|
const body = { item_id: id };
|
|
8932
9072
|
if (params.subject) body.subject = params.subject;
|
|
9073
|
+
if (params.about) body.about = params.about;
|
|
8933
9074
|
if (params.verification) body.verification = params.verification;
|
|
8934
9075
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8935
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9076
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8936
9077
|
});
|
|
8937
9078
|
}
|
|
8938
9079
|
/**
|
|
@@ -8944,7 +9085,7 @@ var Niadra = class _Niadra {
|
|
|
8944
9085
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8945
9086
|
*/
|
|
8946
9087
|
async objectState(object, options = {}) {
|
|
8947
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9088
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8948
9089
|
}
|
|
8949
9090
|
/**
|
|
8950
9091
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8957,7 +9098,7 @@ var Niadra = class _Niadra {
|
|
|
8957
9098
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8958
9099
|
}
|
|
8959
9100
|
const path = `${objectPath(object)}/timeline`;
|
|
8960
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9101
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8961
9102
|
spec.query = { cursor: params.cursor, limit: String(limit3) };
|
|
8962
9103
|
return spec;
|
|
8963
9104
|
});
|
|
@@ -8976,6 +9117,7 @@ var Niadra = class _Niadra {
|
|
|
8976
9117
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8977
9118
|
open: (id, customer, bound, voice) => {
|
|
8978
9119
|
const scope = { subject: customer };
|
|
9120
|
+
if (bound.about) scope.about = bound.about;
|
|
8979
9121
|
if (bound.verification) scope.verification = bound.verification;
|
|
8980
9122
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8981
9123
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -9010,7 +9152,7 @@ var Niadra = class _Niadra {
|
|
|
9010
9152
|
const key2 = AgentMemoryCache.key(params);
|
|
9011
9153
|
const fresh = cache?.fresh(key2);
|
|
9012
9154
|
if (fresh) return blockResult(fresh, "cache");
|
|
9013
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9155
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
9014
9156
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
9015
9157
|
spec.query = {
|
|
9016
9158
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -9047,7 +9189,7 @@ var Niadra = class _Niadra {
|
|
|
9047
9189
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
9048
9190
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9049
9191
|
if (params.task_id) body.task_id = params.task_id;
|
|
9050
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9192
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9051
9193
|
});
|
|
9052
9194
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9053
9195
|
}
|
|
@@ -9141,6 +9283,41 @@ var Niadra = class _Niadra {
|
|
|
9141
9283
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9142
9284
|
}
|
|
9143
9285
|
}
|
|
9286
|
+
/**
|
|
9287
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9288
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9289
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9290
|
+
*/
|
|
9291
|
+
contextUse(params = {}, options = {}) {
|
|
9292
|
+
return this.navigate(() => {
|
|
9293
|
+
const { group_by: groups, ...filters } = params;
|
|
9294
|
+
const query = { ...filters };
|
|
9295
|
+
if (groups?.length) query.group_by = groups;
|
|
9296
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
/**
|
|
9300
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9301
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9302
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9303
|
+
*/
|
|
9304
|
+
link(params, options = {}) {
|
|
9305
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9306
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9307
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9308
|
+
}
|
|
9309
|
+
/**
|
|
9310
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9311
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9312
|
+
*/
|
|
9313
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9314
|
+
return this.navigate(() => {
|
|
9315
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9316
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9317
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9318
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9319
|
+
});
|
|
9320
|
+
}
|
|
9144
9321
|
/**
|
|
9145
9322
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9146
9323
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9233,16 +9410,20 @@ var Niadra = class _Niadra {
|
|
|
9233
9410
|
* emits `conversation.ended` when you call `end()`.
|
|
9234
9411
|
*/
|
|
9235
9412
|
conversation(params) {
|
|
9236
|
-
|
|
9413
|
+
const conversation = new Conversation(this, params, {
|
|
9237
9414
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9238
9415
|
});
|
|
9416
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9417
|
+
return conversation;
|
|
9239
9418
|
}
|
|
9240
9419
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9241
9420
|
task(params) {
|
|
9242
|
-
|
|
9421
|
+
const task = new Task(this, params, {
|
|
9243
9422
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9244
9423
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9245
9424
|
});
|
|
9425
|
+
this.warm(`task:${task.id}`, task);
|
|
9426
|
+
return task;
|
|
9246
9427
|
}
|
|
9247
9428
|
/**
|
|
9248
9429
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9262,6 +9443,8 @@ var Niadra = class _Niadra {
|
|
|
9262
9443
|
*/
|
|
9263
9444
|
async shutdown() {
|
|
9264
9445
|
this.unregisterExit();
|
|
9446
|
+
clearInterval(this.warmTimer);
|
|
9447
|
+
this.warmTimer = void 0;
|
|
9265
9448
|
if (!this.core) return;
|
|
9266
9449
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9267
9450
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9365,9 +9548,9 @@ var Niadra = class _Niadra {
|
|
|
9365
9548
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9366
9549
|
return normalizeContext(response.data);
|
|
9367
9550
|
} catch (error) {
|
|
9368
|
-
const
|
|
9551
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9369
9552
|
const left = timeout - (Date.now() - started);
|
|
9370
|
-
if (!request.include?.length || !
|
|
9553
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9371
9554
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9372
9555
|
const { include: _dropped, ...plain2 } = request;
|
|
9373
9556
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9435,6 +9618,7 @@ var Niadra = class _Niadra {
|
|
|
9435
9618
|
/** Measures the round trip to the region once per client, in the background. */
|
|
9436
9619
|
probe(core) {
|
|
9437
9620
|
if (!this.voice.claimProbe()) return;
|
|
9621
|
+
this.measuring = true;
|
|
9438
9622
|
void (async () => {
|
|
9439
9623
|
const samples = [];
|
|
9440
9624
|
for (let i = 0; i < 2; i++) {
|
|
@@ -9448,16 +9632,44 @@ var Niadra = class _Niadra {
|
|
|
9448
9632
|
});
|
|
9449
9633
|
} catch (error) {
|
|
9450
9634
|
this.logger.debug(`round trip probe failed: ${describe(toNiadraError(error))}`);
|
|
9635
|
+
this.measuring = false;
|
|
9451
9636
|
return;
|
|
9452
9637
|
}
|
|
9453
9638
|
samples.push(Date.now() - started);
|
|
9454
9639
|
}
|
|
9455
9640
|
const rtt = Math.min(...samples);
|
|
9456
9641
|
this.voice.rtt = rtt;
|
|
9642
|
+
this.measuring = false;
|
|
9457
9643
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9458
|
-
|
|
9644
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9645
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9646
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9459
9647
|
})();
|
|
9460
9648
|
}
|
|
9649
|
+
/**
|
|
9650
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9651
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9652
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9653
|
+
*/
|
|
9654
|
+
readBudget(name) {
|
|
9655
|
+
if (!this.defaultReads.has(name)) return this.timeouts[name];
|
|
9656
|
+
const rtt = this.voice.rtt;
|
|
9657
|
+
if (rtt !== null) return this.timeouts[name] + rtt;
|
|
9658
|
+
const open = this.core?.transport.connectionOpen() ?? false;
|
|
9659
|
+
return this.measuring && open ? this.timeouts[name] + this.timeouts.connect : this.timeouts[name];
|
|
9660
|
+
}
|
|
9661
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9662
|
+
startVoice(core) {
|
|
9663
|
+
this.voiceStarted = true;
|
|
9664
|
+
this.probe(core);
|
|
9665
|
+
this.warnVoice();
|
|
9666
|
+
}
|
|
9667
|
+
warnVoice() {
|
|
9668
|
+
const rtt = this.voice.rtt;
|
|
9669
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9670
|
+
this.voiceWarned = true;
|
|
9671
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9672
|
+
}
|
|
9461
9673
|
/**
|
|
9462
9674
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9463
9675
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9467,7 +9679,7 @@ var Niadra = class _Niadra {
|
|
|
9467
9679
|
const key2 = cacheKey(request);
|
|
9468
9680
|
const deadline = Date.now() + timeout;
|
|
9469
9681
|
const line = this.voice.line(scope);
|
|
9470
|
-
this.
|
|
9682
|
+
this.startVoice(core);
|
|
9471
9683
|
line.request = request;
|
|
9472
9684
|
const words2 = wordsOf(query);
|
|
9473
9685
|
const background = this.timeouts.prefetch;
|
|
@@ -9653,8 +9865,39 @@ var Niadra = class _Niadra {
|
|
|
9653
9865
|
core.queue.flushInBackground(true);
|
|
9654
9866
|
});
|
|
9655
9867
|
}
|
|
9868
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9869
|
+
warm(scope, session) {
|
|
9870
|
+
const core = this.core;
|
|
9871
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9872
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9873
|
+
if (this.warmTimer !== void 0) return;
|
|
9874
|
+
const timer = setInterval(() => {
|
|
9875
|
+
this.warmTick(core);
|
|
9876
|
+
}, EVERY_MS);
|
|
9877
|
+
timer.unref?.();
|
|
9878
|
+
this.warmTimer = timer;
|
|
9879
|
+
}
|
|
9880
|
+
warmTick(core) {
|
|
9881
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9882
|
+
if (step === "stop") {
|
|
9883
|
+
clearInterval(this.warmTimer);
|
|
9884
|
+
this.warmTimer = void 0;
|
|
9885
|
+
return;
|
|
9886
|
+
}
|
|
9887
|
+
if (step !== "ping") return;
|
|
9888
|
+
core.transport.request({
|
|
9889
|
+
method: "GET",
|
|
9890
|
+
path: "/healthz",
|
|
9891
|
+
timeoutMs: 2e3,
|
|
9892
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9893
|
+
activity: false
|
|
9894
|
+
}).catch((error) => {
|
|
9895
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9896
|
+
});
|
|
9897
|
+
}
|
|
9656
9898
|
async endScope(item, scope) {
|
|
9657
9899
|
this.forgetScope(scope);
|
|
9900
|
+
this.keepWarm.end(scope);
|
|
9658
9901
|
return this.sendNow(() => item);
|
|
9659
9902
|
}
|
|
9660
9903
|
async sendBatch(transport, items2, maxAttempts, backoff) {
|
|
@@ -11509,7 +11752,7 @@ var Replayer = class {
|
|
|
11509
11752
|
try {
|
|
11510
11753
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
11511
11754
|
} catch (error) {
|
|
11512
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
11755
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
11513
11756
|
throw error;
|
|
11514
11757
|
}
|
|
11515
11758
|
let run = runOf(answer);
|
|
@@ -11612,7 +11855,7 @@ var Replayer = class {
|
|
|
11612
11855
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
11613
11856
|
}
|
|
11614
11857
|
};
|
|
11615
|
-
function
|
|
11858
|
+
function refused2(scenarioIds, results) {
|
|
11616
11859
|
const scenarios = scenarioIds.map((id) => {
|
|
11617
11860
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
11618
11861
|
return {
|