@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/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.3";
|
|
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,32 @@ 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;
|
|
519
531
|
async request(spec) {
|
|
520
|
-
|
|
532
|
+
if (spec.activity !== false) this.lastActivityAt = Date.now();
|
|
533
|
+
const budgeted = this.withAllowance(spec);
|
|
534
|
+
return budgeted.retry.kind === "read" ? this.read(budgeted, budgeted.retry) : this.write(budgeted, budgeted.retry);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* `spec` with the allowance for opening a connection, when it has a budget and none is likely open. The calls
|
|
538
|
+
* that start while the first one opens it get it too; after that, none does until an answer comes, so an
|
|
539
|
+
* outage costs the allowance once, not on every turn.
|
|
540
|
+
*/
|
|
541
|
+
withAllowance(spec) {
|
|
542
|
+
const allowance = this.config.coldAllowanceMs ?? 0;
|
|
543
|
+
const { retry } = spec;
|
|
544
|
+
if (allowance <= 0 || retry.kind === "write" && retry.totalMs === void 0) return spec;
|
|
545
|
+
const now = Date.now();
|
|
546
|
+
const keep = this.config.keepAliveMs ?? 0;
|
|
547
|
+
if (this.answeredAt !== void 0 && now - this.answeredAt <= keep) return spec;
|
|
548
|
+
this.grantedAt ??= now;
|
|
549
|
+
if (now - this.grantedAt > allowance) return spec;
|
|
550
|
+
const total = retry.kind === "write" && retry.totalMs !== void 0 ? { ...retry, totalMs: retry.totalMs + allowance } : retry;
|
|
551
|
+
return { ...spec, timeoutMs: spec.timeoutMs + allowance, retry: total };
|
|
521
552
|
}
|
|
522
553
|
async read(spec, policy) {
|
|
523
554
|
const deadline = new Deadline(spec.timeoutMs, spec.signal);
|
|
@@ -588,7 +619,18 @@ var Transport = class {
|
|
|
588
619
|
headers["content-type"] = "application/json";
|
|
589
620
|
init.body = JSON.stringify(spec.body);
|
|
590
621
|
}
|
|
591
|
-
|
|
622
|
+
try {
|
|
623
|
+
const answer = await this.exchange(this.url(spec.path, spec.query), init, deadline);
|
|
624
|
+
this.answered();
|
|
625
|
+
return answer;
|
|
626
|
+
} catch (error) {
|
|
627
|
+
if (error instanceof NiadraAPIError) this.answered();
|
|
628
|
+
throw error;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
answered() {
|
|
632
|
+
this.answeredAt = Date.now();
|
|
633
|
+
this.grantedAt = void 0;
|
|
592
634
|
}
|
|
593
635
|
async exchange(url, init, deadline) {
|
|
594
636
|
let response;
|
|
@@ -4025,8 +4067,7 @@ var TurnSender = class {
|
|
|
4025
4067
|
return true;
|
|
4026
4068
|
}
|
|
4027
4069
|
}
|
|
4028
|
-
|
|
4029
|
-
this.recorder.rejected(batch.frames.length, [code]);
|
|
4070
|
+
this.recorder.rejected(batch.frames.length, [explain(error)]);
|
|
4030
4071
|
return true;
|
|
4031
4072
|
}
|
|
4032
4073
|
accepted(batch, answer) {
|
|
@@ -4034,8 +4075,8 @@ var TurnSender = class {
|
|
|
4034
4075
|
this.resumeAt = 0;
|
|
4035
4076
|
this.recorder.sent(answer.accepted, answer.duplicates);
|
|
4036
4077
|
const again = [];
|
|
4037
|
-
const
|
|
4038
|
-
let
|
|
4078
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
4079
|
+
let refused3 = 0;
|
|
4039
4080
|
for (const error of answer.errors ?? []) {
|
|
4040
4081
|
const frame = batch.frames[error.index];
|
|
4041
4082
|
if (frame === void 0) continue;
|
|
@@ -4044,12 +4085,12 @@ var TurnSender = class {
|
|
|
4044
4085
|
this.recorder.modeRefused();
|
|
4045
4086
|
again.push(frame);
|
|
4046
4087
|
} else {
|
|
4047
|
-
|
|
4048
|
-
|
|
4088
|
+
refused3++;
|
|
4089
|
+
reasons.add(error.detail ? `${error.code}: ${error.detail.slice(0, 300)}` : error.code);
|
|
4049
4090
|
}
|
|
4050
4091
|
}
|
|
4051
4092
|
if (again.length > 0) this.queue.requeue(again);
|
|
4052
|
-
if (
|
|
4093
|
+
if (refused3 > 0) this.recorder.rejected(refused3, [...reasons]);
|
|
4053
4094
|
}
|
|
4054
4095
|
};
|
|
4055
4096
|
|
|
@@ -4171,9 +4212,12 @@ var TurnRecorder = class {
|
|
|
4171
4212
|
this.accepted += accepted;
|
|
4172
4213
|
this.duplicates += duplicates;
|
|
4173
4214
|
}
|
|
4174
|
-
|
|
4215
|
+
/** `reasons`: the API's code and detail of each refusal (field paths and rules, never a value). */
|
|
4216
|
+
rejected(count2, reasons) {
|
|
4175
4217
|
this.rejectedTurns += count2;
|
|
4176
|
-
|
|
4218
|
+
const shown = [...new Set(reasons)].sort();
|
|
4219
|
+
const more = shown.length > 3 ? ` (and ${shown.length - 3} more)` : "";
|
|
4220
|
+
this.logger.warn(`${count2} turn records were refused: ${shown.slice(0, 3).join("; ")}${more}`);
|
|
4177
4221
|
}
|
|
4178
4222
|
/** The space does not record turns: these are dropped, and with `off` (a 404) recording stops a while. */
|
|
4179
4223
|
notRecorded(count2, off) {
|
|
@@ -4243,6 +4287,10 @@ async function hex(text2) {
|
|
|
4243
4287
|
}
|
|
4244
4288
|
|
|
4245
4289
|
// src/coordination/client.ts
|
|
4290
|
+
var REFUSED = /* @__PURE__ */ new Set([400, 401, 403, 422]);
|
|
4291
|
+
function refused(error) {
|
|
4292
|
+
return error instanceof NiadraAPIError && REFUSED.has(error.status);
|
|
4293
|
+
}
|
|
4246
4294
|
var FAIL_CLOSED = /* @__PURE__ */ new Set(["marketing", "retention", "collection"]);
|
|
4247
4295
|
var CHECK_BUDGET_MS = 200;
|
|
4248
4296
|
function fallback(request, suppressed, failOpen) {
|
|
@@ -4264,15 +4312,22 @@ function claimed(data, error) {
|
|
|
4264
4312
|
return { held: false, claim: null, error: code };
|
|
4265
4313
|
}
|
|
4266
4314
|
var Coordinator = class {
|
|
4267
|
-
constructor(outbox, suppressions, declareNow) {
|
|
4315
|
+
constructor(outbox, suppressions, declareNow, logger) {
|
|
4268
4316
|
this.outbox = outbox;
|
|
4269
4317
|
this.suppressions = suppressions;
|
|
4270
4318
|
this.declareNow = declareNow;
|
|
4319
|
+
this.logger = logger;
|
|
4271
4320
|
}
|
|
4272
4321
|
outbox;
|
|
4273
4322
|
suppressions;
|
|
4274
4323
|
declareNow;
|
|
4324
|
+
logger;
|
|
4275
4325
|
async failed(request, failOpen, error) {
|
|
4326
|
+
if (refused(error)) {
|
|
4327
|
+
this.logger?.warn(`the coordination check was refused: ${explain(error)}`);
|
|
4328
|
+
const decision = request.direction === "inbound" ? "allow" : "defer";
|
|
4329
|
+
return recorded({ decision, decision_id: uuidv7(), reasons: ["invalid_request"], valid_for_s: 0 });
|
|
4330
|
+
}
|
|
4276
4331
|
const off = error instanceof NiadraAPIError && error.status === 404;
|
|
4277
4332
|
const suppressed = request.subject != null && request.direction === "outbound" ? !await this.suppressions.mayContact(request.subject, request.purpose, { channel: request.channel ?? null, failOpen: true }) : false;
|
|
4278
4333
|
const plain2 = off ? { ...request, effect_key: null } : request;
|
|
@@ -4293,7 +4348,7 @@ var Coordinator = class {
|
|
|
4293
4348
|
if (who.subject) body.subject = who.subject;
|
|
4294
4349
|
if (who.object) body.object = who.object;
|
|
4295
4350
|
const key2 = uuidv7();
|
|
4296
|
-
this.outbox.put({ send: () => this.declareNow(body, key2) });
|
|
4351
|
+
this.outbox.put({ send: () => this.declareNow(body, key2), route: "POST /v1/coordination/declare" });
|
|
4297
4352
|
return key2;
|
|
4298
4353
|
}
|
|
4299
4354
|
};
|
|
@@ -4812,7 +4867,7 @@ var Outbox = class {
|
|
|
4812
4867
|
return false;
|
|
4813
4868
|
}
|
|
4814
4869
|
this.writes.shift();
|
|
4815
|
-
this.logger.warn(
|
|
4870
|
+
this.logger.warn(`${write.route ?? "a write"} was refused: ${explain(error)}`);
|
|
4816
4871
|
this.settle(write, void 0, error);
|
|
4817
4872
|
return true;
|
|
4818
4873
|
}
|
|
@@ -6349,6 +6404,7 @@ var AgentSession = class {
|
|
|
6349
6404
|
try {
|
|
6350
6405
|
return this.host.coordinator.decided(await this.host.check(request, options.timeoutMs ?? CHECK_BUDGET_MS), request, this.checked);
|
|
6351
6406
|
} catch (error) {
|
|
6407
|
+
if (this.host.strict && refused(error)) throw error;
|
|
6352
6408
|
return this.host.coordinator.failed(request, options.failOpen, error);
|
|
6353
6409
|
}
|
|
6354
6410
|
}
|
|
@@ -6960,6 +7016,7 @@ var SessionState = class {
|
|
|
6960
7016
|
/** The guards of the last read: they hold the answers to the turn they were written for. */
|
|
6961
7017
|
guards = /* @__PURE__ */ new Map();
|
|
6962
7018
|
lastReport = null;
|
|
7019
|
+
unlinkedSaid = false;
|
|
6963
7020
|
/** After the first pack, every read also asks what changed since. */
|
|
6964
7021
|
get wantsDelta() {
|
|
6965
7022
|
return this.etag !== null;
|
|
@@ -6988,6 +7045,19 @@ var SessionState = class {
|
|
|
6988
7045
|
this.last = absorbed;
|
|
6989
7046
|
return absorbed;
|
|
6990
7047
|
}
|
|
7048
|
+
/**
|
|
7049
|
+
* Once per session: the read named an organization (`about`) with no active link to the subject, so it went
|
|
7050
|
+
* on with the subject's own memory, never the organization's.
|
|
7051
|
+
*/
|
|
7052
|
+
sayUnlinked(result2, logger) {
|
|
7053
|
+
if (result2.response?.about_unlinked && !this.unlinkedSaid) {
|
|
7054
|
+
this.unlinkedSaid = true;
|
|
7055
|
+
logger.warn(
|
|
7056
|
+
"`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."
|
|
7057
|
+
);
|
|
7058
|
+
}
|
|
7059
|
+
return result2;
|
|
7060
|
+
}
|
|
6991
7061
|
/** The last backing check of an agent's answer. */
|
|
6992
7062
|
get lastBacking() {
|
|
6993
7063
|
return this.lastReport;
|
|
@@ -7235,7 +7305,7 @@ var Conversation = class {
|
|
|
7235
7305
|
* `query` picks this read's slots by other words than the turn; the pack is the pinned one.
|
|
7236
7306
|
*/
|
|
7237
7307
|
async context(options = {}) {
|
|
7238
|
-
const { query, turn: turn2, format, explain, include, ...requestOptions } = options;
|
|
7308
|
+
const { query, turn: turn2, format, explain: explain2, include, ...requestOptions } = options;
|
|
7239
7309
|
const params = {
|
|
7240
7310
|
subject: this.subject,
|
|
7241
7311
|
view: this.view,
|
|
@@ -7244,7 +7314,7 @@ var Conversation = class {
|
|
|
7244
7314
|
...this.params.about ? { about: this.params.about } : {},
|
|
7245
7315
|
...this.params.target ? { target: this.params.target } : {},
|
|
7246
7316
|
...format === "json" ? { format } : {},
|
|
7247
|
-
...
|
|
7317
|
+
...explain2 ? { explain: explain2 } : {}
|
|
7248
7318
|
};
|
|
7249
7319
|
if (query) params.query = query;
|
|
7250
7320
|
if (include?.length) params.include = include;
|
|
@@ -7252,7 +7322,7 @@ var Conversation = class {
|
|
|
7252
7322
|
params.turn = turn2 === void 0 ? this.turnText : turn2;
|
|
7253
7323
|
const result2 = await this.client.context(params, requestOptions);
|
|
7254
7324
|
this.features.observe(result2);
|
|
7255
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7325
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7256
7326
|
}
|
|
7257
7327
|
/**
|
|
7258
7328
|
* Starts this conversation's first read now, in the background: call it when the call starts
|
|
@@ -7432,6 +7502,35 @@ function withHandles(base, extra) {
|
|
|
7432
7502
|
return all;
|
|
7433
7503
|
}
|
|
7434
7504
|
|
|
7505
|
+
// src/warm.ts
|
|
7506
|
+
var EVERY_MS = 1e5;
|
|
7507
|
+
var IDLE_MS = 9e4;
|
|
7508
|
+
var WARM_FOR_MS = 6e5;
|
|
7509
|
+
var KeepWarm = class {
|
|
7510
|
+
constructor(enabled) {
|
|
7511
|
+
this.enabled = enabled;
|
|
7512
|
+
}
|
|
7513
|
+
enabled;
|
|
7514
|
+
open = /* @__PURE__ */ new Map();
|
|
7515
|
+
openedAt = 0;
|
|
7516
|
+
/** Notes an open conversation or task. */
|
|
7517
|
+
add(scope, session, now) {
|
|
7518
|
+
if (!this.enabled || typeof WeakRef === "undefined") return;
|
|
7519
|
+
this.open.set(scope, new WeakRef(session));
|
|
7520
|
+
this.openedAt = now;
|
|
7521
|
+
}
|
|
7522
|
+
/** The conversation or task ended. */
|
|
7523
|
+
end(scope) {
|
|
7524
|
+
this.open.delete(scope);
|
|
7525
|
+
}
|
|
7526
|
+
step(now, lastActivityAt, idleMs = IDLE_MS, warmForMs = WARM_FOR_MS) {
|
|
7527
|
+
for (const [scope, ref] of this.open) if (ref.deref() === void 0) this.open.delete(scope);
|
|
7528
|
+
const used = Math.max(lastActivityAt ?? 0, this.openedAt);
|
|
7529
|
+
if (this.open.size === 0 || now - used > warmForMs) return "stop";
|
|
7530
|
+
return now - used >= idleMs ? "ping" : "wait";
|
|
7531
|
+
}
|
|
7532
|
+
};
|
|
7533
|
+
|
|
7435
7534
|
// src/exit.ts
|
|
7436
7535
|
var registered = /* @__PURE__ */ new Set();
|
|
7437
7536
|
var installedOn = null;
|
|
@@ -7561,8 +7660,10 @@ var DEFAULT_TIMEOUTS = {
|
|
|
7561
7660
|
write: 5e3,
|
|
7562
7661
|
token: 2e3,
|
|
7563
7662
|
upload: 6e4,
|
|
7564
|
-
prefetch: 1e3
|
|
7663
|
+
prefetch: 1e3,
|
|
7664
|
+
connect: 1e3
|
|
7565
7665
|
};
|
|
7666
|
+
var FETCH_KEEPALIVE_MS = 4e3;
|
|
7566
7667
|
var DEFAULT_CACHE = {
|
|
7567
7668
|
ttlMs: 1e4,
|
|
7568
7669
|
staleWhileRevalidateMs: 10 * 6e4,
|
|
@@ -7669,7 +7770,7 @@ var Task = class {
|
|
|
7669
7770
|
* Resolves with an empty result, never rejects, unless the client is strict.
|
|
7670
7771
|
*/
|
|
7671
7772
|
async context(options = {}) {
|
|
7672
|
-
const { query, format, explain, include, ...requestOptions } = options;
|
|
7773
|
+
const { query, format, explain: explain2, include, ...requestOptions } = options;
|
|
7673
7774
|
const target = this.object ? { object: this.object } : this.params.subject ? { subject: this.params.subject } : {};
|
|
7674
7775
|
const params = {
|
|
7675
7776
|
...target,
|
|
@@ -7679,18 +7780,18 @@ var Task = class {
|
|
|
7679
7780
|
...this.level ? { verification: this.level } : {},
|
|
7680
7781
|
...this.params.target ? { target: this.params.target } : {},
|
|
7681
7782
|
...format === "json" ? { format } : {},
|
|
7682
|
-
...
|
|
7783
|
+
...explain2 ? { explain: explain2 } : {},
|
|
7683
7784
|
...include?.length ? { include } : {}
|
|
7684
7785
|
};
|
|
7685
7786
|
if (query) {
|
|
7686
7787
|
const answered = await this.client.context({ ...params, query }, requestOptions);
|
|
7687
7788
|
this.features.observe(answered);
|
|
7688
|
-
return this.state.observe(answered);
|
|
7789
|
+
return this.state.observe(this.state.sayUnlinked(answered, this.logger));
|
|
7689
7790
|
}
|
|
7690
7791
|
if (this.state.wantsDelta) params.delta = true;
|
|
7691
7792
|
const result2 = await this.client.context(params, requestOptions);
|
|
7692
7793
|
this.features.observe(result2);
|
|
7693
|
-
return this.state.observe(this.state.absorb(result2));
|
|
7794
|
+
return this.state.observe(this.state.sayUnlinked(this.state.absorb(result2), this.logger));
|
|
7694
7795
|
}
|
|
7695
7796
|
/**
|
|
7696
7797
|
* The agent's own working notes for this task's prompt, as `niadra.agentMemory()` with
|
|
@@ -7820,14 +7921,14 @@ var AGENT_MEMORY_TOOL_NAMES = {
|
|
|
7820
7921
|
search: "search_agent_memory",
|
|
7821
7922
|
remember: "remember"
|
|
7822
7923
|
};
|
|
7823
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
7924
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait", "system_event"];
|
|
7824
7925
|
var NOTE_KINDS = ["procedure", "tool_note", "process_note", "pitfall"];
|
|
7825
7926
|
var TOOL_DEFINITIONS = [
|
|
7826
7927
|
{
|
|
7827
7928
|
"type": "function",
|
|
7828
7929
|
"function": {
|
|
7829
7930
|
"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 '
|
|
7931
|
+
"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
7932
|
"parameters": {
|
|
7832
7933
|
"type": "object",
|
|
7833
7934
|
"properties": {
|
|
@@ -7871,7 +7972,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7871
7972
|
"open_item",
|
|
7872
7973
|
"action",
|
|
7873
7974
|
"object",
|
|
7874
|
-
"trait"
|
|
7975
|
+
"trait",
|
|
7976
|
+
"system_event"
|
|
7875
7977
|
]
|
|
7876
7978
|
}
|
|
7877
7979
|
},
|
|
@@ -7898,7 +8000,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7898
8000
|
"type": "function",
|
|
7899
8001
|
"function": {
|
|
7900
8002
|
"name": "get_customer_timeline",
|
|
7901
|
-
"description": "List this customer's conversations
|
|
8003
|
+
"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
8004
|
"parameters": {
|
|
7903
8005
|
"type": "object",
|
|
7904
8006
|
"properties": {
|
|
@@ -7946,7 +8048,8 @@ var TOOL_DEFINITIONS = [
|
|
|
7946
8048
|
"open_item",
|
|
7947
8049
|
"action",
|
|
7948
8050
|
"object",
|
|
7949
|
-
"trait"
|
|
8051
|
+
"trait",
|
|
8052
|
+
"system_event"
|
|
7950
8053
|
]
|
|
7951
8054
|
}
|
|
7952
8055
|
},
|
|
@@ -7965,7 +8068,7 @@ var TOOL_DEFINITIONS = [
|
|
|
7965
8068
|
"type": "function",
|
|
7966
8069
|
"function": {
|
|
7967
8070
|
"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.",
|
|
8071
|
+
"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
8072
|
"parameters": {
|
|
7970
8073
|
"type": "object",
|
|
7971
8074
|
"properties": {
|
|
@@ -8336,6 +8439,13 @@ function compose(body, read2) {
|
|
|
8336
8439
|
const pack = body.pack ? { pack: { ...body.pack, slots: fetched?.pack?.slots ?? [] } } : {};
|
|
8337
8440
|
return { ...body, slots: fetched?.slots ?? null, guards: fetched?.guards ?? [], ...pack };
|
|
8338
8441
|
}
|
|
8442
|
+
var RTT_MARGIN_MS = 50;
|
|
8443
|
+
function budgetWarnings(rttMs, timeouts, explicit) {
|
|
8444
|
+
const ms = Math.round(rttMs);
|
|
8445
|
+
return [...explicit].sort().filter((name) => timeouts[name] < rttMs + RTT_MARGIN_MS).map(
|
|
8446
|
+
(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`
|
|
8447
|
+
);
|
|
8448
|
+
}
|
|
8339
8449
|
function rttWarnings(rttMs, timeouts) {
|
|
8340
8450
|
const ms = Math.round(rttMs);
|
|
8341
8451
|
const found2 = [];
|
|
@@ -8373,6 +8483,12 @@ var Niadra = class _Niadra {
|
|
|
8373
8483
|
enabled;
|
|
8374
8484
|
core;
|
|
8375
8485
|
timeouts;
|
|
8486
|
+
keepWarm;
|
|
8487
|
+
warmTimer;
|
|
8488
|
+
/** The read budgets the caller left at their defaults: they take the measured round trip on top. */
|
|
8489
|
+
defaultReads;
|
|
8490
|
+
voiceStarted = false;
|
|
8491
|
+
voiceWarned = false;
|
|
8376
8492
|
strict;
|
|
8377
8493
|
/** Where the client reports what it swallows in fail-open mode. */
|
|
8378
8494
|
logger;
|
|
@@ -8417,6 +8533,8 @@ var Niadra = class _Niadra {
|
|
|
8417
8533
|
this.strict = options.strict ?? false;
|
|
8418
8534
|
this.logger = options.logger ?? consoleLogger;
|
|
8419
8535
|
this.timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
8536
|
+
this.keepWarm = new KeepWarm(options.keepWarm ?? true);
|
|
8537
|
+
this.defaultReads = new Set(["context", "navigation"].filter((name) => options.timeouts?.[name] === void 0));
|
|
8420
8538
|
this.voice = new VoiceLines(
|
|
8421
8539
|
options.voice === false ? { ...DEFAULT_VOICE, enabled: false } : { ...DEFAULT_VOICE, ...options.voice }
|
|
8422
8540
|
);
|
|
@@ -8442,13 +8560,14 @@ var Niadra = class _Niadra {
|
|
|
8442
8560
|
this.coordinator = new Coordinator(
|
|
8443
8561
|
this.outbox,
|
|
8444
8562
|
this.suppressions,
|
|
8445
|
-
(body, key2) => this.api.declare(body, { idempotency_key: key2 })
|
|
8563
|
+
(body, key2) => this.api.declare(body, { idempotency_key: key2 }),
|
|
8564
|
+
this.logger
|
|
8446
8565
|
);
|
|
8447
8566
|
this.states = new AgentStates(
|
|
8448
8567
|
this.outbox,
|
|
8449
8568
|
{
|
|
8450
|
-
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.
|
|
8451
|
-
write: (write) => this.api.writeAgentState(write, { timeout: this.
|
|
8569
|
+
read: (scope, agent) => this.api.readAgentState({ scope, agent }, { timeout: this.readBudget("navigation") }),
|
|
8570
|
+
write: (write) => this.api.writeAgentState(write, { timeout: this.readBudget("navigation") })
|
|
8452
8571
|
},
|
|
8453
8572
|
this.logger
|
|
8454
8573
|
);
|
|
@@ -8475,6 +8594,7 @@ var Niadra = class _Niadra {
|
|
|
8475
8594
|
this.core = setup;
|
|
8476
8595
|
this.enabled = true;
|
|
8477
8596
|
this.disabledReason = null;
|
|
8597
|
+
this.probe(setup);
|
|
8478
8598
|
if (options.flushOnExit ?? true) this.unregisterExit = registerExitFlush(this);
|
|
8479
8599
|
}
|
|
8480
8600
|
setup(options) {
|
|
@@ -8497,7 +8617,9 @@ var Niadra = class _Niadra {
|
|
|
8497
8617
|
apiKey,
|
|
8498
8618
|
fetch: fetchImpl,
|
|
8499
8619
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
8500
|
-
logger: this.logger
|
|
8620
|
+
logger: this.logger,
|
|
8621
|
+
coldAllowanceMs: this.timeouts.connect,
|
|
8622
|
+
keepAliveMs: options.keepAliveMs ?? FETCH_KEEPALIVE_MS
|
|
8501
8623
|
});
|
|
8502
8624
|
const queueOptions = { ...DEFAULT_QUEUE, ...options.queue };
|
|
8503
8625
|
queueOptions.maxBatchSize = Math.min(queueOptions.maxBatchSize, 499);
|
|
@@ -8553,7 +8675,7 @@ var Niadra = class _Niadra {
|
|
|
8553
8675
|
return result2;
|
|
8554
8676
|
}
|
|
8555
8677
|
readContext(core, params, request, options) {
|
|
8556
|
-
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.
|
|
8678
|
+
const timeout = options.timeout ?? (request.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8557
8679
|
const { query: own2, ...pinned2 } = request;
|
|
8558
8680
|
const query = turnText(own2 ?? params.turn);
|
|
8559
8681
|
const voiceCache = this.voiceCache(core, pinned2, options.cache);
|
|
@@ -8569,7 +8691,7 @@ var Niadra = class _Niadra {
|
|
|
8569
8691
|
*/
|
|
8570
8692
|
async profile() {
|
|
8571
8693
|
if (!this.core) return null;
|
|
8572
|
-
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.
|
|
8694
|
+
return this.profileCache.refresh(() => this.api.sdkProfile({ timeout: this.readBudget("navigation") }));
|
|
8573
8695
|
}
|
|
8574
8696
|
/**
|
|
8575
8697
|
* Checks outputs against this claim contract instead of the one the profile serves (a company's own copy,
|
|
@@ -8594,7 +8716,7 @@ var Niadra = class _Niadra {
|
|
|
8594
8716
|
*/
|
|
8595
8717
|
async mayContact(handle, purpose, options = {}) {
|
|
8596
8718
|
if (this.core && this.suppressions.due()) {
|
|
8597
|
-
const read2 = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.
|
|
8719
|
+
const read2 = this.readSuppressions(this.suppressions.held ? this.timeouts.write : this.readBudget("navigation"));
|
|
8598
8720
|
if (!this.suppressions.held) await read2;
|
|
8599
8721
|
}
|
|
8600
8722
|
const checkOptions = { channel: options.channel ?? null };
|
|
@@ -8604,8 +8726,8 @@ var Niadra = class _Niadra {
|
|
|
8604
8726
|
readSuppressions(budgetMs) {
|
|
8605
8727
|
return this.suppressions.read(
|
|
8606
8728
|
{
|
|
8607
|
-
salt: () => this.api.suppressionSalt({ timeout: this.
|
|
8608
|
-
page: (cursor, limit2) => this.api.suppressions({ cursor, limit: limit2 }, { timeout: this.
|
|
8729
|
+
salt: () => this.api.suppressionSalt({ timeout: this.readBudget("navigation") }),
|
|
8730
|
+
page: (cursor, limit2) => this.api.suppressions({ cursor, limit: limit2 }, { timeout: this.readBudget("navigation") })
|
|
8609
8731
|
},
|
|
8610
8732
|
budgetMs
|
|
8611
8733
|
);
|
|
@@ -8684,6 +8806,7 @@ var Niadra = class _Niadra {
|
|
|
8684
8806
|
}
|
|
8685
8807
|
/** What a conversation or a task needs of its client for the agent features. */
|
|
8686
8808
|
get agentHost() {
|
|
8809
|
+
const client = this;
|
|
8687
8810
|
return {
|
|
8688
8811
|
recorder: this.turns,
|
|
8689
8812
|
coordinator: this.coordinator,
|
|
@@ -8697,7 +8820,10 @@ var Niadra = class _Niadra {
|
|
|
8697
8820
|
claim: (request, timeoutMs) => this.api.claim(request, {}, { timeout: timeoutMs }),
|
|
8698
8821
|
verifyClaim: (ref, field, value, options) => this.verifyClaim(ref, field, value, options),
|
|
8699
8822
|
enabled: this.enabled,
|
|
8700
|
-
|
|
8823
|
+
strict: this.strict,
|
|
8824
|
+
get navigationMs() {
|
|
8825
|
+
return client.readBudget("navigation");
|
|
8826
|
+
}
|
|
8701
8827
|
};
|
|
8702
8828
|
}
|
|
8703
8829
|
/**
|
|
@@ -8725,7 +8851,7 @@ var Niadra = class _Niadra {
|
|
|
8725
8851
|
if (!cache) return false;
|
|
8726
8852
|
const key2 = cacheKey(request);
|
|
8727
8853
|
const line = this.voice.line(cacheScope(request) ?? "");
|
|
8728
|
-
this.
|
|
8854
|
+
this.startVoice(core);
|
|
8729
8855
|
line.request = request;
|
|
8730
8856
|
if (!cache.has(key2) && line.inFlight().length === 0) {
|
|
8731
8857
|
this.voiceRead(core, cache, line, key2, request, null, this.timeouts.contextVoiceStart);
|
|
@@ -8862,13 +8988,13 @@ var Niadra = class _Niadra {
|
|
|
8862
8988
|
if (!params.query || params.query.length > 2e3) {
|
|
8863
8989
|
throw new NiadraValidationError("query must be 1 to 2000 characters");
|
|
8864
8990
|
}
|
|
8865
|
-
return this.readSpec("POST", "/v1/history/search", params, this.
|
|
8991
|
+
return this.readSpec("POST", "/v1/history/search", params, this.readBudget("navigation"), options);
|
|
8866
8992
|
});
|
|
8867
8993
|
}
|
|
8868
8994
|
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
8869
8995
|
async timeline(params, options = {}) {
|
|
8870
8996
|
return this.navigate(
|
|
8871
|
-
() => this.readSpec("POST", "/v1/history/timeline", params, this.
|
|
8997
|
+
() => this.readSpec("POST", "/v1/history/timeline", params, this.readBudget("navigation"), options)
|
|
8872
8998
|
);
|
|
8873
8999
|
}
|
|
8874
9000
|
/**
|
|
@@ -8881,9 +9007,10 @@ var Niadra = class _Niadra {
|
|
|
8881
9007
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
8882
9008
|
const body = { item_id: id };
|
|
8883
9009
|
if (params.subject) body.subject = params.subject;
|
|
9010
|
+
if (params.about) body.about = params.about;
|
|
8884
9011
|
if (params.verification) body.verification = params.verification;
|
|
8885
9012
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
8886
|
-
return this.readSpec("POST", "/v1/history/open", body, this.
|
|
9013
|
+
return this.readSpec("POST", "/v1/history/open", body, this.readBudget("navigation"), options);
|
|
8887
9014
|
});
|
|
8888
9015
|
}
|
|
8889
9016
|
/**
|
|
@@ -8895,7 +9022,7 @@ var Niadra = class _Niadra {
|
|
|
8895
9022
|
* const { data: invoice } = await niadra.objectState("invoice:erp:0823");
|
|
8896
9023
|
*/
|
|
8897
9024
|
async objectState(object, options = {}) {
|
|
8898
|
-
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.
|
|
9025
|
+
return this.navigate(() => this.readSpec("GET", objectPath(object), void 0, this.readBudget("navigation"), options));
|
|
8899
9026
|
}
|
|
8900
9027
|
/**
|
|
8901
9028
|
* System events and agent actions about one object, newest first, one line each and never
|
|
@@ -8908,7 +9035,7 @@ var Niadra = class _Niadra {
|
|
|
8908
9035
|
throw new NiadraValidationError("limit must be between 1 and 100");
|
|
8909
9036
|
}
|
|
8910
9037
|
const path = `${objectPath(object)}/timeline`;
|
|
8911
|
-
const spec = this.readSpec("GET", path, void 0, this.
|
|
9038
|
+
const spec = this.readSpec("GET", path, void 0, this.readBudget("navigation"), options);
|
|
8912
9039
|
spec.query = { cursor: params.cursor, limit: String(limit2) };
|
|
8913
9040
|
return spec;
|
|
8914
9041
|
});
|
|
@@ -8927,6 +9054,7 @@ var Niadra = class _Niadra {
|
|
|
8927
9054
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
8928
9055
|
open: (id, customer, bound, voice) => {
|
|
8929
9056
|
const scope = { subject: customer };
|
|
9057
|
+
if (bound.about) scope.about = bound.about;
|
|
8930
9058
|
if (bound.verification) scope.verification = bound.verification;
|
|
8931
9059
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
8932
9060
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
@@ -8961,7 +9089,7 @@ var Niadra = class _Niadra {
|
|
|
8961
9089
|
const key2 = AgentMemoryCache.key(params);
|
|
8962
9090
|
const fresh = cache?.fresh(key2);
|
|
8963
9091
|
if (fresh) return blockResult(fresh, "cache");
|
|
8964
|
-
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.
|
|
9092
|
+
const timeout = options.timeout ?? (params.view === "voice" ? this.timeouts.contextVoice : this.readBudget("context"));
|
|
8965
9093
|
const spec = this.readSpec("GET", "/v1/agent-memory/block", void 0, timeout, options);
|
|
8966
9094
|
spec.query = {
|
|
8967
9095
|
max_tokens: String(params.max_tokens ?? 300),
|
|
@@ -8998,7 +9126,7 @@ var Niadra = class _Niadra {
|
|
|
8998
9126
|
if (params.limit !== void 0) body.limit = params.limit;
|
|
8999
9127
|
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
9000
9128
|
if (params.task_id) body.task_id = params.task_id;
|
|
9001
|
-
return this.readSpec("POST", "/v1/agent-memory/search", body, this.
|
|
9129
|
+
return this.readSpec("POST", "/v1/agent-memory/search", body, this.readBudget("navigation"), options);
|
|
9002
9130
|
});
|
|
9003
9131
|
return result2.error ? result2 : { data: result2.data.notes, error: null };
|
|
9004
9132
|
}
|
|
@@ -9092,6 +9220,41 @@ var Niadra = class _Niadra {
|
|
|
9092
9220
|
return { ok: false, idempotency_key: key2, error: this.swallow(error, "feedback") };
|
|
9093
9221
|
}
|
|
9094
9222
|
}
|
|
9223
|
+
/**
|
|
9224
|
+
* How the space's agents used the context they read (`GET /v1/context-use`): sessions, deliveries, use,
|
|
9225
|
+
* repetition, transfers and recontact, with intervals, grouped by `group_by`. A key of an `analyst` source
|
|
9226
|
+
* with the `analytics` scope reads every source of the space; a key with `admin` reads its own source.
|
|
9227
|
+
*/
|
|
9228
|
+
contextUse(params = {}, options = {}) {
|
|
9229
|
+
return this.navigate(() => {
|
|
9230
|
+
const { group_by: groups, ...filters } = params;
|
|
9231
|
+
const query = { ...filters };
|
|
9232
|
+
if (groups?.length) query.group_by = groups;
|
|
9233
|
+
return { ...this.readSpec("GET", "/v1/context-use", void 0, this.timeouts.write, options), query };
|
|
9234
|
+
});
|
|
9235
|
+
}
|
|
9236
|
+
/**
|
|
9237
|
+
* Links a person to the organization they act for (an account or a partner), as a system of record that
|
|
9238
|
+
* knows who works for whom: a CRM, an HR system. Needs a key with the `identity:link` scope (or `admin`);
|
|
9239
|
+
* `can_see_contacts` needs `admin`. Reads with `about` reach the organization through the link.
|
|
9240
|
+
*/
|
|
9241
|
+
link(params, options = {}) {
|
|
9242
|
+
const { idempotency_key: key2, ...rest } = params;
|
|
9243
|
+
const body = { can_see_contacts: false, method: "system_import", ...rest };
|
|
9244
|
+
return this.navigate(() => this.writeSpec("/v1/identity/links", body, key2 ?? uuidv7(), options));
|
|
9245
|
+
}
|
|
9246
|
+
/**
|
|
9247
|
+
* Ends a link, from `valid_to` (now when absent): the person no longer acts for the organization, and reads
|
|
9248
|
+
* with `about` for the pair go on with the person's own memory. Needs `identity:link` or `admin`.
|
|
9249
|
+
*/
|
|
9250
|
+
endLink(linkId, params = {}, options = {}) {
|
|
9251
|
+
return this.navigate(() => {
|
|
9252
|
+
if (!linkId) throw new NiadraValidationError("endLink() needs a link id");
|
|
9253
|
+
const body = params.valid_to ? { valid_to: params.valid_to } : {};
|
|
9254
|
+
const path = `/v1/identity/links/${encodeURIComponent(linkId)}/end`;
|
|
9255
|
+
return this.writeSpec(path, body, params.idempotency_key ?? uuidv7(), options);
|
|
9256
|
+
});
|
|
9257
|
+
}
|
|
9095
9258
|
/**
|
|
9096
9259
|
* Up to 500 corrections in one call, each with its own idempotency key (minted when missing).
|
|
9097
9260
|
* Resolves with `accepted`, `duplicates` for replayed keys and one error per refused item, by index.
|
|
@@ -9184,16 +9347,20 @@ var Niadra = class _Niadra {
|
|
|
9184
9347
|
* emits `conversation.ended` when you call `end()`.
|
|
9185
9348
|
*/
|
|
9186
9349
|
conversation(params) {
|
|
9187
|
-
|
|
9350
|
+
const conversation = new Conversation(this, params, {
|
|
9188
9351
|
endConversation: (id) => this.endScope(buildConversationEnded(id), `conversation:${id}`)
|
|
9189
9352
|
});
|
|
9353
|
+
this.warm(`conversation:${conversation.id}`, conversation);
|
|
9354
|
+
return conversation;
|
|
9190
9355
|
}
|
|
9191
9356
|
/** A helper for one internal-agent task: binds `task_id` to reads and writes and emits `task.ended`. */
|
|
9192
9357
|
task(params) {
|
|
9193
|
-
|
|
9358
|
+
const task = new Task(this, params, {
|
|
9194
9359
|
endTask: (id) => this.endScope(buildTaskEnded(id), `task:${id}`),
|
|
9195
9360
|
verifyTask: (verify) => this.verifyWith(verify)
|
|
9196
9361
|
});
|
|
9362
|
+
this.warm(`task:${task.id}`, task);
|
|
9363
|
+
return task;
|
|
9197
9364
|
}
|
|
9198
9365
|
/**
|
|
9199
9366
|
* Sends every queued event and resolves when done. Call it before a serverless function
|
|
@@ -9213,6 +9380,8 @@ var Niadra = class _Niadra {
|
|
|
9213
9380
|
*/
|
|
9214
9381
|
async shutdown() {
|
|
9215
9382
|
this.unregisterExit();
|
|
9383
|
+
clearInterval(this.warmTimer);
|
|
9384
|
+
this.warmTimer = void 0;
|
|
9216
9385
|
if (!this.core) return;
|
|
9217
9386
|
await this.turnSender?.stop(this.timeouts.write);
|
|
9218
9387
|
await this.outbox.stop(this.timeouts.write);
|
|
@@ -9316,9 +9485,9 @@ var Niadra = class _Niadra {
|
|
|
9316
9485
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", request, timeout, { signal, headers }));
|
|
9317
9486
|
return normalizeContext(response.data);
|
|
9318
9487
|
} catch (error) {
|
|
9319
|
-
const
|
|
9488
|
+
const refused3 = error instanceof NiadraAPIError && error.status === 404;
|
|
9320
9489
|
const left = timeout - (Date.now() - started);
|
|
9321
|
-
if (!request.include?.length || !
|
|
9490
|
+
if (!request.include?.length || !refused3 || left <= 0) throw error;
|
|
9322
9491
|
for (const name of request.include) this.refusedBlocks.set(name, Date.now() + BLOCK_RECHECK_AFTER_MS);
|
|
9323
9492
|
const { include: _dropped, ...plain2 } = request;
|
|
9324
9493
|
const response = await core.transport.request(this.readSpec("POST", "/v1/context", plain2, left, { signal, headers }));
|
|
@@ -9406,9 +9575,32 @@ var Niadra = class _Niadra {
|
|
|
9406
9575
|
const rtt = Math.min(...samples);
|
|
9407
9576
|
this.voice.rtt = rtt;
|
|
9408
9577
|
this.logger.debug(`round trip to the region ${Math.round(rtt)} ms`);
|
|
9409
|
-
|
|
9578
|
+
const explicit = ["context", "navigation"].filter((name) => !this.defaultReads.has(name));
|
|
9579
|
+
for (const warning of budgetWarnings(rtt, this.timeouts, explicit)) this.logger.warn(warning);
|
|
9580
|
+
if (this.voiceStarted) this.warnVoice();
|
|
9410
9581
|
})();
|
|
9411
9582
|
}
|
|
9583
|
+
/**
|
|
9584
|
+
* A read budget: one the caller left at its default is what the API may take, and the measured round trip
|
|
9585
|
+
* to the region goes on top, so an agent far from the region (Sao Paulo, 170 ms from us-east-2) is not
|
|
9586
|
+
* timed out by the network; one the caller set is a ceiling.
|
|
9587
|
+
*/
|
|
9588
|
+
readBudget(name) {
|
|
9589
|
+
const rtt = this.voice.rtt;
|
|
9590
|
+
return rtt !== null && this.defaultReads.has(name) ? this.timeouts[name] + rtt : this.timeouts[name];
|
|
9591
|
+
}
|
|
9592
|
+
/** The client reads in voice: the voice budgets' warnings matter from now on. */
|
|
9593
|
+
startVoice(core) {
|
|
9594
|
+
this.voiceStarted = true;
|
|
9595
|
+
this.probe(core);
|
|
9596
|
+
this.warnVoice();
|
|
9597
|
+
}
|
|
9598
|
+
warnVoice() {
|
|
9599
|
+
const rtt = this.voice.rtt;
|
|
9600
|
+
if (this.voiceWarned || rtt === null) return;
|
|
9601
|
+
this.voiceWarned = true;
|
|
9602
|
+
for (const warning of rttWarnings(rtt, this.timeouts)) this.logger.warn(warning);
|
|
9603
|
+
}
|
|
9412
9604
|
/**
|
|
9413
9605
|
* A voice turn: the pinned body from memory, and the slots of the read of its words when that
|
|
9414
9606
|
* read lands within `timeout`. See `voice.ts`.
|
|
@@ -9418,7 +9610,7 @@ var Niadra = class _Niadra {
|
|
|
9418
9610
|
const key2 = cacheKey(request);
|
|
9419
9611
|
const deadline = Date.now() + timeout;
|
|
9420
9612
|
const line = this.voice.line(scope);
|
|
9421
|
-
this.
|
|
9613
|
+
this.startVoice(core);
|
|
9422
9614
|
line.request = request;
|
|
9423
9615
|
const words2 = wordsOf(query);
|
|
9424
9616
|
const background = this.timeouts.prefetch;
|
|
@@ -9604,8 +9796,39 @@ var Niadra = class _Niadra {
|
|
|
9604
9796
|
core.queue.flushInBackground(true);
|
|
9605
9797
|
});
|
|
9606
9798
|
}
|
|
9799
|
+
/** Notes an open conversation or task; the first one starts the keep-warm timer (`warm.ts`). */
|
|
9800
|
+
warm(scope, session) {
|
|
9801
|
+
const core = this.core;
|
|
9802
|
+
if (!core || !this.keepWarm.enabled) return;
|
|
9803
|
+
this.keepWarm.add(scope, session, Date.now());
|
|
9804
|
+
if (this.warmTimer !== void 0) return;
|
|
9805
|
+
const timer = setInterval(() => {
|
|
9806
|
+
this.warmTick(core);
|
|
9807
|
+
}, EVERY_MS);
|
|
9808
|
+
timer.unref?.();
|
|
9809
|
+
this.warmTimer = timer;
|
|
9810
|
+
}
|
|
9811
|
+
warmTick(core) {
|
|
9812
|
+
const step = this.keepWarm.step(Date.now(), core.transport.lastActivityAt);
|
|
9813
|
+
if (step === "stop") {
|
|
9814
|
+
clearInterval(this.warmTimer);
|
|
9815
|
+
this.warmTimer = void 0;
|
|
9816
|
+
return;
|
|
9817
|
+
}
|
|
9818
|
+
if (step !== "ping") return;
|
|
9819
|
+
core.transport.request({
|
|
9820
|
+
method: "GET",
|
|
9821
|
+
path: "/healthz",
|
|
9822
|
+
timeoutMs: 2e3,
|
|
9823
|
+
retry: { kind: "read", maxAttempts: 1 },
|
|
9824
|
+
activity: false
|
|
9825
|
+
}).catch((error) => {
|
|
9826
|
+
this.logger.debug(`keep-warm ping failed: ${describe(toNiadraError(error))}`);
|
|
9827
|
+
});
|
|
9828
|
+
}
|
|
9607
9829
|
async endScope(item, scope) {
|
|
9608
9830
|
this.forgetScope(scope);
|
|
9831
|
+
this.keepWarm.end(scope);
|
|
9609
9832
|
return this.sendNow(() => item);
|
|
9610
9833
|
}
|
|
9611
9834
|
async sendBatch(transport, items, maxAttempts, backoff) {
|
|
@@ -9910,7 +10133,7 @@ var Replayer = class {
|
|
|
9910
10133
|
try {
|
|
9911
10134
|
answer = await this.niadra.callRoute({ method: "POST", path: "/v1/scenario-runs", body, idempotencyKey: uuidv7() });
|
|
9912
10135
|
} catch (error) {
|
|
9913
|
-
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return
|
|
10136
|
+
if (error instanceof NiadraAPIError && error.code === "pin_mismatch") return refused2(scenarioIds, results);
|
|
9914
10137
|
throw error;
|
|
9915
10138
|
}
|
|
9916
10139
|
let run = runOf(answer);
|
|
@@ -10013,7 +10236,7 @@ var Replayer = class {
|
|
|
10013
10236
|
return result(scenarioId, turnId, caseId, run, "completed", { paraphrase: rephrased, assertions, divergent_calls: played.divergent, latency_ms: latency });
|
|
10014
10237
|
}
|
|
10015
10238
|
};
|
|
10016
|
-
function
|
|
10239
|
+
function refused2(scenarioIds, results) {
|
|
10017
10240
|
const scenarios = scenarioIds.map((id) => {
|
|
10018
10241
|
const mine = results.filter((r) => r.scenario_id === id);
|
|
10019
10242
|
return {
|