@niadra/sdk 0.9.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -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 +319 -96
- package/dist/cli.js.map +1 -1
- package/dist/{client-CWMxmJHs.d.cts → client-rmWhZJOx.d.cts} +190 -7
- package/dist/{client-CWMxmJHs.d.ts → client-rmWhZJOx.d.ts} +190 -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 +329 -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 +329 -98
- package/dist/index.js.map +1 -1
- package/dist/{intercept-CGngQI8B.d.cts → intercept-C0XODsy9.d.cts} +1 -1
- package/dist/{intercept-Bv0nLpSw.d.ts → intercept-veMDudKV.d.ts} +1 -1
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.d.cts +3 -3
- package/dist/langchain.d.ts +3 -3
- package/dist/langchain.js.map +1 -1
- package/dist/livekit.cjs +10 -2
- package/dist/livekit.cjs.map +1 -1
- package/dist/livekit.d.cts +3 -3
- package/dist/livekit.d.ts +3 -3
- package/dist/livekit.js +10 -2
- package/dist/livekit.js.map +1 -1
- package/dist/llamaindex.d.cts +3 -3
- package/dist/llamaindex.d.ts +3 -3
- package/dist/mastra.d.cts +3 -3
- package/dist/mastra.d.ts +3 -3
- package/dist/openai-agents.d.cts +3 -3
- package/dist/openai-agents.d.ts +3 -3
- package/dist/retell.cjs +7 -5
- package/dist/retell.cjs.map +1 -1
- package/dist/retell.d.cts +5 -5
- package/dist/retell.d.ts +5 -5
- package/dist/retell.js +7 -5
- package/dist/retell.js.map +1 -1
- package/dist/{shared-B4Ef2Wpg.d.ts → shared-Cs7h5C4Y.d.ts} +1 -1
- package/dist/{shared-Cq8mz-7m.d.cts → shared-zaTBum17.d.cts} +1 -1
- package/dist/strands.d.cts +3 -3
- package/dist/strands.d.ts +3 -3
- package/dist/twilio.cjs +10 -2
- package/dist/twilio.cjs.map +1 -1
- package/dist/twilio.d.cts +4 -4
- package/dist/twilio.d.ts +4 -4
- package/dist/twilio.js +10 -2
- package/dist/twilio.js.map +1 -1
- package/dist/vapi.cjs +7 -5
- package/dist/vapi.cjs.map +1 -1
- package/dist/vapi.d.cts +5 -5
- package/dist/vapi.d.ts +5 -5
- package/dist/vapi.js +7 -5
- package/dist/vapi.js.map +1 -1
- package/dist/voltagent.d.cts +3 -3
- package/dist/voltagent.d.ts +3 -3
- package/dist/{webhook-z2TjUzjv.d.ts → webhook-2V5QgnKI.d.ts} +1 -1
- package/dist/{webhook-jTx_SegV.d.cts → webhook-CX7nvQig.d.cts} +1 -1
- package/dist/whatsapp.cjs +10 -2
- package/dist/whatsapp.cjs.map +1 -1
- package/dist/whatsapp.d.cts +2 -2
- package/dist/whatsapp.d.ts +2 -2
- package/dist/whatsapp.js +10 -2
- package/dist/whatsapp.js.map +1 -1
- package/package.json +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.3";
|
|
520
527
|
|
|
521
528
|
// src/transport.ts
|
|
522
529
|
var RETRYABLE_WRITE_STATUS = /* @__PURE__ */ new Set([408, 421, 429, 500, 502, 503, 504]);
|
|
@@ -529,8 +536,32 @@ var Transport = class {
|
|
|
529
536
|
}
|
|
530
537
|
config;
|
|
531
538
|
baseURL;
|
|
539
|
+
answeredAt;
|
|
540
|
+
/** When the allowance was first given since the last answer; it holds for the calls of that moment only. */
|
|
541
|
+
grantedAt;
|
|
542
|
+
/** When the client last sent a request of its own (`RequestSpec.activity`), in epoch milliseconds. */
|
|
543
|
+
lastActivityAt;
|
|
532
544
|
async request(spec) {
|
|
533
|
-
|
|
545
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
546
|
+
const budgeted = this.withAllowance(spec);
|
|
547
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
551
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
552
|
+
* outage costs the allowance once, not on every turn.
|
|
553
|
+
*/
|
|
554
|
+
withAllowance(spec) {
|
|
555
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
556
|
+
const { retry } = spec;
|
|
557
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
558
|
+
const now = Date.now();
|
|
559
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
560
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
561
|
+
this.grantedAt ??= now;
|
|
562
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
563
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
564
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
534
565
|
}
|
|
535
566
|
async read(spec, policy) {
|
|
536
567
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -601,7 +632,18 @@ var Transport = class {
|
|
|
601
632
|
headers["content-type"] = "application/json";
|
|
602
633
|
init.body = JSON.stringify(spec.body);
|
|
603
634
|
}
|
|
604
|
-
|
|
635
|
+
try {
|
|
636
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
637
|
+
this.answered();
|
|
638
|
+
return answer;
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
641
|
+
throw error;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
answered() {
|
|
645
|
+
this.answeredAt = Date.now();
|
|
646
|
+
this.grantedAt = void 0;
|
|
605
647
|
}
|
|
606
648
|
async exchange(url, init, deadline) {
|
|
607
649
|
let response;
|
|
@@ -4042,8 +4084,7 @@ var TurnSender = class {
|
|
|
4042
4084
|
return true;
|
|
4043
4085
|
}
|
|
4044
4086
|
}
|
|
4045
|
-
|
|
4046
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4087
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4047
4088
|
return true;
|
|
4048
4089
|
}
|
|
4049
4090
|
accepted(batch, answer) {
|
|
@@ -4051,8 +4092,8 @@ var TurnSender = class {
|
|
|
4051
4092
|
this.resumeAt = 0;
|
|
4052
4093
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4053
4094
|
const again = [];
|
|
4054
|
-
const
|
|
4055
|
-
let
|
|
4095
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4096
|
+
let refused3 = 0;
|
|
4056
4097
|
for (const error of answer.errors ?? []) {
|
|
4057
4098
|
const frame = batch.frames[error.index];
|
|
4058
4099
|
if (frame === void 0) continue;
|
|
@@ -4061,12 +4102,12 @@ var TurnSender = class {
|
|
|
4061
4102
|
this.recorder.modeRefused();
|
|
4062
4103
|
again.push(frame);
|
|
4063
4104
|
} else {
|
|
4064
|
-
|
|
4065
|
-
|
|
4105
|
+
refused3++;
|
|
4106
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4066
4107
|
}
|
|
4067
4108
|
}
|
|
4068
4109
|
if (again.length > 0) this.queue.requeue(again);
|
|
4069
|
-
if (
|
|
4110
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4070
4111
|
}
|
|
4071
4112
|
};
|
|
4072
4113
|
|
|
@@ -4188,9 +4229,12 @@ var TurnRecorder = class {
|
|
|
4188
4229
|
this.accepted += accepted;
|
|
4189
4230
|
this.duplicates += duplicates;
|
|
4190
4231
|
}
|
|
4191
|
-
|
|
4232
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4233
|
+
rejected(count3, reasons) {
|
|
4192
4234
|
this.rejectedTurns += count3;
|
|
4193
|
-
|
|
4235
|
+
const shown = [...new Set(reasons)].sort();
|
|
4236
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4237
|
+
this.logger.warn(`${count3} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4194
4238
|
}
|
|
4195
4239
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4196
4240
|
notRecorded(count3, off) {
|
|
@@ -4260,6 +4304,10 @@ async function hex(text2) {
|
|
|
4260
4304
|
}
|
|
4261
4305
|
|
|
4262
4306
|
// src/coordination/client.ts
|
|
4307
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4308
|
+
function refused(error) {
|
|
4309
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4310
|
+
}
|
|
4263
4311
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4264
4312
|
var CHECK_BUDGET_MS = 200;
|
|
4265
4313
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4281,15 +4329,22 @@ function claimed(data, error) {
|
|
|
4281
4329
|
return { held: false, claim: null, error: code };
|
|
4282
4330
|
}
|
|
4283
4331
|
var Coordinator = class {
|
|
4284
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4332
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4285
4333
|
this.outbox = outbox;
|
|
4286
4334
|
this.suppressions = suppressions;
|
|
4287
4335
|
this.declareNow = declareNow;
|
|
4336
|
+
this.logger = logger;
|
|
4288
4337
|
}
|
|
4289
4338
|
outbox;
|
|
4290
4339
|
suppressions;
|
|
4291
4340
|
declareNow;
|
|
4341
|
+
logger;
|
|
4292
4342
|
async failed(request, failOpen, error) {
|
|
4343
|
+
if (refused(error)) {
|
|
4344
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4345
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4346
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4347
|
+
}
|
|
4293
4348
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4294
4349
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4295
4350
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4310,7 +4365,7 @@ var Coordinator = class {
|
|
|
4310
4365
|
if (who.subject) body.subject = who.subject;
|
|
4311
4366
|
if (who.object) body.object = who.object;
|
|
4312
4367
|
const key2 = uuidv7();
|
|
4313
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4368
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4314
4369
|
return key2;
|
|
4315
4370
|
}
|
|
4316
4371
|
};
|
|
@@ -4829,7 +4884,7 @@ var Outbox = class {
|
|
|
4829
4884
|
return false;
|
|
4830
4885
|
}
|
|
4831
4886
|
this.writes.shift();
|
|
4832
|
-
this.logger.warn(
|
|
4887
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4833
4888
|
this.settle(write, void 0, error);
|
|
4834
4889
|
return true;
|
|
4835
4890
|
}
|
|
@@ -5719,9 +5774,17 @@ var handles = {
|
|
|
5719
5774
|
appUserId: (value, options) => build("app_user_id", value, void 0, options),
|
|
5720
5775
|
/** The id of a person or organization in a system of record; `system` names that system, such as `crm`. */
|
|
5721
5776
|
systemId: (value, system, options) => build("system_id", value, system, options),
|
|
5722
|
-
/**
|
|
5777
|
+
/**
|
|
5778
|
+
* A person's national document, such as a CPF: `govIdHmac("529.982.247-25", "BR")`. Send the number
|
|
5779
|
+
* itself, never a hash of it: the server checks its check digits, then keeps only a keyed hash with your
|
|
5780
|
+
* space's secret and an encrypted copy, and shows it masked. `country` is its ISO 3166-1 alpha-2 code.
|
|
5781
|
+
*/
|
|
5723
5782
|
govIdHmac: (value, country, options) => build("gov_id_hmac", value, country, options),
|
|
5724
|
-
/**
|
|
5783
|
+
/**
|
|
5784
|
+
* A company's registry number, such as a CNPJ: `orgRegistryHmac("11.222.333/0001-81", "BR")`. As with
|
|
5785
|
+
* `govIdHmac`, send the number itself; the server checks it and keeps only a keyed hash. Identifies an
|
|
5786
|
+
* organization.
|
|
5787
|
+
*/
|
|
5725
5788
|
orgRegistryHmac: (value, country) => build("org_registry_hmac", value, country, { subjectKind: "account" }),
|
|
5726
5789
|
/** An e-mail domain, such as `acme.com`. Identifies an organization. */
|
|
5727
5790
|
emailDomain: (domain) => build("email_domain", domain, void 0, { subjectKind: "account" }),
|
|
@@ -6395,6 +6458,7 @@ var AgentSession = class {
|
|
|
6395
6458
|
try {
|
|
6396
6459
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6397
6460
|
} catch (error) {
|
|
6461
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6398
6462
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6399
6463
|
}
|
|
6400
6464
|
}
|
|
@@ -7006,6 +7070,7 @@ var SessionState = class {
|
|
|
7006
7070
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
7007
7071
|
guards = /* @__PURE__ */ new Map();
|
|
7008
7072
|
lastReport = null;
|
|
7073
|
+
unlinkedSaid = false;
|
|
7009
7074
|
/** After the first pack, every read also asks what changed since. */
|
|
7010
7075
|
get wantsDelta() {
|
|
7011
7076
|
return this.etag !== null;
|
|
@@ -7034,6 +7099,19 @@ var SessionState = class {
|
|
|
7034
7099
|
this.last = absorbed;
|
|
7035
7100
|
return absorbed;
|
|
7036
7101
|
}
|
|
7102
|
+
/**
|
|
7103
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7104
|
+
* on with the subject's own memory, never the organization's.
|
|
7105
|
+
*/
|
|
7106
|
+
sayUnlinked(result2, logger) {
|
|
7107
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7108
|
+
this.unlinkedSaid = true;
|
|
7109
|
+
logger.warn(
|
|
7110
|
+
"`about` names an organization with no active link to the subject; the context is the subject's own, without that organization's memory. Create the link to read it."
|
|
7111
|
+
);
|
|
7112
|
+
}
|
|
7113
|
+
return result2;
|
|
7114
|
+
}
|
|
7037
7115
|
/** The last backing check of an agent's answer. */
|
|
7038
7116
|
get lastBacking() {
|
|
7039
7117
|
return this.lastReport;
|
|
@@ -7281,7 +7359,7 @@ var Conversation = class {
|
|
|
7281
7359
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7282
7360
|
*/
|
|
7283
7361
|
async context(options = {}) {
|
|
7284
|
-
const { query, turn, format, explain, include, ...requestOptions } = options;
|
|
7362
|
+
const { query, turn, format, explain: explain2, include, ...requestOptions } = options;
|
|
7285
7363
|
const params = {
|
|
7286
7364
|
subject: this.subject,
|
|
7287
7365
|
view: this.view,
|
|
@@ -7290,7 +7368,7 @@ var Conversation = class {
|
|
|
7290
7368
|
...this.params.about ? { about: this.params.about } : {},
|
|
7291
7369
|
...this.params.target ? { target: this.params.target } : {},
|
|
7292
7370
|
...format === "json" ? { format } : {},
|
|
7293
|
-
...
|
|
7371
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7294
7372
|
};
|
|
7295
7373
|
if (query) params.query = query;
|
|
7296
7374
|
if (include?.length) params.include = include;
|
|
@@ -7298,7 +7376,7 @@ var Conversation = class {
|
|
|
7298
7376
|
params.turn = turn === void 0 ? this.turnText : turn;
|
|
7299
7377
|
const result2 = await this.client.context(params, requestOptions);
|
|
7300
7378
|
this.features.observe(result2);
|
|
7301
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7379
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7302
7380
|
}
|
|
7303
7381
|
/**
|
|
7304
7382
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7478,6 +7556,35 @@ function withHandles(base, extra) {
|
|
|
7478
7556
|
return all;
|
|
7479
7557
|
}
|
|
7480
7558
|
|
|
7559
|
+
// src/warm.ts
|
|
7560
|
+
var EVERY_MS = 1e5;
|
|
7561
|
+
var IDLE_MS = 9e4;
|
|
7562
|
+
var WARM_FOR_MS = 6e5;
|
|
7563
|
+
var KeepWarm = class {
|
|
7564
|
+
constructor(enabled) {
|
|
7565
|
+
this.enabled = enabled;
|
|
7566
|
+
}
|
|
7567
|
+
enabled;
|
|
7568
|
+
open = /* @__PURE__ */ new Map();
|
|
7569
|
+
openedAt = 0;
|
|
7570
|
+
/** Notes an open conversation or task. */
|
|
7571
|
+
add(scope, session, now) {
|
|
7572
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7573
|
+
this.open.set(scope, new WeakRef(session));
|
|
7574
|
+
this.openedAt = now;
|
|
7575
|
+
}
|
|
7576
|
+
/** The conversation or task ended. */
|
|
7577
|
+
end(scope) {
|
|
7578
|
+
this.open.delete(scope);
|
|
7579
|
+
}
|
|
7580
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7581
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7582
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7583
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7584
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7585
|
+
}
|
|
7586
|
+
};
|
|
7587
|
+
|
|
7481
7588
|
// src/exit.ts
|
|
7482
7589
|
var registered = /* @__PURE__ */ new Set();
|
|
7483
7590
|
var installedOn = null;
|
|
@@ -7608,8 +7715,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7608
7715
|
write: 5e3,
|
|
7609
7716
|
token: 2e3,
|
|
7610
7717
|
upload: 6e4,
|
|
7611
|
-
prefetch: 1e3
|
|
7718
|
+
prefetch: 1e3,
|
|
7719
|
+
connect: 1e3
|
|
7612
7720
|
};
|
|
7721
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7613
7722
|
var DEFAULT_CACHE = {
|
|
7614
7723
|
ttlMs: 1e4,
|
|
7615
7724
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7716,7 +7825,7 @@ var Task = class {
|
|
|
7716
7825
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7717
7826
|
*/
|
|
7718
7827
|
async context(options = {}) {
|
|
7719
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7828
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7720
7829
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7721
7830
|
const params = {
|
|
7722
7831
|
...target,
|
|
@@ -7726,18 +7835,18 @@ var Task = class {
|
|
|
7726
7835
|
...this.level ? { verification: this.level } : {},
|
|
7727
7836
|
...this.params.target ? { target: this.params.target } : {},
|
|
7728
7837
|
...format === "json" ? { format } : {},
|
|
7729
|
-
...
|
|
7838
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7730
7839
|
...include?.length ? { include } : {}
|
|
7731
7840
|
};
|
|
7732
7841
|
if (query) {
|
|
7733
7842
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7734
7843
|
this.features.observe(answered);
|
|
7735
|
-
return this.state.observe(answered);
|
|
7844
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7736
7845
|
}
|
|
7737
7846
|
if (this.state.wantsDelta) params.delta = true;
|
|
7738
7847
|
const result2 = await this.client.context(params, requestOptions);
|
|
7739
7848
|
this.features.observe(result2);
|
|
7740
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7849
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7741
7850
|
}
|
|
7742
7851
|
/**
|
|
7743
7852
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7867,14 +7976,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7867
7976
|
search: "search_agent_memory",
|
|
7868
7977
|
remember: "remember"
|
|
7869
7978
|
};
|
|
7870
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7979
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7871
7980
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7872
7981
|
var TOOL_DEFINITIONS = [
|
|
7873
7982
|
{
|
|
7874
7983
|
"type": "function",
|
|
7875
7984
|
"function": {
|
|
7876
7985
|
"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 '
|
|
7986
|
+
"description": "Search everything that already happened with this customer: past conversations, promises, agent actions, orders and invoices. Use it when the customer refers to something earlier or asks whether a problem happened before. Do not call it when the answer is already in the context block, including its 'Hist\xF3rico' line, which already counts recurrences. Returns short items with date, channel and outcome, plus a recurrence count. Open a conversation (`episode:`) or an object (`object:`) with open_history_item; the other items are complete as listed.",
|
|
7878
7987
|
"parameters": {
|
|
7879
7988
|
"type": "object",
|
|
7880
7989
|
"properties": {
|
|
@@ -7918,7 +8027,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7918
8027
|
"open_item",
|
|
7919
8028
|
"action",
|
|
7920
8029
|
"object",
|
|
7921
|
-
"trait"
|
|
8030
|
+
"trait",
|
|
8031
|
+
"system_event"
|
|
7922
8032
|
]
|
|
7923
8033
|
}
|
|
7924
8034
|
},
|
|
@@ -7945,7 +8055,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7945
8055
|
"type": "function",
|
|
7946
8056
|
"function": {
|
|
7947
8057
|
"name": "get_customer_timeline",
|
|
7948
|
-
"description": "List this customer's conversations
|
|
8058
|
+
"description": "List this customer's conversations, agent actions and system events in order, newest first, one line each. Use it to leaf through the history when you do not know what to search for; filters.item_kinds also lists the open items, facts, patterns or objects it names. Pass next_cursor to continue. Prefer search_customer_history for a specific question.",
|
|
7949
8059
|
"parameters": {
|
|
7950
8060
|
"type": "object",
|
|
7951
8061
|
"properties": {
|
|
@@ -7993,7 +8103,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7993
8103
|
"open_item",
|
|
7994
8104
|
"action",
|
|
7995
8105
|
"object",
|
|
7996
|
-
"trait"
|
|
8106
|
+
"trait",
|
|
8107
|
+
"system_event"
|
|
7997
8108
|
]
|
|
7998
8109
|
}
|
|
7999
8110
|
},
|
|
@@ -8012,7 +8123,7 @@ var TOOL_DEFINITIONS = [
|
|
|
8012
8123
|
"type": "function",
|
|
8013
8124
|
"function": {
|
|
8014
8125
|
"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.",
|
|
8126
|
+
"description": "Open one conversation (`episode:`) or business object (`object:`) returned by search_customer_history or get_customer_timeline: what was asked, what was promised and by whom, the outcome and what memory came from it. Actions, events, facts and promises are complete as listed and do not open. Use it only after a search or timeline pointed to the item.",
|
|
8016
8127
|
"parameters": {
|
|
8017
8128
|
"type": "object",
|
|
8018
8129
|
"properties": {
|
|
@@ -8383,6 +8494,13 @@ function compose(body, read) {
|
|
|
8383
8494
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8384
8495
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8385
8496
|
}
|
|
8497
|
+
var RTT_MARGIN_MS = 50;
|
|
8498
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8499
|
+
const ms = Math.round(rttMs);
|
|
8500
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8501
|
+
(name) => `timeouts.${name} (${timeouts[name]} ms) is shorter than the round trip to the region (${ms} ms) plus ${RTT_MARGIN_MS} ms for the API: its reads will run out of time. Leave it at its default, which adds the measured round trip, or raise it`
|
|
8502
|
+
);
|
|
8503
|
+
}
|
|
8386
8504
|
function rttWarnings(rttMs, timeouts) {
|
|
8387
8505
|
const ms = Math.round(rttMs);
|
|
8388
8506
|
const found2 = [];
|
|
@@ -8420,6 +8538,12 @@ var Niadra = class _Niadra {
|
|
|
8420
8538
|
enabled;
|
|
8421
8539
|
core;
|
|
8422
8540
|
timeouts;
|
|
8541
|
+
keepWarm;
|
|
8542
|
+
warmTimer;
|
|
8543
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8544
|
+
defaultReads;
|
|
8545
|
+
voiceStarted = false;
|
|
8546
|
+
voiceWarned = false;
|
|
8423
8547
|
strict;
|
|
8424
8548
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8425
8549
|
logger;
|
|
@@ -8464,6 +8588,8 @@ var Niadra = class _Niadra {
|
|
|
8464
8588
|
this.strict = options.strict ?? false;
|
|
8465
8589
|
this.logger = options.logger ?? consoleLogger;
|
|
8466
8590
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8591
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8592
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8467
8593
|
this.voice = new VoiceLines(
|
|
8468
8594
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8469
8595
|
);
|
|
@@ -8489,13 +8615,14 @@ var Niadra = class _Niadra {
|
|
|
8489
8615
|
this.coordinator = new Coordinator(
|
|
8490
8616
|
this.outbox,
|
|
8491
8617
|
this.suppressions,
|
|
8492
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8618
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8619
|
+
this.logger
|
|
8493
8620
|
);
|
|
8494
8621
|
this.states = new AgentStates(
|
|
8495
8622
|
this.outbox,
|
|
8496
8623
|
{
|
|
8497
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8498
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8624
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8625
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8499
8626
|
},
|
|
8500
8627
|
this.logger
|
|
8501
8628
|
);
|
|
@@ -8522,6 +8649,7 @@ var Niadra = class _Niadra {
|
|
|
8522
8649
|
this.core = setup;
|
|
8523
8650
|
this.enabled = true;
|
|
8524
8651
|
this.disabledReason = null;
|
|
8652
|
+
this.probe(setup);
|
|
8525
8653
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8526
8654
|
}
|
|
8527
8655
|
setup(options) {
|
|
@@ -8544,7 +8672,9 @@ var Niadra = class _Niadra {
|
|
|
8544
8672
|
apiKey,
|
|
8545
8673
|
fetch: fetchImpl,
|
|
8546
8674
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
8547
|
-
logger: this.logger
|
|
8675
|
+
logger: this.logger,
|
|
8676
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8677
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8548
8678
|
});
|
|
8549
8679
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8550
8680
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8600,7 +8730,7 @@ var Niadra = class _Niadra {
|
|
|
8600
8730
|
return result2;
|
|
8601
8731
|
}
|
|
8602
8732
|
readContext(core, params, request, options) {
|
|
8603
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8733
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8604
8734
|
const { query: own3, ...pinned2 } = request;
|
|
8605
8735
|
const query = turnText(own3 ?? params.turn);
|
|
8606
8736
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8616,7 +8746,7 @@ var Niadra = class _Niadra {
|
|
|
8616
8746
|
*/
|
|
8617
8747
|
async profile() {
|
|
8618
8748
|
if (!this.core) return null;
|
|
8619
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8749
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8620
8750
|
}
|
|
8621
8751
|
/**
|
|
8622
8752
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8641,7 +8771,7 @@ var Niadra = class _Niadra {
|
|
|
8641
8771
|
*/
|
|
8642
8772
|
async mayContact(handle, purpose, options = {}) {
|
|
8643
8773
|
if (this.core && this.suppressions.due()) {
|
|
8644
|
-
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8774
|
+
const read = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8645
8775
|
if (!this.suppressions.held) await read;
|
|
8646
8776
|
}
|
|
8647
8777
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8651,8 +8781,8 @@ var Niadra = class _Niadra {
|
|
|
8651
8781
|
readSuppressions(budgetMs) {
|
|
8652
8782
|
return this.suppressions.read(
|
|
8653
8783
|
{
|
|
8654
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8655
|
-
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.
|
|
8784
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8785
|
+
page: (cursor, limit3) => this.api.suppressions({ cursor, limit: limit3 }, { timeout: this.readBudget("navigation") })
|
|
8656
8786
|
},
|
|
8657
8787
|
budgetMs
|
|
8658
8788
|
);
|
|
@@ -8731,6 +8861,7 @@ var Niadra = class _Niadra {
|
|
|
8731
8861
|
}
|
|
8732
8862
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8733
8863
|
get agentHost() {
|
|
8864
|
+
const client = this;
|
|
8734
8865
|
return {
|
|
8735
8866
|
recorder: this.turns,
|
|
8736
8867
|
coordinator: this.coordinator,
|
|
@@ -8744,7 +8875,10 @@ var Niadra = class _Niadra {
|
|
|
8744
8875
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8745
8876
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8746
8877
|
enabled: this.enabled,
|
|
8747
|
-
|
|
8878
|
+
strict: this.strict,
|
|
8879
|
+
get navigationMs() {
|
|
8880
|
+
return client.readBudget("navigation");
|
|
8881
|
+
}
|
|
8748
8882
|
};
|
|
8749
8883
|
}
|
|
8750
8884
|
/**
|
|
@@ -8772,7 +8906,7 @@ var Niadra = class _Niadra {
|
|
|
8772
8906
|
if (!cache) return false;
|
|
8773
8907
|
const key2 = cacheKey(request);
|
|
8774
8908
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8775
|
-
this.
|
|
8909
|
+
this.startVoice(core);
|
|
8776
8910
|
line.request = request;
|
|
8777
8911
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8778
8912
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8909,13 +9043,13 @@ var Niadra = class _Niadra {
|
|
|
8909
9043
|
if (!params.query || params.query.length > 2e3) {
|
|
8910
9044
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8911
9045
|
}
|
|
8912
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
9046
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8913
9047
|
});
|
|
8914
9048
|
}
|
|
8915
9049
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8916
9050
|
async timeline(params, options = {}) {
|
|
8917
9051
|
return this.navigate(
|
|
8918
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
9052
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8919
9053
|
);
|
|
8920
9054
|
}
|
|
8921
9055
|
/**
|
|
@@ -8928,9 +9062,10 @@ var Niadra = class _Niadra {
|
|
|
8928
9062
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8929
9063
|
const body = { item_id: id };
|
|
8930
9064
|
if (params.subject) body.subject = params.subject;
|
|
9065
|
+
if (params.about) body.about = params.about;
|
|
8931
9066
|
if (params.verification) body.verification = params.verification;
|
|
8932
9067
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8933
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9068
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8934
9069
|
});
|
|
8935
9070
|
}
|
|
8936
9071
|
/**
|
|
@@ -8942,7 +9077,7 @@ var Niadra = class _Niadra {
|
|
|
8942
9077
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8943
9078
|
*/
|
|
8944
9079
|
async objectState(object, options = {}) {
|
|
8945
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9080
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8946
9081
|
}
|
|
8947
9082
|
/**
|
|
8948
9083
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8955,7 +9090,7 @@ var Niadra = class _Niadra {
|
|
|
8955
9090
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8956
9091
|
}
|
|
8957
9092
|
const path = `${objectPath(object)}/timeline`;
|
|
8958
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9093
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8959
9094
|
spec.query = { cursor: params.cursor, limit: String(limit3) };
|
|
8960
9095
|
return spec;
|
|
8961
9096
|
});
|
|
@@ -8974,6 +9109,7 @@ var Niadra = class _Niadra {
|
|
|
8974
9109
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8975
9110
|
open: (id, customer, bound, voice) => {
|
|
8976
9111
|
const scope = { subject: customer };
|
|
9112
|
+
if (bound.about) scope.about = bound.about;
|
|
8977
9113
|
if (bound.verification) scope.verification = bound.verification;
|
|
8978
9114
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8979
9115
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -9008,7 +9144,7 @@ var Niadra = class _Niadra {
|
|
|
9008
9144
|
const key2 = AgentMemoryCache.key(params);
|
|
9009
9145
|
const fresh = cache?.fresh(key2);
|
|
9010
9146
|
if (fresh) return blockResult(fresh, "cache");
|
|
9011
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9147
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
9012
9148
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
9013
9149
|
spec.query = {
|
|
9014
9150
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -9045,7 +9181,7 @@ var Niadra = class _Niadra {
|
|
|
9045
9181
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
9046
9182
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9047
9183
|
if (params.task_id) body.task_id = params.task_id;
|
|
9048
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9184
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9049
9185
|
});
|
|
9050
9186
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9051
9187
|
}
|
|
@@ -9139,6 +9275,41 @@ var Niadra = class _Niadra {
|
|
|
9139
9275
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9140
9276
|
}
|
|
9141
9277
|
}
|
|
9278
|
+
/**
|
|
9279
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9280
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9281
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9282
|
+
*/
|
|
9283
|
+
contextUse(params = {}, options = {}) {
|
|
9284
|
+
return this.navigate(() => {
|
|
9285
|
+
const { group_by: groups, ...filters } = params;
|
|
9286
|
+
const query = { ...filters };
|
|
9287
|
+
if (groups?.length) query.group_by = groups;
|
|
9288
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9289
|
+
});
|
|
9290
|
+
}
|
|
9291
|
+
/**
|
|
9292
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9293
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9294
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9295
|
+
*/
|
|
9296
|
+
link(params, options = {}) {
|
|
9297
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9298
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9299
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9300
|
+
}
|
|
9301
|
+
/**
|
|
9302
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9303
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9304
|
+
*/
|
|
9305
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9306
|
+
return this.navigate(() => {
|
|
9307
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9308
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9309
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9310
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9311
|
+
});
|
|
9312
|
+
}
|
|
9142
9313
|
/**
|
|
9143
9314
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9144
9315
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9231,16 +9402,20 @@ var Niadra = class _Niadra {
|
|
|
9231
9402
|
* emits `conversation.ended` when you call `end()`.
|
|
9232
9403
|
*/
|
|
9233
9404
|
conversation(params) {
|
|
9234
|
-
|
|
9405
|
+
const conversation = new Conversation(this, params, {
|
|
9235
9406
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9236
9407
|
});
|
|
9408
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9409
|
+
return conversation;
|
|
9237
9410
|
}
|
|
9238
9411
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9239
9412
|
task(params) {
|
|
9240
|
-
|
|
9413
|
+
const task = new Task(this, params, {
|
|
9241
9414
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9242
9415
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9243
9416
|
});
|
|
9417
|
+
this.warm(`task:${task.id}`, task);
|
|
9418
|
+
return task;
|
|
9244
9419
|
}
|
|
9245
9420
|
/**
|
|
9246
9421
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9260,6 +9435,8 @@ var Niadra = class _Niadra {
|
|
|
9260
9435
|
*/
|
|
9261
9436
|
async shutdown() {
|
|
9262
9437
|
this.unregisterExit();
|
|
9438
|
+
clearInterval(this.warmTimer);
|
|
9439
|
+
this.warmTimer = void 0;
|
|
9263
9440
|
if (!this.core) return;
|
|
9264
9441
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9265
9442
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9363,9 +9540,9 @@ var Niadra = class _Niadra {
|
|
|
9363
9540
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9364
9541
|
return normalizeContext(response.data);
|
|
9365
9542
|
} catch (error) {
|
|
9366
|
-
const
|
|
9543
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9367
9544
|
const left = timeout - (Date.now() - started);
|
|
9368
|
-
if (!request.include?.length || !
|
|
9545
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9369
9546
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9370
9547
|
const { include: _dropped, ...plain2 } = request;
|
|
9371
9548
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9453,9 +9630,32 @@ var Niadra = class _Niadra {
|
|
|
9453
9630
|
const rtt = Math.min(...samples);
|
|
9454
9631
|
this.voice.rtt = rtt;
|
|
9455
9632
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9456
|
-
|
|
9633
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9634
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9635
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9457
9636
|
})();
|
|
9458
9637
|
}
|
|
9638
|
+
/**
|
|
9639
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9640
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9641
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9642
|
+
*/
|
|
9643
|
+
readBudget(name) {
|
|
9644
|
+
const rtt = this.voice.rtt;
|
|
9645
|
+
return rtt !== null && this.defaultReads.has(name) ? this.timeouts[name] + rtt : this.timeouts[name];
|
|
9646
|
+
}
|
|
9647
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9648
|
+
startVoice(core) {
|
|
9649
|
+
this.voiceStarted = true;
|
|
9650
|
+
this.probe(core);
|
|
9651
|
+
this.warnVoice();
|
|
9652
|
+
}
|
|
9653
|
+
warnVoice() {
|
|
9654
|
+
const rtt = this.voice.rtt;
|
|
9655
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9656
|
+
this.voiceWarned = true;
|
|
9657
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9658
|
+
}
|
|
9459
9659
|
/**
|
|
9460
9660
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9461
9661
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9465,7 +9665,7 @@ var Niadra = class _Niadra {
|
|
|
9465
9665
|
const key2 = cacheKey(request);
|
|
9466
9666
|
const deadline = Date.now() + timeout;
|
|
9467
9667
|
const line = this.voice.line(scope);
|
|
9468
|
-
this.
|
|
9668
|
+
this.startVoice(core);
|
|
9469
9669
|
line.request = request;
|
|
9470
9670
|
const words2 = wordsOf(query);
|
|
9471
9671
|
const background = this.timeouts.prefetch;
|
|
@@ -9651,8 +9851,39 @@ var Niadra = class _Niadra {
|
|
|
9651
9851
|
core.queue.flushInBackground(true);
|
|
9652
9852
|
});
|
|
9653
9853
|
}
|
|
9854
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9855
|
+
warm(scope, session) {
|
|
9856
|
+
const core = this.core;
|
|
9857
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9858
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9859
|
+
if (this.warmTimer !== void 0) return;
|
|
9860
|
+
const timer = setInterval(() => {
|
|
9861
|
+
this.warmTick(core);
|
|
9862
|
+
}, EVERY_MS);
|
|
9863
|
+
timer.unref?.();
|
|
9864
|
+
this.warmTimer = timer;
|
|
9865
|
+
}
|
|
9866
|
+
warmTick(core) {
|
|
9867
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9868
|
+
if (step === "stop") {
|
|
9869
|
+
clearInterval(this.warmTimer);
|
|
9870
|
+
this.warmTimer = void 0;
|
|
9871
|
+
return;
|
|
9872
|
+
}
|
|
9873
|
+
if (step !== "ping") return;
|
|
9874
|
+
core.transport.request({
|
|
9875
|
+
method: "GET",
|
|
9876
|
+
path: "/healthz",
|
|
9877
|
+
timeoutMs: 2e3,
|
|
9878
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9879
|
+
activity: false
|
|
9880
|
+
}).catch((error) => {
|
|
9881
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9882
|
+
});
|
|
9883
|
+
}
|
|
9654
9884
|
async endScope(item, scope) {
|
|
9655
9885
|
this.forgetScope(scope);
|
|
9886
|
+
this.keepWarm.end(scope);
|
|
9656
9887
|
return this.sendNow(() => item);
|
|
9657
9888
|
}
|
|
9658
9889
|
async sendBatch(transport, items2, maxAttempts, backoff) {
|
|
@@ -11507,7 +11738,7 @@ var Replayer = class {
|
|
|
11507
11738
|
try {
|
|
11508
11739
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
11509
11740
|
} catch (error) {
|
|
11510
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
11741
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
11511
11742
|
throw error;
|
|
11512
11743
|
}
|
|
11513
11744
|
let run = runOf(answer);
|
|
@@ -11610,7 +11841,7 @@ var Replayer = class {
|
|
|
11610
11841
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
11611
11842
|
}
|
|
11612
11843
|
};
|
|
11613
|
-
function
|
|
11844
|
+
function refused2(scenarioIds, results) {
|
|
11614
11845
|
const scenarios = scenarioIds.map((id) => {
|
|
11615
11846
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
11616
11847
|
return {
|