@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/cli.js
CHANGED
|
@@ -5,6 +5,46 @@ import { resolve as resolvePath } from "path";
|
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
6
|
import { parseArgs as parseArgs4 } from "util";
|
|
7
7
|
|
|
8
|
+
// src/ids.ts
|
|
9
|
+
var lastMs = -1;
|
|
10
|
+
var counter = 0;
|
|
11
|
+
function randomBytes(length) {
|
|
12
|
+
const bytes = new Uint8Array(length);
|
|
13
|
+
const webCrypto = globalThis.crypto;
|
|
14
|
+
if (webCrypto?.getRandomValues) {
|
|
15
|
+
webCrypto.getRandomValues(bytes);
|
|
16
|
+
} else {
|
|
17
|
+
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
18
|
+
}
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
function uuidv7(now = Date.now()) {
|
|
22
|
+
let ms = now;
|
|
23
|
+
if (ms <= lastMs) {
|
|
24
|
+
ms = lastMs;
|
|
25
|
+
counter = counter + 1 & 4095;
|
|
26
|
+
if (counter === 0) ms += 1;
|
|
27
|
+
} else {
|
|
28
|
+
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
29
|
+
}
|
|
30
|
+
lastMs = ms;
|
|
31
|
+
const bytes = randomBytes(16);
|
|
32
|
+
const high = Math.floor(ms / 2 ** 16);
|
|
33
|
+
const low = ms % 2 ** 16;
|
|
34
|
+
bytes[0] = high >>> 24 & 255;
|
|
35
|
+
bytes[1] = high >>> 16 & 255;
|
|
36
|
+
bytes[2] = high >>> 8 & 255;
|
|
37
|
+
bytes[3] = high & 255;
|
|
38
|
+
bytes[4] = low >>> 8 & 255;
|
|
39
|
+
bytes[5] = low & 255;
|
|
40
|
+
bytes[6] = 112 | counter >>> 8 & 15;
|
|
41
|
+
bytes[7] = counter & 255;
|
|
42
|
+
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
43
|
+
let hex2 = "";
|
|
44
|
+
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
45
|
+
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
8
48
|
// src/errors.ts
|
|
9
49
|
var NiadraError = class extends Error {
|
|
10
50
|
name = "NiadraError";
|
|
@@ -40,11 +80,12 @@ var NiadraAPIError = class extends NiadraError {
|
|
|
40
80
|
constructor(status, problem, requestId, retryAfterMs = null) {
|
|
41
81
|
const code = problem?.code ?? `http_${status}`;
|
|
42
82
|
const detail = problem?.detail ? `: ${problem.detail}` : "";
|
|
43
|
-
|
|
83
|
+
const id = requestId ?? problem?.request_id ?? null;
|
|
84
|
+
super(`${status} ${code}${detail}${id ? ` (request ${id})` : ""}`);
|
|
44
85
|
this.status = status;
|
|
45
86
|
this.code = code;
|
|
46
87
|
this.problem = problem;
|
|
47
|
-
this.requestId =
|
|
88
|
+
this.requestId = id;
|
|
48
89
|
this.retryAfterMs = retryAfterMs;
|
|
49
90
|
}
|
|
50
91
|
};
|
|
@@ -68,45 +109,11 @@ function toNiadraError(error) {
|
|
|
68
109
|
const message = error instanceof Error ? error.message : String(error);
|
|
69
110
|
return new NiadraError(message, { cause: error });
|
|
70
111
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const bytes = new Uint8Array(length);
|
|
77
|
-
const webCrypto = globalThis.crypto;
|
|
78
|
-
if (webCrypto?.getRandomValues) {
|
|
79
|
-
webCrypto.getRandomValues(bytes);
|
|
80
|
-
} else {
|
|
81
|
-
for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
82
|
-
}
|
|
83
|
-
return bytes;
|
|
84
|
-
}
|
|
85
|
-
function uuidv7(now = Date.now()) {
|
|
86
|
-
let ms = now;
|
|
87
|
-
if (ms <= lastMs) {
|
|
88
|
-
ms = lastMs;
|
|
89
|
-
counter = counter + 1 & 4095;
|
|
90
|
-
if (counter === 0) ms += 1;
|
|
91
|
-
} else {
|
|
92
|
-
counter = (randomBytes(2)[0] ?? 0) & 127;
|
|
93
|
-
}
|
|
94
|
-
lastMs = ms;
|
|
95
|
-
const bytes = randomBytes(16);
|
|
96
|
-
const high = Math.floor(ms / 2 ** 16);
|
|
97
|
-
const low = ms % 2 ** 16;
|
|
98
|
-
bytes[0] = high >>> 24 & 255;
|
|
99
|
-
bytes[1] = high >>> 16 & 255;
|
|
100
|
-
bytes[2] = high >>> 8 & 255;
|
|
101
|
-
bytes[3] = high & 255;
|
|
102
|
-
bytes[4] = low >>> 8 & 255;
|
|
103
|
-
bytes[5] = low & 255;
|
|
104
|
-
bytes[6] = 112 | counter >>> 8 & 15;
|
|
105
|
-
bytes[7] = counter & 255;
|
|
106
|
-
bytes[8] = 128 | (bytes[8] ?? 0) & 63;
|
|
107
|
-
let hex2 = "";
|
|
108
|
-
for (const byte of bytes) hex2 += byte.toString(16).padStart(2, "0");
|
|
109
|
-
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
|
|
112
|
+
function explain(error) {
|
|
113
|
+
if (!(error instanceof NiadraAPIError)) return error instanceof Error ? error.name : String(error);
|
|
114
|
+
const detail = error.problem?.detail?.trim();
|
|
115
|
+
const said = `${error.status} ${error.code}${detail ? `: ${detail.slice(0, 300)}` : ""}`;
|
|
116
|
+
return error.requestId ? `${said} (request ${error.requestId})` : said;
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
// src/admin.ts
|
|
@@ -503,7 +510,7 @@ function internalRecord(span, ref, act) {
|
|
|
503
510
|
}
|
|
504
511
|
|
|
505
512
|
// src/version.ts
|
|
506
|
-
var VERSION = "0.
|
|
513
|
+
var VERSION = "0.10.4";
|
|
507
514
|
|
|
508
515
|
// src/transport.ts
|
|
509
516
|
var RETRYABLE_WRITE_STATUS = /* @__PURE__ */ new Set([408, 421, 429, 500, 502, 503, 504]);
|
|
@@ -516,8 +523,36 @@ var Transport = class {
|
|
|
516
523
|
}
|
|
517
524
|
config;
|
|
518
525
|
baseURL;
|
|
526
|
+
answeredAt;
|
|
527
|
+
/** When the allowance was first given since the last answer; it holds for the calls of that moment only. */
|
|
528
|
+
grantedAt;
|
|
529
|
+
/** When the client last sent a request of its own (`RequestSpec.activity`), in epoch milliseconds. */
|
|
530
|
+
lastActivityAt;
|
|
531
|
+
/** Likely: an answer came within `keepAliveMs`. When not, the next budgeted call gets `coldAllowanceMs`. */
|
|
532
|
+
connectionOpen() {
|
|
533
|
+
return this.answeredAt !== void 0 && Date.now() - this.answeredAt <= (this.config.keepAliveMs ?? 0);
|
|
534
|
+
}
|
|
519
535
|
async request(spec) {
|
|
520
|
-
|
|
536
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
537
|
+
const budgeted = this.withAllowance(spec);
|
|
538
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
542
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
543
|
+
* outage costs the allowance once, not on every turn.
|
|
544
|
+
*/
|
|
545
|
+
withAllowance(spec) {
|
|
546
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
547
|
+
const { retry } = spec;
|
|
548
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
549
|
+
const now = Date.now();
|
|
550
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
551
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
552
|
+
this.grantedAt ??= now;
|
|
553
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
554
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
555
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
521
556
|
}
|
|
522
557
|
async read(spec, policy) {
|
|
523
558
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -588,7 +623,18 @@ var Transport = class {
|
|
|
588
623
|
headers["content-type"] = "application/json";
|
|
589
624
|
init.body = JSON.stringify(spec.body);
|
|
590
625
|
}
|
|
591
|
-
|
|
626
|
+
try {
|
|
627
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
628
|
+
this.answered();
|
|
629
|
+
return answer;
|
|
630
|
+
} catch (error) {
|
|
631
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
632
|
+
throw error;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
answered() {
|
|
636
|
+
this.answeredAt = Date.now();
|
|
637
|
+
this.grantedAt = void 0;
|
|
592
638
|
}
|
|
593
639
|
async exchange(url, init, deadline) {
|
|
594
640
|
let response;
|
|
@@ -4025,8 +4071,7 @@ var TurnSender = class {
|
|
|
4025
4071
|
return true;
|
|
4026
4072
|
}
|
|
4027
4073
|
}
|
|
4028
|
-
|
|
4029
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4074
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4030
4075
|
return true;
|
|
4031
4076
|
}
|
|
4032
4077
|
accepted(batch, answer) {
|
|
@@ -4034,8 +4079,8 @@ var TurnSender = class {
|
|
|
4034
4079
|
this.resumeAt = 0;
|
|
4035
4080
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4036
4081
|
const again = [];
|
|
4037
|
-
const
|
|
4038
|
-
let
|
|
4082
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4083
|
+
let refused3 = 0;
|
|
4039
4084
|
for (const error of answer.errors ?? []) {
|
|
4040
4085
|
const frame = batch.frames[error.index];
|
|
4041
4086
|
if (frame === void 0) continue;
|
|
@@ -4044,12 +4089,12 @@ var TurnSender = class {
|
|
|
4044
4089
|
this.recorder.modeRefused();
|
|
4045
4090
|
again.push(frame);
|
|
4046
4091
|
} else {
|
|
4047
|
-
|
|
4048
|
-
|
|
4092
|
+
refused3++;
|
|
4093
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4049
4094
|
}
|
|
4050
4095
|
}
|
|
4051
4096
|
if (again.length > 0) this.queue.requeue(again);
|
|
4052
|
-
if (
|
|
4097
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4053
4098
|
}
|
|
4054
4099
|
};
|
|
4055
4100
|
|
|
@@ -4171,9 +4216,12 @@ var TurnRecorder = class {
|
|
|
4171
4216
|
this.accepted += accepted;
|
|
4172
4217
|
this.duplicates += duplicates;
|
|
4173
4218
|
}
|
|
4174
|
-
|
|
4219
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4220
|
+
rejected(count2, reasons) {
|
|
4175
4221
|
this.rejectedTurns += count2;
|
|
4176
|
-
|
|
4222
|
+
const shown = [...new Set(reasons)].sort();
|
|
4223
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4224
|
+
this.logger.warn(`${count2} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4177
4225
|
}
|
|
4178
4226
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4179
4227
|
notRecorded(count2, off) {
|
|
@@ -4243,6 +4291,10 @@ async function hex(text2) {
|
|
|
4243
4291
|
}
|
|
4244
4292
|
|
|
4245
4293
|
// src/coordination/client.ts
|
|
4294
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4295
|
+
function refused(error) {
|
|
4296
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4297
|
+
}
|
|
4246
4298
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4247
4299
|
var CHECK_BUDGET_MS = 200;
|
|
4248
4300
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4264,15 +4316,22 @@ function claimed(data, error) {
|
|
|
4264
4316
|
return { held: false, claim: null, error: code };
|
|
4265
4317
|
}
|
|
4266
4318
|
var Coordinator = class {
|
|
4267
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4319
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4268
4320
|
this.outbox = outbox;
|
|
4269
4321
|
this.suppressions = suppressions;
|
|
4270
4322
|
this.declareNow = declareNow;
|
|
4323
|
+
this.logger = logger;
|
|
4271
4324
|
}
|
|
4272
4325
|
outbox;
|
|
4273
4326
|
suppressions;
|
|
4274
4327
|
declareNow;
|
|
4328
|
+
logger;
|
|
4275
4329
|
async failed(request, failOpen, error) {
|
|
4330
|
+
if (refused(error)) {
|
|
4331
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4332
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4333
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4334
|
+
}
|
|
4276
4335
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4277
4336
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4278
4337
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4293,7 +4352,7 @@ var Coordinator = class {
|
|
|
4293
4352
|
if (who.subject) body.subject = who.subject;
|
|
4294
4353
|
if (who.object) body.object = who.object;
|
|
4295
4354
|
const key2 = uuidv7();
|
|
4296
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4355
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4297
4356
|
return key2;
|
|
4298
4357
|
}
|
|
4299
4358
|
};
|
|
@@ -4812,7 +4871,7 @@ var Outbox = class {
|
|
|
4812
4871
|
return false;
|
|
4813
4872
|
}
|
|
4814
4873
|
this.writes.shift();
|
|
4815
|
-
this.logger.warn(
|
|
4874
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4816
4875
|
this.settle(write, void 0, error);
|
|
4817
4876
|
return true;
|
|
4818
4877
|
}
|
|
@@ -6349,6 +6408,7 @@ var AgentSession = class {
|
|
|
6349
6408
|
try {
|
|
6350
6409
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6351
6410
|
} catch (error) {
|
|
6411
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6352
6412
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6353
6413
|
}
|
|
6354
6414
|
}
|
|
@@ -6960,6 +7020,7 @@ var SessionState = class {
|
|
|
6960
7020
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
6961
7021
|
guards = /* @__PURE__ */ new Map();
|
|
6962
7022
|
lastReport = null;
|
|
7023
|
+
unlinkedSaid = false;
|
|
6963
7024
|
/** After the first pack, every read also asks what changed since. */
|
|
6964
7025
|
get wantsDelta() {
|
|
6965
7026
|
return this.etag !== null;
|
|
@@ -6988,6 +7049,19 @@ var SessionState = class {
|
|
|
6988
7049
|
this.last = absorbed;
|
|
6989
7050
|
return absorbed;
|
|
6990
7051
|
}
|
|
7052
|
+
/**
|
|
7053
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7054
|
+
* on with the subject's own memory, never the organization's.
|
|
7055
|
+
*/
|
|
7056
|
+
sayUnlinked(result2, logger) {
|
|
7057
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7058
|
+
this.unlinkedSaid = true;
|
|
7059
|
+
logger.warn(
|
|
7060
|
+
"`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."
|
|
7061
|
+
);
|
|
7062
|
+
}
|
|
7063
|
+
return result2;
|
|
7064
|
+
}
|
|
6991
7065
|
/** The last backing check of an agent's answer. */
|
|
6992
7066
|
get lastBacking() {
|
|
6993
7067
|
return this.lastReport;
|
|
@@ -7235,7 +7309,7 @@ var Conversation = class {
|
|
|
7235
7309
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7236
7310
|
*/
|
|
7237
7311
|
async context(options = {}) {
|
|
7238
|
-
const { query, turn: turn2, format, explain, include, ...requestOptions } = options;
|
|
7312
|
+
const { query, turn: turn2, format, explain: explain2, include, ...requestOptions } = options;
|
|
7239
7313
|
const params = {
|
|
7240
7314
|
subject: this.subject,
|
|
7241
7315
|
view: this.view,
|
|
@@ -7244,7 +7318,7 @@ var Conversation = class {
|
|
|
7244
7318
|
...this.params.about ? { about: this.params.about } : {},
|
|
7245
7319
|
...this.params.target ? { target: this.params.target } : {},
|
|
7246
7320
|
...format === "json" ? { format } : {},
|
|
7247
|
-
...
|
|
7321
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7248
7322
|
};
|
|
7249
7323
|
if (query) params.query = query;
|
|
7250
7324
|
if (include?.length) params.include = include;
|
|
@@ -7252,7 +7326,7 @@ var Conversation = class {
|
|
|
7252
7326
|
params.turn = turn2 === void 0 ? this.turnText : turn2;
|
|
7253
7327
|
const result2 = await this.client.context(params, requestOptions);
|
|
7254
7328
|
this.features.observe(result2);
|
|
7255
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7329
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7256
7330
|
}
|
|
7257
7331
|
/**
|
|
7258
7332
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7432,6 +7506,35 @@ function withHandles(base, extra) {
|
|
|
7432
7506
|
return all;
|
|
7433
7507
|
}
|
|
7434
7508
|
|
|
7509
|
+
// src/warm.ts
|
|
7510
|
+
var EVERY_MS = 1e5;
|
|
7511
|
+
var IDLE_MS = 9e4;
|
|
7512
|
+
var WARM_FOR_MS = 6e5;
|
|
7513
|
+
var KeepWarm = class {
|
|
7514
|
+
constructor(enabled) {
|
|
7515
|
+
this.enabled = enabled;
|
|
7516
|
+
}
|
|
7517
|
+
enabled;
|
|
7518
|
+
open = /* @__PURE__ */ new Map();
|
|
7519
|
+
openedAt = 0;
|
|
7520
|
+
/** Notes an open conversation or task. */
|
|
7521
|
+
add(scope, session, now) {
|
|
7522
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7523
|
+
this.open.set(scope, new WeakRef(session));
|
|
7524
|
+
this.openedAt = now;
|
|
7525
|
+
}
|
|
7526
|
+
/** The conversation or task ended. */
|
|
7527
|
+
end(scope) {
|
|
7528
|
+
this.open.delete(scope);
|
|
7529
|
+
}
|
|
7530
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7531
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7532
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7533
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7534
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7535
|
+
}
|
|
7536
|
+
};
|
|
7537
|
+
|
|
7435
7538
|
// src/exit.ts
|
|
7436
7539
|
var registered = /* @__PURE__ */ new Set();
|
|
7437
7540
|
var installedOn = null;
|
|
@@ -7561,8 +7664,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7561
7664
|
write: 5e3,
|
|
7562
7665
|
token: 2e3,
|
|
7563
7666
|
upload: 6e4,
|
|
7564
|
-
prefetch: 1e3
|
|
7667
|
+
prefetch: 1e3,
|
|
7668
|
+
connect: 1e3
|
|
7565
7669
|
};
|
|
7670
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7566
7671
|
var DEFAULT_CACHE = {
|
|
7567
7672
|
ttlMs: 1e4,
|
|
7568
7673
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7669,7 +7774,7 @@ var Task = class {
|
|
|
7669
7774
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7670
7775
|
*/
|
|
7671
7776
|
async context(options = {}) {
|
|
7672
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7777
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7673
7778
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7674
7779
|
const params = {
|
|
7675
7780
|
...target,
|
|
@@ -7679,18 +7784,18 @@ var Task = class {
|
|
|
7679
7784
|
...this.level ? { verification: this.level } : {},
|
|
7680
7785
|
...this.params.target ? { target: this.params.target } : {},
|
|
7681
7786
|
...format === "json" ? { format } : {},
|
|
7682
|
-
...
|
|
7787
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7683
7788
|
...include?.length ? { include } : {}
|
|
7684
7789
|
};
|
|
7685
7790
|
if (query) {
|
|
7686
7791
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7687
7792
|
this.features.observe(answered);
|
|
7688
|
-
return this.state.observe(answered);
|
|
7793
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7689
7794
|
}
|
|
7690
7795
|
if (this.state.wantsDelta) params.delta = true;
|
|
7691
7796
|
const result2 = await this.client.context(params, requestOptions);
|
|
7692
7797
|
this.features.observe(result2);
|
|
7693
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7798
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7694
7799
|
}
|
|
7695
7800
|
/**
|
|
7696
7801
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7820,14 +7925,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7820
7925
|
search: "search_agent_memory",
|
|
7821
7926
|
remember: "remember"
|
|
7822
7927
|
};
|
|
7823
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7928
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7824
7929
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7825
7930
|
var TOOL_DEFINITIONS = [
|
|
7826
7931
|
{
|
|
7827
7932
|
"type": "function",
|
|
7828
7933
|
"function": {
|
|
7829
7934
|
"name": "search_customer_history",
|
|
7830
|
-
"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 '
|
|
7935
|
+
"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.",
|
|
7831
7936
|
"parameters": {
|
|
7832
7937
|
"type": "object",
|
|
7833
7938
|
"properties": {
|
|
@@ -7871,7 +7976,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7871
7976
|
"open_item",
|
|
7872
7977
|
"action",
|
|
7873
7978
|
"object",
|
|
7874
|
-
"trait"
|
|
7979
|
+
"trait",
|
|
7980
|
+
"system_event"
|
|
7875
7981
|
]
|
|
7876
7982
|
}
|
|
7877
7983
|
},
|
|
@@ -7898,7 +8004,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7898
8004
|
"type": "function",
|
|
7899
8005
|
"function": {
|
|
7900
8006
|
"name": "get_customer_timeline",
|
|
7901
|
-
"description": "List this customer's conversations
|
|
8007
|
+
"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.",
|
|
7902
8008
|
"parameters": {
|
|
7903
8009
|
"type": "object",
|
|
7904
8010
|
"properties": {
|
|
@@ -7946,7 +8052,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7946
8052
|
"open_item",
|
|
7947
8053
|
"action",
|
|
7948
8054
|
"object",
|
|
7949
|
-
"trait"
|
|
8055
|
+
"trait",
|
|
8056
|
+
"system_event"
|
|
7950
8057
|
]
|
|
7951
8058
|
}
|
|
7952
8059
|
},
|
|
@@ -7965,7 +8072,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7965
8072
|
"type": "function",
|
|
7966
8073
|
"function": {
|
|
7967
8074
|
"name": "open_history_item",
|
|
7968
|
-
"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.",
|
|
8075
|
+
"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.",
|
|
7969
8076
|
"parameters": {
|
|
7970
8077
|
"type": "object",
|
|
7971
8078
|
"properties": {
|
|
@@ -8336,6 +8443,13 @@ function compose(body, read2) {
|
|
|
8336
8443
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8337
8444
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8338
8445
|
}
|
|
8446
|
+
var RTT_MARGIN_MS = 50;
|
|
8447
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8448
|
+
const ms = Math.round(rttMs);
|
|
8449
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8450
|
+
(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`
|
|
8451
|
+
);
|
|
8452
|
+
}
|
|
8339
8453
|
function rttWarnings(rttMs, timeouts) {
|
|
8340
8454
|
const ms = Math.round(rttMs);
|
|
8341
8455
|
const found2 = [];
|
|
@@ -8373,6 +8487,14 @@ var Niadra = class _Niadra {
|
|
|
8373
8487
|
enabled;
|
|
8374
8488
|
core;
|
|
8375
8489
|
timeouts;
|
|
8490
|
+
keepWarm;
|
|
8491
|
+
warmTimer;
|
|
8492
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8493
|
+
defaultReads;
|
|
8494
|
+
voiceStarted = false;
|
|
8495
|
+
/** A probe is on its way: until it ends, default read budgets get `timeouts.connect` on top. */
|
|
8496
|
+
measuring = false;
|
|
8497
|
+
voiceWarned = false;
|
|
8376
8498
|
strict;
|
|
8377
8499
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8378
8500
|
logger;
|
|
@@ -8417,6 +8539,8 @@ var Niadra = class _Niadra {
|
|
|
8417
8539
|
this.strict = options.strict ?? false;
|
|
8418
8540
|
this.logger = options.logger ?? consoleLogger;
|
|
8419
8541
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8542
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8543
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8420
8544
|
this.voice = new VoiceLines(
|
|
8421
8545
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8422
8546
|
);
|
|
@@ -8442,13 +8566,14 @@ var Niadra = class _Niadra {
|
|
|
8442
8566
|
this.coordinator = new Coordinator(
|
|
8443
8567
|
this.outbox,
|
|
8444
8568
|
this.suppressions,
|
|
8445
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8569
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8570
|
+
this.logger
|
|
8446
8571
|
);
|
|
8447
8572
|
this.states = new AgentStates(
|
|
8448
8573
|
this.outbox,
|
|
8449
8574
|
{
|
|
8450
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8451
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8575
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8576
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8452
8577
|
},
|
|
8453
8578
|
this.logger
|
|
8454
8579
|
);
|
|
@@ -8475,6 +8600,7 @@ var Niadra = class _Niadra {
|
|
|
8475
8600
|
this.core = setup;
|
|
8476
8601
|
this.enabled = true;
|
|
8477
8602
|
this.disabledReason = null;
|
|
8603
|
+
this.probe(setup);
|
|
8478
8604
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8479
8605
|
}
|
|
8480
8606
|
setup(options) {
|
|
@@ -8497,7 +8623,9 @@ var Niadra = class _Niadra {
|
|
|
8497
8623
|
apiKey,
|
|
8498
8624
|
fetch: fetchImpl,
|
|
8499
8625
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
8500
|
-
logger: this.logger
|
|
8626
|
+
logger: this.logger,
|
|
8627
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8628
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8501
8629
|
});
|
|
8502
8630
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8503
8631
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8553,7 +8681,7 @@ var Niadra = class _Niadra {
|
|
|
8553
8681
|
return result2;
|
|
8554
8682
|
}
|
|
8555
8683
|
readContext(core, params, request, options) {
|
|
8556
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8684
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8557
8685
|
const { query: own2, ...pinned2 } = request;
|
|
8558
8686
|
const query = turnText(own2 ?? params.turn);
|
|
8559
8687
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8569,7 +8697,7 @@ var Niadra = class _Niadra {
|
|
|
8569
8697
|
*/
|
|
8570
8698
|
async profile() {
|
|
8571
8699
|
if (!this.core) return null;
|
|
8572
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8700
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8573
8701
|
}
|
|
8574
8702
|
/**
|
|
8575
8703
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8594,7 +8722,7 @@ var Niadra = class _Niadra {
|
|
|
8594
8722
|
*/
|
|
8595
8723
|
async mayContact(handle, purpose, options = {}) {
|
|
8596
8724
|
if (this.core && this.suppressions.due()) {
|
|
8597
|
-
const read2 = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8725
|
+
const read2 = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8598
8726
|
if (!this.suppressions.held) await read2;
|
|
8599
8727
|
}
|
|
8600
8728
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8604,8 +8732,8 @@ var Niadra = class _Niadra {
|
|
|
8604
8732
|
readSuppressions(budgetMs) {
|
|
8605
8733
|
return this.suppressions.read(
|
|
8606
8734
|
{
|
|
8607
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8608
|
-
page: (cursor, limit2) => this.api.suppressions({ cursor, limit: limit2 }, { timeout: this.
|
|
8735
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8736
|
+
page: (cursor, limit2) => this.api.suppressions({ cursor, limit: limit2 }, { timeout: this.readBudget("navigation") })
|
|
8609
8737
|
},
|
|
8610
8738
|
budgetMs
|
|
8611
8739
|
);
|
|
@@ -8684,6 +8812,7 @@ var Niadra = class _Niadra {
|
|
|
8684
8812
|
}
|
|
8685
8813
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8686
8814
|
get agentHost() {
|
|
8815
|
+
const client = this;
|
|
8687
8816
|
return {
|
|
8688
8817
|
recorder: this.turns,
|
|
8689
8818
|
coordinator: this.coordinator,
|
|
@@ -8697,7 +8826,10 @@ var Niadra = class _Niadra {
|
|
|
8697
8826
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8698
8827
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8699
8828
|
enabled: this.enabled,
|
|
8700
|
-
|
|
8829
|
+
strict: this.strict,
|
|
8830
|
+
get navigationMs() {
|
|
8831
|
+
return client.readBudget("navigation");
|
|
8832
|
+
}
|
|
8701
8833
|
};
|
|
8702
8834
|
}
|
|
8703
8835
|
/**
|
|
@@ -8725,7 +8857,7 @@ var Niadra = class _Niadra {
|
|
|
8725
8857
|
if (!cache) return false;
|
|
8726
8858
|
const key2 = cacheKey(request);
|
|
8727
8859
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8728
|
-
this.
|
|
8860
|
+
this.startVoice(core);
|
|
8729
8861
|
line.request = request;
|
|
8730
8862
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8731
8863
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8862,13 +8994,13 @@ var Niadra = class _Niadra {
|
|
|
8862
8994
|
if (!params.query || params.query.length > 2e3) {
|
|
8863
8995
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8864
8996
|
}
|
|
8865
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
8997
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8866
8998
|
});
|
|
8867
8999
|
}
|
|
8868
9000
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8869
9001
|
async timeline(params, options = {}) {
|
|
8870
9002
|
return this.navigate(
|
|
8871
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
9003
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8872
9004
|
);
|
|
8873
9005
|
}
|
|
8874
9006
|
/**
|
|
@@ -8881,9 +9013,10 @@ var Niadra = class _Niadra {
|
|
|
8881
9013
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8882
9014
|
const body = { item_id: id };
|
|
8883
9015
|
if (params.subject) body.subject = params.subject;
|
|
9016
|
+
if (params.about) body.about = params.about;
|
|
8884
9017
|
if (params.verification) body.verification = params.verification;
|
|
8885
9018
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8886
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9019
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8887
9020
|
});
|
|
8888
9021
|
}
|
|
8889
9022
|
/**
|
|
@@ -8895,7 +9028,7 @@ var Niadra = class _Niadra {
|
|
|
8895
9028
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8896
9029
|
*/
|
|
8897
9030
|
async objectState(object, options = {}) {
|
|
8898
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9031
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8899
9032
|
}
|
|
8900
9033
|
/**
|
|
8901
9034
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8908,7 +9041,7 @@ var Niadra = class _Niadra {
|
|
|
8908
9041
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8909
9042
|
}
|
|
8910
9043
|
const path = `${objectPath(object)}/timeline`;
|
|
8911
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9044
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8912
9045
|
spec.query = { cursor: params.cursor, limit: String(limit2) };
|
|
8913
9046
|
return spec;
|
|
8914
9047
|
});
|
|
@@ -8927,6 +9060,7 @@ var Niadra = class _Niadra {
|
|
|
8927
9060
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8928
9061
|
open: (id, customer, bound, voice) => {
|
|
8929
9062
|
const scope = { subject: customer };
|
|
9063
|
+
if (bound.about) scope.about = bound.about;
|
|
8930
9064
|
if (bound.verification) scope.verification = bound.verification;
|
|
8931
9065
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8932
9066
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -8961,7 +9095,7 @@ var Niadra = class _Niadra {
|
|
|
8961
9095
|
const key2 = AgentMemoryCache.key(params);
|
|
8962
9096
|
const fresh = cache?.fresh(key2);
|
|
8963
9097
|
if (fresh) return blockResult(fresh, "cache");
|
|
8964
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9098
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8965
9099
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
8966
9100
|
spec.query = {
|
|
8967
9101
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -8998,7 +9132,7 @@ var Niadra = class _Niadra {
|
|
|
8998
9132
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
8999
9133
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9000
9134
|
if (params.task_id) body.task_id = params.task_id;
|
|
9001
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9135
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9002
9136
|
});
|
|
9003
9137
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9004
9138
|
}
|
|
@@ -9092,6 +9226,41 @@ var Niadra = class _Niadra {
|
|
|
9092
9226
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9093
9227
|
}
|
|
9094
9228
|
}
|
|
9229
|
+
/**
|
|
9230
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9231
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9232
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9233
|
+
*/
|
|
9234
|
+
contextUse(params = {}, options = {}) {
|
|
9235
|
+
return this.navigate(() => {
|
|
9236
|
+
const { group_by: groups, ...filters } = params;
|
|
9237
|
+
const query = { ...filters };
|
|
9238
|
+
if (groups?.length) query.group_by = groups;
|
|
9239
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9240
|
+
});
|
|
9241
|
+
}
|
|
9242
|
+
/**
|
|
9243
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9244
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9245
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9246
|
+
*/
|
|
9247
|
+
link(params, options = {}) {
|
|
9248
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9249
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9250
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9251
|
+
}
|
|
9252
|
+
/**
|
|
9253
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9254
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9255
|
+
*/
|
|
9256
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9257
|
+
return this.navigate(() => {
|
|
9258
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9259
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9260
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9261
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9262
|
+
});
|
|
9263
|
+
}
|
|
9095
9264
|
/**
|
|
9096
9265
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9097
9266
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9184,16 +9353,20 @@ var Niadra = class _Niadra {
|
|
|
9184
9353
|
* emits `conversation.ended` when you call `end()`.
|
|
9185
9354
|
*/
|
|
9186
9355
|
conversation(params) {
|
|
9187
|
-
|
|
9356
|
+
const conversation = new Conversation(this, params, {
|
|
9188
9357
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9189
9358
|
});
|
|
9359
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9360
|
+
return conversation;
|
|
9190
9361
|
}
|
|
9191
9362
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9192
9363
|
task(params) {
|
|
9193
|
-
|
|
9364
|
+
const task = new Task(this, params, {
|
|
9194
9365
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9195
9366
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9196
9367
|
});
|
|
9368
|
+
this.warm(`task:${task.id}`, task);
|
|
9369
|
+
return task;
|
|
9197
9370
|
}
|
|
9198
9371
|
/**
|
|
9199
9372
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9213,6 +9386,8 @@ var Niadra = class _Niadra {
|
|
|
9213
9386
|
*/
|
|
9214
9387
|
async shutdown() {
|
|
9215
9388
|
this.unregisterExit();
|
|
9389
|
+
clearInterval(this.warmTimer);
|
|
9390
|
+
this.warmTimer = void 0;
|
|
9216
9391
|
if (!this.core) return;
|
|
9217
9392
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9218
9393
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9316,9 +9491,9 @@ var Niadra = class _Niadra {
|
|
|
9316
9491
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9317
9492
|
return normalizeContext(response.data);
|
|
9318
9493
|
} catch (error) {
|
|
9319
|
-
const
|
|
9494
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9320
9495
|
const left = timeout - (Date.now() - started);
|
|
9321
|
-
if (!request.include?.length || !
|
|
9496
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9322
9497
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9323
9498
|
const { include: _dropped, ...plain2 } = request;
|
|
9324
9499
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9386,6 +9561,7 @@ var Niadra = class _Niadra {
|
|
|
9386
9561
|
/** Measures the round trip to the region once per client, in the background. */
|
|
9387
9562
|
probe(core) {
|
|
9388
9563
|
if (!this.voice.claimProbe()) return;
|
|
9564
|
+
this.measuring = true;
|
|
9389
9565
|
void (async () => {
|
|
9390
9566
|
const samples = [];
|
|
9391
9567
|
for (let i = 0; i < 2; i++) {
|
|
@@ -9399,16 +9575,44 @@ var Niadra = class _Niadra {
|
|
|
9399
9575
|
});
|
|
9400
9576
|
} catch (error) {
|
|
9401
9577
|
this.logger.debug(`round trip probe failed: ${describe(toNiadraError(error))}`);
|
|
9578
|
+
this.measuring = false;
|
|
9402
9579
|
return;
|
|
9403
9580
|
}
|
|
9404
9581
|
samples.push(Date.now() - started);
|
|
9405
9582
|
}
|
|
9406
9583
|
const rtt = Math.min(...samples);
|
|
9407
9584
|
this.voice.rtt = rtt;
|
|
9585
|
+
this.measuring = false;
|
|
9408
9586
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9409
|
-
|
|
9587
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9588
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9589
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9410
9590
|
})();
|
|
9411
9591
|
}
|
|
9592
|
+
/**
|
|
9593
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9594
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9595
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9596
|
+
*/
|
|
9597
|
+
readBudget(name) {
|
|
9598
|
+
if (!this.defaultReads.has(name)) return this.timeouts[name];
|
|
9599
|
+
const rtt = this.voice.rtt;
|
|
9600
|
+
if (rtt !== null) return this.timeouts[name] + rtt;
|
|
9601
|
+
const open = this.core?.transport.connectionOpen() ?? false;
|
|
9602
|
+
return this.measuring && open ? this.timeouts[name] + this.timeouts.connect : this.timeouts[name];
|
|
9603
|
+
}
|
|
9604
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9605
|
+
startVoice(core) {
|
|
9606
|
+
this.voiceStarted = true;
|
|
9607
|
+
this.probe(core);
|
|
9608
|
+
this.warnVoice();
|
|
9609
|
+
}
|
|
9610
|
+
warnVoice() {
|
|
9611
|
+
const rtt = this.voice.rtt;
|
|
9612
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9613
|
+
this.voiceWarned = true;
|
|
9614
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9615
|
+
}
|
|
9412
9616
|
/**
|
|
9413
9617
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9414
9618
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9418,7 +9622,7 @@ var Niadra = class _Niadra {
|
|
|
9418
9622
|
const key2 = cacheKey(request);
|
|
9419
9623
|
const deadline = Date.now() + timeout;
|
|
9420
9624
|
const line = this.voice.line(scope);
|
|
9421
|
-
this.
|
|
9625
|
+
this.startVoice(core);
|
|
9422
9626
|
line.request = request;
|
|
9423
9627
|
const words2 = wordsOf(query);
|
|
9424
9628
|
const background = this.timeouts.prefetch;
|
|
@@ -9604,8 +9808,39 @@ var Niadra = class _Niadra {
|
|
|
9604
9808
|
core.queue.flushInBackground(true);
|
|
9605
9809
|
});
|
|
9606
9810
|
}
|
|
9811
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9812
|
+
warm(scope, session) {
|
|
9813
|
+
const core = this.core;
|
|
9814
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9815
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9816
|
+
if (this.warmTimer !== void 0) return;
|
|
9817
|
+
const timer = setInterval(() => {
|
|
9818
|
+
this.warmTick(core);
|
|
9819
|
+
}, EVERY_MS);
|
|
9820
|
+
timer.unref?.();
|
|
9821
|
+
this.warmTimer = timer;
|
|
9822
|
+
}
|
|
9823
|
+
warmTick(core) {
|
|
9824
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9825
|
+
if (step === "stop") {
|
|
9826
|
+
clearInterval(this.warmTimer);
|
|
9827
|
+
this.warmTimer = void 0;
|
|
9828
|
+
return;
|
|
9829
|
+
}
|
|
9830
|
+
if (step !== "ping") return;
|
|
9831
|
+
core.transport.request({
|
|
9832
|
+
method: "GET",
|
|
9833
|
+
path: "/healthz",
|
|
9834
|
+
timeoutMs: 2e3,
|
|
9835
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9836
|
+
activity: false
|
|
9837
|
+
}).catch((error) => {
|
|
9838
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9839
|
+
});
|
|
9840
|
+
}
|
|
9607
9841
|
async endScope(item, scope) {
|
|
9608
9842
|
this.forgetScope(scope);
|
|
9843
|
+
this.keepWarm.end(scope);
|
|
9609
9844
|
return this.sendNow(() => item);
|
|
9610
9845
|
}
|
|
9611
9846
|
async sendBatch(transport, items, maxAttempts, backoff) {
|
|
@@ -9910,7 +10145,7 @@ var Replayer = class {
|
|
|
9910
10145
|
try {
|
|
9911
10146
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
9912
10147
|
} catch (error) {
|
|
9913
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
10148
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
9914
10149
|
throw error;
|
|
9915
10150
|
}
|
|
9916
10151
|
let run = runOf(answer);
|
|
@@ -10013,7 +10248,7 @@ var Replayer = class {
|
|
|
10013
10248
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
10014
10249
|
}
|
|
10015
10250
|
};
|
|
10016
|
-
function
|
|
10251
|
+
function refused2(scenarioIds, results) {
|
|
10017
10252
|
const scenarios = scenarioIds.map((id) => {
|
|
10018
10253
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
10019
10254
|
return {
|