@openbkn/bkn-sdk 0.1.1 → 0.1.2
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/README.md +2 -1
- package/README.zh.md +1 -0
- package/dist/{chunk-NC6DZ2AU.js → chunk-LH3ONZGQ.js} +1785 -77
- package/dist/cli.js +124 -40
- package/dist/index.d.ts +612 -38
- package/dist/index.js +3 -1
- package/package.json +2 -1
|
@@ -26,38 +26,38 @@ var InputError = class extends Error {
|
|
|
26
26
|
this.name = "InputError";
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
-
function toExitCode(
|
|
30
|
-
if (
|
|
31
|
-
if (
|
|
32
|
-
if (
|
|
29
|
+
function toExitCode(err2) {
|
|
30
|
+
if (err2 instanceof InputError) return 2;
|
|
31
|
+
if (err2 instanceof HttpError) {
|
|
32
|
+
if (err2.status === 401 || err2.status === 403) return 3;
|
|
33
33
|
return 1;
|
|
34
34
|
}
|
|
35
35
|
return 1;
|
|
36
36
|
}
|
|
37
|
-
function formatError(
|
|
38
|
-
if (
|
|
39
|
-
const serverMsg = serverError(
|
|
40
|
-
if (
|
|
41
|
-
const next =
|
|
37
|
+
function formatError(err2) {
|
|
38
|
+
if (err2 instanceof HttpError) {
|
|
39
|
+
const serverMsg = serverError(err2.body);
|
|
40
|
+
if (err2.status === 401) {
|
|
41
|
+
const next = err2.hint ?? "Run `openbkn auth login` and retry.";
|
|
42
42
|
return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. ${next}`;
|
|
43
43
|
}
|
|
44
|
-
if (
|
|
44
|
+
if (err2.status === 403) {
|
|
45
45
|
return `Forbidden (HTTP 403)${serverMsg ? `: ${serverMsg}` : " \u2014 admin privileges required"}.`;
|
|
46
46
|
}
|
|
47
|
-
const detail =
|
|
48
|
-
return `Request failed (HTTP ${
|
|
47
|
+
const detail = err2.body ? `: ${truncate(err2.body, 500)}` : "";
|
|
48
|
+
return `Request failed (HTTP ${err2.status} ${err2.statusText})${detail}`;
|
|
49
49
|
}
|
|
50
|
-
if (
|
|
51
|
-
const cause =
|
|
50
|
+
if (err2 instanceof Error) {
|
|
51
|
+
const cause = err2.cause;
|
|
52
52
|
if (cause?.code && isTlsCertError(cause.code)) {
|
|
53
53
|
return `TLS certificate rejected (${cause.code}). The platform is likely self-signed \u2014 retry with \`-k\`/\`--insecure\`.`;
|
|
54
54
|
}
|
|
55
|
-
if (
|
|
55
|
+
if (err2.message === "fetch failed" && cause?.message) {
|
|
56
56
|
return `Request failed: ${cause.message}${cause.code ? ` (${cause.code})` : ""}`;
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return err2.message;
|
|
59
59
|
}
|
|
60
|
-
return String(
|
|
60
|
+
return String(err2);
|
|
61
61
|
}
|
|
62
62
|
function serverError(body) {
|
|
63
63
|
if (!body) return "";
|
|
@@ -80,16 +80,29 @@ function truncate(s, n) {
|
|
|
80
80
|
import { spawn } from "child_process";
|
|
81
81
|
|
|
82
82
|
// src/api/tls.ts
|
|
83
|
-
import { Agent, fetch as undiciFetch } from "undici";
|
|
83
|
+
import { Agent, FormData as UndiciFormData, fetch as undiciFetch } from "undici";
|
|
84
84
|
var insecureAgent;
|
|
85
85
|
function insecureDispatcher() {
|
|
86
86
|
insecureAgent ??= new Agent({ connect: { rejectUnauthorized: false } });
|
|
87
87
|
return insecureAgent;
|
|
88
88
|
}
|
|
89
|
+
function isFormData(body) {
|
|
90
|
+
return typeof body === "object" && body !== null && body[Symbol.toStringTag] === "FormData";
|
|
91
|
+
}
|
|
92
|
+
function toUndiciBody(body) {
|
|
93
|
+
if (body instanceof UndiciFormData || !isFormData(body)) return body;
|
|
94
|
+
const form2 = new UndiciFormData();
|
|
95
|
+
for (const [name, value] of body.entries()) {
|
|
96
|
+
if (typeof value === "string") form2.append(name, value);
|
|
97
|
+
else form2.append(name, value, value.name);
|
|
98
|
+
}
|
|
99
|
+
return form2;
|
|
100
|
+
}
|
|
89
101
|
function tlsFetch(insecure, url, init) {
|
|
90
102
|
if (!insecure) return fetch(url, init);
|
|
91
103
|
return undiciFetch(url, {
|
|
92
104
|
...init,
|
|
105
|
+
...init?.body === void 0 || init?.body === null ? {} : { body: toUndiciBody(init.body) },
|
|
93
106
|
dispatcher: insecureDispatcher()
|
|
94
107
|
});
|
|
95
108
|
}
|
|
@@ -305,11 +318,96 @@ async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
|
|
|
305
318
|
);
|
|
306
319
|
}
|
|
307
320
|
|
|
321
|
+
// src/trace-context.ts
|
|
322
|
+
import { randomBytes, randomUUID } from "crypto";
|
|
323
|
+
var TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/;
|
|
324
|
+
var REQUEST_ID_RE = /^req_[0-9A-Za-z_.-]+$/;
|
|
325
|
+
var CORRELATION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
326
|
+
var RFC3339_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
327
|
+
var ALLOWED_BAGGAGE = /* @__PURE__ */ new Set(["bkn.account.type", "bkn.runtime.env"]);
|
|
328
|
+
function isValidRequestId(value) {
|
|
329
|
+
return typeof value === "string" && REQUEST_ID_RE.test(value);
|
|
330
|
+
}
|
|
331
|
+
function isValidCorrelationId(value) {
|
|
332
|
+
return typeof value === "string" && CORRELATION_ID_RE.test(value.trim());
|
|
333
|
+
}
|
|
334
|
+
function isValidTraceparent(value) {
|
|
335
|
+
if (typeof value !== "string") return false;
|
|
336
|
+
const match = TRACEPARENT_RE.exec(value);
|
|
337
|
+
if (!match) return false;
|
|
338
|
+
const [, traceId, spanId] = match;
|
|
339
|
+
return traceId !== "0".repeat(32) && spanId !== "0".repeat(16);
|
|
340
|
+
}
|
|
341
|
+
function createTraceContext(opts = {}) {
|
|
342
|
+
const requestId = isValidRequestId(opts.requestId) ? opts.requestId : `req_${randomUUID()}`;
|
|
343
|
+
const traceparent = isValidTraceparent(opts.traceparent) ? opts.traceparent : newTraceparent();
|
|
344
|
+
const baggage = filterBaggage(opts.baggage);
|
|
345
|
+
const conversationId = isValidCorrelationId(opts.conversationId) ? opts.conversationId.trim() : void 0;
|
|
346
|
+
const interactionId = isValidCorrelationId(opts.interactionId) ? opts.interactionId.trim() : void 0;
|
|
347
|
+
const operationId = isValidCorrelationId(opts.operationId) ? opts.operationId.trim() : void 0;
|
|
348
|
+
const attempt = Number.isInteger(opts.attempt) && (opts.attempt ?? 0) >= 1 && (opts.attempt ?? 0) <= 1e3 ? opts.attempt : void 0;
|
|
349
|
+
const observedAt = isValidObservedAt(opts.observedAt) ? opts.observedAt : void 0;
|
|
350
|
+
return {
|
|
351
|
+
requestId,
|
|
352
|
+
traceparent,
|
|
353
|
+
...conversationId ? { conversationId } : {},
|
|
354
|
+
...interactionId ? { interactionId } : {},
|
|
355
|
+
...operationId ? { operationId } : {},
|
|
356
|
+
...attempt ? { attempt } : {},
|
|
357
|
+
...observedAt ? { observedAt } : {},
|
|
358
|
+
...Object.keys(baggage).length > 0 ? { baggage } : {}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function createOperationTraceContext(trace2) {
|
|
362
|
+
return {
|
|
363
|
+
...trace2,
|
|
364
|
+
operationId: trace2.operationId ?? `op_${randomUUID()}`,
|
|
365
|
+
attempt: trace2.attempt ?? 1,
|
|
366
|
+
observedAt: isValidObservedAt(trace2.observedAt) ? trace2.observedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function isValidObservedAt(value) {
|
|
370
|
+
return typeof value === "string" && RFC3339_RE.test(value) && !Number.isNaN(Date.parse(value));
|
|
371
|
+
}
|
|
372
|
+
function filterBaggage(baggage) {
|
|
373
|
+
const filtered = {};
|
|
374
|
+
for (const [key, value] of Object.entries(baggage ?? {})) {
|
|
375
|
+
if (ALLOWED_BAGGAGE.has(key) && value !== "") filtered[key] = value;
|
|
376
|
+
}
|
|
377
|
+
return filtered;
|
|
378
|
+
}
|
|
379
|
+
function serializeBaggage(baggage) {
|
|
380
|
+
const filtered = filterBaggage(baggage);
|
|
381
|
+
const entries = Object.entries(filtered);
|
|
382
|
+
if (entries.length === 0) return void 0;
|
|
383
|
+
return entries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
|
|
384
|
+
}
|
|
385
|
+
function newTraceparent() {
|
|
386
|
+
return `00-${randomHex(16)}-${randomHex(8)}-01`;
|
|
387
|
+
}
|
|
388
|
+
function randomHex(bytes) {
|
|
389
|
+
let value = randomBytes(bytes).toString("hex");
|
|
390
|
+
while (/^0+$/.test(value)) value = randomBytes(bytes).toString("hex");
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
|
|
308
394
|
// src/api/headers.ts
|
|
309
395
|
function buildHeaders(ctx, extra) {
|
|
396
|
+
const baggage = serializeBaggage(ctx.trace?.baggage);
|
|
310
397
|
return {
|
|
311
398
|
authorization: `Bearer ${ctx.token}`,
|
|
312
399
|
"x-business-domain": ctx.businessDomain,
|
|
400
|
+
...ctx.trace ? {
|
|
401
|
+
"bkn-request-id": ctx.trace.requestId,
|
|
402
|
+
"x-request-id": ctx.trace.requestId,
|
|
403
|
+
traceparent: ctx.trace.traceparent,
|
|
404
|
+
...ctx.trace.conversationId ? { "bkn-conversation-id": ctx.trace.conversationId } : {},
|
|
405
|
+
...ctx.trace.interactionId ? { "bkn-interaction-id": ctx.trace.interactionId } : {},
|
|
406
|
+
...ctx.trace.operationId ? { "bkn-operation-id": ctx.trace.operationId } : {},
|
|
407
|
+
...ctx.trace.attempt ? { "bkn-attempt": String(ctx.trace.attempt) } : {},
|
|
408
|
+
...ctx.trace.observedAt ? { "bkn-event-observed-at": ctx.trace.observedAt } : {}
|
|
409
|
+
} : {},
|
|
410
|
+
...baggage ? { baggage } : {},
|
|
313
411
|
...extra
|
|
314
412
|
};
|
|
315
413
|
}
|
|
@@ -335,6 +433,7 @@ async function request(ctx, path, init = {}) {
|
|
|
335
433
|
...init.headers
|
|
336
434
|
}),
|
|
337
435
|
body: hasBody ? JSON.stringify(init.body) : void 0,
|
|
436
|
+
redirect: init.redirect,
|
|
338
437
|
signal: controller.signal
|
|
339
438
|
});
|
|
340
439
|
try {
|
|
@@ -588,6 +687,13 @@ function resolveContext(opts = {}) {
|
|
|
588
687
|
token,
|
|
589
688
|
businessDomain: opts.businessDomain ?? readPlatformConfig(normalized).businessDomain ?? DEFAULT_BUSINESS_DOMAIN,
|
|
590
689
|
insecure,
|
|
690
|
+
...opts.evidenceIngestToken ?? process.env.BKN_TRACE_EVIDENCE_INGEST_TOKEN ? {
|
|
691
|
+
evidenceIngestToken: opts.evidenceIngestToken ?? process.env.BKN_TRACE_EVIDENCE_INGEST_TOKEN
|
|
692
|
+
} : {},
|
|
693
|
+
// Correlation ids come from `opts.trace` only. The CLI reads its flags and
|
|
694
|
+
// env vars in `commands/_shared.ts`; a library client must not inherit an
|
|
695
|
+
// ambient interaction id it would then freeze for its whole lifetime.
|
|
696
|
+
trace: createTraceContext(opts.trace),
|
|
591
697
|
...refresh ? { refresh } : {}
|
|
592
698
|
};
|
|
593
699
|
}
|
|
@@ -774,12 +880,12 @@ async function importLicenseSafe(ctx, licenseText, opts = {}) {
|
|
|
774
880
|
method: "POST",
|
|
775
881
|
body: { license: text }
|
|
776
882
|
});
|
|
777
|
-
} catch (
|
|
778
|
-
if (
|
|
779
|
-
const stored = storedImport(
|
|
883
|
+
} catch (err2) {
|
|
884
|
+
if (err2 instanceof HttpError) {
|
|
885
|
+
const stored = storedImport(err2.body);
|
|
780
886
|
if (stored) return stored;
|
|
781
887
|
}
|
|
782
|
-
throw
|
|
888
|
+
throw err2;
|
|
783
889
|
}
|
|
784
890
|
}
|
|
785
891
|
function storedImport(body) {
|
|
@@ -1150,19 +1256,20 @@ function nextId() {
|
|
|
1150
1256
|
function mcpUrl(ctx) {
|
|
1151
1257
|
return `${ctx.baseUrl}${MCP_PATH}`;
|
|
1152
1258
|
}
|
|
1259
|
+
function operationContext(ctx) {
|
|
1260
|
+
return ctx.trace ? { ...ctx, trace: createOperationTraceContext(ctx.trace) } : ctx;
|
|
1261
|
+
}
|
|
1153
1262
|
function mcpInfo(ctx) {
|
|
1154
1263
|
return request(ctx, `${MCP_PATH}/info`);
|
|
1155
1264
|
}
|
|
1156
1265
|
function headers(ctx, knId, sessionId) {
|
|
1157
|
-
|
|
1266
|
+
return buildHeaders(ctx, {
|
|
1158
1267
|
"content-type": "application/json",
|
|
1159
1268
|
accept: "application/json, text/event-stream",
|
|
1160
1269
|
"x-kn-id": knId,
|
|
1161
1270
|
"mcp-protocol-version": PROTOCOL,
|
|
1162
|
-
|
|
1163
|
-
};
|
|
1164
|
-
if (sessionId) h["mcp-session-id"] = sessionId;
|
|
1165
|
-
return h;
|
|
1271
|
+
...sessionId ? { "mcp-session-id": sessionId } : {}
|
|
1272
|
+
});
|
|
1166
1273
|
}
|
|
1167
1274
|
function parseBody(text) {
|
|
1168
1275
|
try {
|
|
@@ -1223,8 +1330,9 @@ function unwrap(parsed) {
|
|
|
1223
1330
|
return result;
|
|
1224
1331
|
}
|
|
1225
1332
|
async function callTool(ctx, knId, name, args) {
|
|
1226
|
-
const
|
|
1227
|
-
const
|
|
1333
|
+
const operationCtx = operationContext(ctx);
|
|
1334
|
+
const sessionId = await ensureSession(operationCtx, knId);
|
|
1335
|
+
const { text } = await post(operationCtx, knId, sessionId, {
|
|
1228
1336
|
jsonrpc: "2.0",
|
|
1229
1337
|
method: "tools/call",
|
|
1230
1338
|
params: { name, arguments: args },
|
|
@@ -1233,8 +1341,9 @@ async function callTool(ctx, knId, name, args) {
|
|
|
1233
1341
|
return unwrap(parseBody(text));
|
|
1234
1342
|
}
|
|
1235
1343
|
async function callMethod(ctx, knId, method, params = {}) {
|
|
1236
|
-
const
|
|
1237
|
-
const
|
|
1344
|
+
const operationCtx = operationContext(ctx);
|
|
1345
|
+
const sessionId = await ensureSession(operationCtx, knId);
|
|
1346
|
+
const { text } = await post(operationCtx, knId, sessionId, {
|
|
1238
1347
|
jsonrpc: "2.0",
|
|
1239
1348
|
method,
|
|
1240
1349
|
params: Object.keys(params).length > 0 ? params : void 0,
|
|
@@ -1369,7 +1478,11 @@ async function executeDataflow(ctx, body, opts = {}) {
|
|
|
1369
1478
|
}
|
|
1370
1479
|
function listDataflowRuns(ctx, dagId, opts = {}) {
|
|
1371
1480
|
return request(ctx, `${BASE2}/dag/${encodeURIComponent(dagId)}/results`, {
|
|
1372
|
-
query: {
|
|
1481
|
+
query: {
|
|
1482
|
+
since: opts.since || void 0,
|
|
1483
|
+
page: opts.page,
|
|
1484
|
+
limit: opts.limit && opts.limit > 0 ? opts.limit : void 0
|
|
1485
|
+
}
|
|
1373
1486
|
});
|
|
1374
1487
|
}
|
|
1375
1488
|
function runDataflowRemote(ctx, dagId, url, name) {
|
|
@@ -1425,9 +1538,11 @@ function deleteKnowledgeNetwork(ctx, knId) {
|
|
|
1425
1538
|
function updateKnowledgeNetwork(ctx, knId, body) {
|
|
1426
1539
|
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}`, { method: "PUT", body });
|
|
1427
1540
|
}
|
|
1541
|
+
var QUERY_OVER_POST = { "X-HTTP-Method-Override": "GET" };
|
|
1428
1542
|
function querySubgraph(ctx, knId, body) {
|
|
1429
1543
|
return request(ctx, `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/subgraph`, {
|
|
1430
1544
|
method: "POST",
|
|
1545
|
+
headers: QUERY_OVER_POST,
|
|
1431
1546
|
body
|
|
1432
1547
|
});
|
|
1433
1548
|
}
|
|
@@ -1459,13 +1574,7 @@ function queryObjectTypeInstances(ctx, knId, otId, body) {
|
|
|
1459
1574
|
return request(
|
|
1460
1575
|
ctx,
|
|
1461
1576
|
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/object-types/${encodeURIComponent(otId)}`,
|
|
1462
|
-
{ method: "POST", body }
|
|
1463
|
-
);
|
|
1464
|
-
}
|
|
1465
|
-
function getObjectTypeProperties(ctx, knId, otId) {
|
|
1466
|
-
return request(
|
|
1467
|
-
ctx,
|
|
1468
|
-
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/object-types/${encodeURIComponent(otId)}/properties`
|
|
1577
|
+
{ method: "POST", headers: QUERY_OVER_POST, body }
|
|
1469
1578
|
);
|
|
1470
1579
|
}
|
|
1471
1580
|
function queryActionType(ctx, knId, atId, body) {
|
|
@@ -1634,7 +1743,10 @@ function listResources2(ctx, opts = {}) {
|
|
|
1634
1743
|
category: opts.category || void 0,
|
|
1635
1744
|
status: opts.status || void 0,
|
|
1636
1745
|
database: opts.database || void 0,
|
|
1637
|
-
|
|
1746
|
+
// Same `/resources` endpoint as `catalogResources`: limit=-1 (NO_LIMIT)
|
|
1747
|
+
// fetches every row; any other non-positive/invalid value falls back to
|
|
1748
|
+
// the backend default.
|
|
1749
|
+
limit: Number.isFinite(opts.limit) && (opts.limit > 0 || opts.limit === -1) ? opts.limit : void 0,
|
|
1638
1750
|
offset: opts.offset,
|
|
1639
1751
|
sort: opts.sort,
|
|
1640
1752
|
direction: opts.direction,
|
|
@@ -1750,18 +1862,31 @@ function deleteResource(ctx, id, opts = {}) {
|
|
|
1750
1862
|
});
|
|
1751
1863
|
}
|
|
1752
1864
|
async function findResource(ctx, name, opts = {}) {
|
|
1753
|
-
const result = await listResources2(ctx, {
|
|
1865
|
+
const result = await listResources2(ctx, {
|
|
1866
|
+
name,
|
|
1867
|
+
datasourceId: opts.datasourceId,
|
|
1868
|
+
limit: opts.limit
|
|
1869
|
+
});
|
|
1754
1870
|
const list = Array.isArray(result) ? result : result.entries ?? [];
|
|
1755
1871
|
return opts.exact ? list.filter((r) => r.name === name) : list;
|
|
1756
1872
|
}
|
|
1757
1873
|
function queryResource(ctx, id, opts = {}) {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1874
|
+
const body = opts.cursor ? {
|
|
1875
|
+
paging: { cursor: opts.cursor },
|
|
1876
|
+
need_total: opts.needTotal ?? false
|
|
1877
|
+
} : {
|
|
1878
|
+
paging: {
|
|
1879
|
+
mode: opts.pagingMode ?? "single",
|
|
1761
1880
|
limit: opts.limit ?? 50,
|
|
1762
1881
|
offset: opts.offset ?? 0,
|
|
1763
|
-
|
|
1764
|
-
}
|
|
1882
|
+
...opts.keepAliveSec !== void 0 ? { keep_alive_sec: opts.keepAliveSec } : {}
|
|
1883
|
+
},
|
|
1884
|
+
need_total: opts.needTotal ?? false
|
|
1885
|
+
};
|
|
1886
|
+
return request(ctx, `${BASE3}/${encodeURIComponent(id)}/data`, {
|
|
1887
|
+
method: "POST",
|
|
1888
|
+
headers: { "X-HTTP-Method-Override": "GET" },
|
|
1889
|
+
body
|
|
1765
1890
|
});
|
|
1766
1891
|
}
|
|
1767
1892
|
|
|
@@ -2999,7 +3124,11 @@ function listBknResources(ctx) {
|
|
|
2999
3124
|
return request(ctx, "/api/bkn-backend/v1/resources");
|
|
3000
3125
|
}
|
|
3001
3126
|
function relationTypePaths(ctx, knId, body) {
|
|
3002
|
-
return request(ctx, knPath(knId, "relation-type-paths"), {
|
|
3127
|
+
return request(ctx, knPath(knId, "relation-type-paths"), {
|
|
3128
|
+
method: "POST",
|
|
3129
|
+
headers: { "X-HTTP-Method-Override": "GET" },
|
|
3130
|
+
body
|
|
3131
|
+
});
|
|
3003
3132
|
}
|
|
3004
3133
|
function listConceptGroups(ctx, knId) {
|
|
3005
3134
|
return request(ctx, knPath(knId, "concept-groups"));
|
|
@@ -3060,18 +3189,6 @@ function setActionScheduleStatus(ctx, knId, scheduleId, body) {
|
|
|
3060
3189
|
function deleteActionSchedules(ctx, knId, ids) {
|
|
3061
3190
|
return request(ctx, knPath(knId, `action-schedules/${ids}`), { method: "DELETE" });
|
|
3062
3191
|
}
|
|
3063
|
-
function listJobs(ctx, knId) {
|
|
3064
|
-
return request(ctx, knPath(knId, "jobs"));
|
|
3065
|
-
}
|
|
3066
|
-
function getJob(ctx, knId, jobId) {
|
|
3067
|
-
return request(ctx, knPath(knId, `jobs/${encodeURIComponent(jobId)}`));
|
|
3068
|
-
}
|
|
3069
|
-
function getJobTasks(ctx, knId, jobId) {
|
|
3070
|
-
return request(ctx, knPath(knId, `jobs/${encodeURIComponent(jobId)}/tasks`));
|
|
3071
|
-
}
|
|
3072
|
-
function deleteJobs(ctx, knId, ids) {
|
|
3073
|
-
return request(ctx, knPath(knId, `jobs/${ids}`), { method: "DELETE" });
|
|
3074
|
-
}
|
|
3075
3192
|
|
|
3076
3193
|
// src/api/vega.ts
|
|
3077
3194
|
import { z } from "zod";
|
|
@@ -3227,9 +3344,15 @@ function discoverCatalog(ctx, id, wait = true) {
|
|
|
3227
3344
|
timeoutMs: 12e4
|
|
3228
3345
|
});
|
|
3229
3346
|
}
|
|
3230
|
-
function listCatalogResources(ctx, id, category) {
|
|
3347
|
+
function listCatalogResources(ctx, id, category, limit, offset) {
|
|
3231
3348
|
return request(ctx, `${VEGA_BASE}/resources`, {
|
|
3232
|
-
query: {
|
|
3349
|
+
query: {
|
|
3350
|
+
catalog_id: id,
|
|
3351
|
+
category: category || void 0,
|
|
3352
|
+
// limit=-1 (NO_LIMIT) fetches all; NaN / 0 fall back to the backend default.
|
|
3353
|
+
limit: Number.isFinite(limit) && (limit > 0 || limit === -1) ? limit : void 0,
|
|
3354
|
+
offset: offset || void 0
|
|
3355
|
+
}
|
|
3233
3356
|
});
|
|
3234
3357
|
}
|
|
3235
3358
|
function catalogHealthStatus(ctx, ids) {
|
|
@@ -3871,7 +3994,6 @@ function kn(ctx) {
|
|
|
3871
3994
|
metricValidate: (knId, body) => validateMetric(ctx, knId, body),
|
|
3872
3995
|
objectTypes: (knId, opts) => listObjectTypes(ctx, knId, opts),
|
|
3873
3996
|
objectTypeQuery: (knId, otId, body) => queryObjectTypeInstances(ctx, knId, otId, body),
|
|
3874
|
-
objectTypeProperties: (knId, otId) => getObjectTypeProperties(ctx, knId, otId),
|
|
3875
3997
|
objectTypeGet: (knId, id) => getSchemaItem(ctx, knId, "object-types", id),
|
|
3876
3998
|
objectTypeCreate: (knId, body) => createSchemaItem(ctx, knId, "object-types", body),
|
|
3877
3999
|
objectTypeUpdate: (knId, id, body) => updateSchemaItem(ctx, knId, "object-types", id, body),
|
|
@@ -3899,10 +4021,6 @@ function kn(ctx) {
|
|
|
3899
4021
|
actionScheduleUpdate: (knId, scheduleId, body) => updateActionSchedule(ctx, knId, scheduleId, body),
|
|
3900
4022
|
actionScheduleSetStatus: (knId, scheduleId, body) => setActionScheduleStatus(ctx, knId, scheduleId, body),
|
|
3901
4023
|
actionScheduleDelete: (knId, ids) => deleteActionSchedules(ctx, knId, ids),
|
|
3902
|
-
jobs: (knId) => listJobs(ctx, knId),
|
|
3903
|
-
job: (knId, jobId) => getJob(ctx, knId, jobId),
|
|
3904
|
-
jobTasks: (knId, jobId) => getJobTasks(ctx, knId, jobId),
|
|
3905
|
-
jobDelete: (knId, ids) => deleteJobs(ctx, knId, ids),
|
|
3906
4024
|
relationTypePaths: (knId, body) => relationTypePaths(ctx, knId, body),
|
|
3907
4025
|
bknResources: () => listBknResources(ctx),
|
|
3908
4026
|
createFromCatalog: (opts) => createFromCatalog(ctx, opts),
|
|
@@ -4366,8 +4484,14 @@ function listToolboxes(ctx, opts = {}) {
|
|
|
4366
4484
|
query: { keyword: opts.keyword || void 0, limit: opts.limit, offset: opts.offset ?? 0 }
|
|
4367
4485
|
});
|
|
4368
4486
|
}
|
|
4369
|
-
function listTools2(ctx, boxId) {
|
|
4370
|
-
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}/tools/list
|
|
4487
|
+
function listTools2(ctx, boxId, opts = {}) {
|
|
4488
|
+
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}/tools/list`, {
|
|
4489
|
+
query: {
|
|
4490
|
+
page: opts.page,
|
|
4491
|
+
page_size: Number.isFinite(opts.pageSize) && opts.pageSize > 0 ? opts.pageSize : void 0,
|
|
4492
|
+
all: opts.all ? "true" : void 0
|
|
4493
|
+
}
|
|
4494
|
+
});
|
|
4371
4495
|
}
|
|
4372
4496
|
function createToolbox(ctx, opts) {
|
|
4373
4497
|
return request(ctx, PATH, {
|
|
@@ -4426,7 +4550,7 @@ function setToolStatuses(ctx, boxId, updates) {
|
|
|
4426
4550
|
function toolboxes(ctx) {
|
|
4427
4551
|
return {
|
|
4428
4552
|
list: (opts) => listToolboxes(ctx, opts),
|
|
4429
|
-
tools: (boxId) => listTools2(ctx, boxId),
|
|
4553
|
+
tools: (boxId, opts) => listTools2(ctx, boxId, opts),
|
|
4430
4554
|
create: (opts) => createToolbox(ctx, opts),
|
|
4431
4555
|
delete: (boxId) => deleteToolbox(ctx, boxId),
|
|
4432
4556
|
publish: (boxId) => setToolboxStatus(ctx, boxId, "published"),
|
|
@@ -4452,8 +4576,728 @@ function toolboxes(ctx) {
|
|
|
4452
4576
|
};
|
|
4453
4577
|
}
|
|
4454
4578
|
|
|
4579
|
+
// src/trace-session.ts
|
|
4580
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4581
|
+
var PAYLOAD_FIELDS = {
|
|
4582
|
+
"agent.interaction.started": /* @__PURE__ */ new Set([
|
|
4583
|
+
"intent_hash",
|
|
4584
|
+
"mode",
|
|
4585
|
+
"agent_id",
|
|
4586
|
+
"app_ref",
|
|
4587
|
+
"question_artifact_ref"
|
|
4588
|
+
]),
|
|
4589
|
+
"retrieval.completed": /* @__PURE__ */ new Set([
|
|
4590
|
+
"query_hash",
|
|
4591
|
+
"candidate_count",
|
|
4592
|
+
"truncated",
|
|
4593
|
+
"version_status",
|
|
4594
|
+
"source_refs"
|
|
4595
|
+
]),
|
|
4596
|
+
"knowledge.read.observed": /* @__PURE__ */ new Set([
|
|
4597
|
+
"kn_id",
|
|
4598
|
+
"read_kind",
|
|
4599
|
+
"version_status",
|
|
4600
|
+
"schema_version",
|
|
4601
|
+
"business_refs"
|
|
4602
|
+
]),
|
|
4603
|
+
"data.query.observed": /* @__PURE__ */ new Set([
|
|
4604
|
+
"query_hash",
|
|
4605
|
+
"query_type",
|
|
4606
|
+
"row_count",
|
|
4607
|
+
"truncated",
|
|
4608
|
+
"as_of",
|
|
4609
|
+
"version_status",
|
|
4610
|
+
"resource_refs",
|
|
4611
|
+
"field_refs",
|
|
4612
|
+
"query_artifact_ref",
|
|
4613
|
+
"result_artifact_ref"
|
|
4614
|
+
]),
|
|
4615
|
+
"logic.execution.observed": /* @__PURE__ */ new Set([
|
|
4616
|
+
"logic_ref",
|
|
4617
|
+
"input_artifact_ref",
|
|
4618
|
+
"result_artifact_ref",
|
|
4619
|
+
"status"
|
|
4620
|
+
]),
|
|
4621
|
+
"model.call.observed": /* @__PURE__ */ new Set([
|
|
4622
|
+
"model_name",
|
|
4623
|
+
"model_provider",
|
|
4624
|
+
"status",
|
|
4625
|
+
"input_token_count",
|
|
4626
|
+
"output_token_count",
|
|
4627
|
+
"prompt_hash",
|
|
4628
|
+
"output_hash",
|
|
4629
|
+
"error_category",
|
|
4630
|
+
"error_hash"
|
|
4631
|
+
]),
|
|
4632
|
+
"tool.called": /* @__PURE__ */ new Set(["tool_id", "tool_name", "args_hash", "visibility", "version_status"]),
|
|
4633
|
+
"tool.result.observed": /* @__PURE__ */ new Set([
|
|
4634
|
+
"tool_id",
|
|
4635
|
+
"tool_name",
|
|
4636
|
+
"status",
|
|
4637
|
+
"result_hash",
|
|
4638
|
+
"result_length",
|
|
4639
|
+
"result_count",
|
|
4640
|
+
"error_hash",
|
|
4641
|
+
"error_category",
|
|
4642
|
+
"visibility",
|
|
4643
|
+
"version_status"
|
|
4644
|
+
]),
|
|
4645
|
+
"claim.created": /* @__PURE__ */ new Set([
|
|
4646
|
+
"claim_id",
|
|
4647
|
+
"claim_type",
|
|
4648
|
+
"claim_hash",
|
|
4649
|
+
"source_event_ids",
|
|
4650
|
+
"operation_ids",
|
|
4651
|
+
"visibility",
|
|
4652
|
+
"version_status",
|
|
4653
|
+
"result_artifact_ref"
|
|
4654
|
+
]),
|
|
4655
|
+
"evidence.refs.created": /* @__PURE__ */ new Set(["claim_id", "evidence_refs"]),
|
|
4656
|
+
"business.refs.resolved": /* @__PURE__ */ new Set(["claim_id", "resolver_status", "business_refs"]),
|
|
4657
|
+
"action.recommended": /* @__PURE__ */ new Set([
|
|
4658
|
+
"action_instance_id",
|
|
4659
|
+
"action_type",
|
|
4660
|
+
"target_refs",
|
|
4661
|
+
"reason_hash",
|
|
4662
|
+
"status",
|
|
4663
|
+
"reason_artifact_ref",
|
|
4664
|
+
"input_artifact_ref"
|
|
4665
|
+
]),
|
|
4666
|
+
"action.approval_requested": /* @__PURE__ */ new Set(["action_instance_id", "policy_ref", "status"]),
|
|
4667
|
+
"action.approved": /* @__PURE__ */ new Set(["action_instance_id", "actor_ref", "policy_decision_ref", "status"]),
|
|
4668
|
+
"action.rejected": /* @__PURE__ */ new Set(["action_instance_id", "actor_ref", "policy_decision_ref", "status"]),
|
|
4669
|
+
"action.executed": /* @__PURE__ */ new Set([
|
|
4670
|
+
"action_instance_id",
|
|
4671
|
+
"status",
|
|
4672
|
+
"invocation_ref",
|
|
4673
|
+
"error_category",
|
|
4674
|
+
"error_hash"
|
|
4675
|
+
]),
|
|
4676
|
+
"action.result_recorded": /* @__PURE__ */ new Set([
|
|
4677
|
+
"action_instance_id",
|
|
4678
|
+
"status",
|
|
4679
|
+
"result_hash",
|
|
4680
|
+
"task_ref",
|
|
4681
|
+
"artifact_ref",
|
|
4682
|
+
"result_artifact_ref"
|
|
4683
|
+
])
|
|
4684
|
+
};
|
|
4685
|
+
var REQUIRED_PAYLOAD_FIELDS = {
|
|
4686
|
+
"agent.interaction.started": ["intent_hash", "mode"],
|
|
4687
|
+
"retrieval.completed": ["query_hash", "candidate_count", "truncated"],
|
|
4688
|
+
"knowledge.read.observed": ["kn_id", "read_kind", "version_status"],
|
|
4689
|
+
"data.query.observed": ["query_hash", "query_type", "row_count"],
|
|
4690
|
+
"logic.execution.observed": ["logic_ref", "input_artifact_ref", "result_artifact_ref", "status"],
|
|
4691
|
+
"model.call.observed": [
|
|
4692
|
+
"model_name",
|
|
4693
|
+
"model_provider",
|
|
4694
|
+
"status",
|
|
4695
|
+
"input_token_count",
|
|
4696
|
+
"output_token_count",
|
|
4697
|
+
"prompt_hash",
|
|
4698
|
+
"output_hash"
|
|
4699
|
+
],
|
|
4700
|
+
"tool.called": ["tool_id", "tool_name", "args_hash", "visibility", "version_status"],
|
|
4701
|
+
"tool.result.observed": ["tool_id", "tool_name", "status", "visibility", "version_status"],
|
|
4702
|
+
"claim.created": [
|
|
4703
|
+
"claim_id",
|
|
4704
|
+
"claim_type",
|
|
4705
|
+
"claim_hash",
|
|
4706
|
+
"source_event_ids",
|
|
4707
|
+
"operation_ids",
|
|
4708
|
+
"visibility",
|
|
4709
|
+
"version_status"
|
|
4710
|
+
],
|
|
4711
|
+
"evidence.refs.created": ["claim_id", "evidence_refs"],
|
|
4712
|
+
"business.refs.resolved": ["claim_id", "resolver_status", "business_refs"],
|
|
4713
|
+
"action.recommended": [
|
|
4714
|
+
"action_instance_id",
|
|
4715
|
+
"action_type",
|
|
4716
|
+
"target_refs",
|
|
4717
|
+
"reason_hash",
|
|
4718
|
+
"status"
|
|
4719
|
+
],
|
|
4720
|
+
"action.approval_requested": ["action_instance_id", "policy_ref", "status"],
|
|
4721
|
+
"action.approved": ["action_instance_id", "actor_ref", "policy_decision_ref", "status"],
|
|
4722
|
+
"action.rejected": ["action_instance_id", "actor_ref", "policy_decision_ref", "status"],
|
|
4723
|
+
"action.executed": ["action_instance_id", "status", "invocation_ref"],
|
|
4724
|
+
"action.result_recorded": ["action_instance_id", "status", "result_hash"]
|
|
4725
|
+
};
|
|
4726
|
+
var REF_FIELDS = /* @__PURE__ */ new Set([
|
|
4727
|
+
"ref_id",
|
|
4728
|
+
"ref_type",
|
|
4729
|
+
"source_system",
|
|
4730
|
+
"validity",
|
|
4731
|
+
"version_status",
|
|
4732
|
+
"visibility",
|
|
4733
|
+
"summary_hash"
|
|
4734
|
+
]);
|
|
4735
|
+
var RAW_KEYS = /* @__PURE__ */ new Set([
|
|
4736
|
+
"authorization",
|
|
4737
|
+
"cookie",
|
|
4738
|
+
"access_token",
|
|
4739
|
+
"refresh_token",
|
|
4740
|
+
"id_token",
|
|
4741
|
+
"api_key",
|
|
4742
|
+
"password",
|
|
4743
|
+
"private_key",
|
|
4744
|
+
"prompt",
|
|
4745
|
+
"user_question",
|
|
4746
|
+
"approval_comment",
|
|
4747
|
+
"sql",
|
|
4748
|
+
"query_params",
|
|
4749
|
+
"rows"
|
|
4750
|
+
]);
|
|
4751
|
+
var HASH_RE = /^sha256:[0-9a-f]{64}$/;
|
|
4752
|
+
var RAW_VALUE_PATTERNS = [
|
|
4753
|
+
/bearer\s+[A-Za-z0-9._-]+/i,
|
|
4754
|
+
/\bselect\s+.+\s+from\b/is,
|
|
4755
|
+
/\binsert\s+into\b/i,
|
|
4756
|
+
/\bupdate\s+\S+\s+set\b/i,
|
|
4757
|
+
/\bdelete\s+from\b/i,
|
|
4758
|
+
/[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}/,
|
|
4759
|
+
/https?:\/\/[^\s"']+/i
|
|
4760
|
+
];
|
|
4761
|
+
function defaultNow() {
|
|
4762
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4763
|
+
}
|
|
4764
|
+
function assertSessionOptions(options) {
|
|
4765
|
+
const trace2 = options.trace;
|
|
4766
|
+
if (!/^[0-9a-f]{32}$/.test(trace2.trace_id)) throw new Error("trace_id must be 32 hex characters");
|
|
4767
|
+
const traceparent = /^00-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}$/.exec(trace2.traceparent);
|
|
4768
|
+
if (!traceparent || traceparent[1] !== trace2.trace_id) {
|
|
4769
|
+
throw new Error("traceparent must be valid and match trace_id");
|
|
4770
|
+
}
|
|
4771
|
+
if (!/^req_[0-9A-Za-z_.-]+$/.test(trace2["bkn.request.id"])) {
|
|
4772
|
+
throw new Error("bkn.request.id must start with req_");
|
|
4773
|
+
}
|
|
4774
|
+
const conversationId = options.conversationId ?? trace2["bkn.conversation.id"];
|
|
4775
|
+
if (conversationId && !/^[0-9A-Za-z_.:-]{1,128}$/.test(conversationId)) {
|
|
4776
|
+
throw new Error("conversationId must be an opaque correlation identifier");
|
|
4777
|
+
}
|
|
4778
|
+
if (options.conversationId && trace2["bkn.conversation.id"] && options.conversationId !== trace2["bkn.conversation.id"]) {
|
|
4779
|
+
throw new Error("conversationId conflicts with trace bkn.conversation.id");
|
|
4780
|
+
}
|
|
4781
|
+
if (!trace2["bkn.tenant.id"] && !trace2.business_domain) {
|
|
4782
|
+
throw new Error("trace requires bkn.tenant.id or business_domain");
|
|
4783
|
+
}
|
|
4784
|
+
if (!trace2["bkn.account.id"] || !trace2["bkn.account.type"]) {
|
|
4785
|
+
throw new Error("trace requires account id and type");
|
|
4786
|
+
}
|
|
4787
|
+
if (!/^[0-9a-f]{16}$/.test(options.spanId)) throw new Error("spanId must be 16 hex characters");
|
|
4788
|
+
if (!options.producerModule.trim()) throw new Error("producerModule is required");
|
|
4789
|
+
}
|
|
4790
|
+
function clone(value) {
|
|
4791
|
+
return JSON.parse(JSON.stringify(value));
|
|
4792
|
+
}
|
|
4793
|
+
function assertSafePayload(eventType, payload) {
|
|
4794
|
+
const allowed = PAYLOAD_FIELDS[eventType];
|
|
4795
|
+
for (const key of Object.keys(payload)) {
|
|
4796
|
+
if (!allowed.has(key)) throw new Error(`${eventType} payload field is not registered: ${key}`);
|
|
4797
|
+
}
|
|
4798
|
+
for (const key of REQUIRED_PAYLOAD_FIELDS[eventType] ?? []) {
|
|
4799
|
+
if (payload[key] === void 0 || payload[key] === "") {
|
|
4800
|
+
throw new Error(`${eventType} payload requires ${key}`);
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
if (eventType === "agent.interaction.started" && !payload.agent_id && !payload.app_ref) {
|
|
4804
|
+
throw new Error("agent.interaction.started requires agent_id or app_ref");
|
|
4805
|
+
}
|
|
4806
|
+
if (eventType === "agent.interaction.started") {
|
|
4807
|
+
assertEnum(payload, "mode", ["chat", "task", "background"]);
|
|
4808
|
+
}
|
|
4809
|
+
if (eventType === "model.call.observed" || eventType === "action.executed") {
|
|
4810
|
+
assertEnum(payload, "status", ["ok", "error"]);
|
|
4811
|
+
}
|
|
4812
|
+
if (eventType === "model.call.observed" && payload.status === "error") {
|
|
4813
|
+
for (const key of ["error_category", "error_hash"]) {
|
|
4814
|
+
if (!payload[key]) throw new Error(`model.call.observed error requires ${key}`);
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
if (eventType === "tool.result.observed") {
|
|
4818
|
+
assertEnum(payload, "status", ["success", "error"]);
|
|
4819
|
+
if (payload.status === "success" && !payload.result_hash) {
|
|
4820
|
+
throw new Error("tool.result.observed success requires result_hash");
|
|
4821
|
+
}
|
|
4822
|
+
if (payload.status === "error" && !payload.error_hash) {
|
|
4823
|
+
throw new Error("tool.result.observed error requires error_hash");
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
if (eventType === "business.refs.resolved") {
|
|
4827
|
+
assertEnum(payload, "resolver_status", ["resolved", "partial", "unresolved"]);
|
|
4828
|
+
}
|
|
4829
|
+
for (const key of ["source_event_ids", "operation_ids", "target_refs"]) {
|
|
4830
|
+
if (key in payload && (!Array.isArray(payload[key]) || payload[key].length === 0)) {
|
|
4831
|
+
throw new Error(`${eventType} payload requires non-empty ${key}`);
|
|
4832
|
+
}
|
|
4833
|
+
}
|
|
4834
|
+
if (eventType === "action.recommended") {
|
|
4835
|
+
for (const ref of payload.target_refs) assertQualifiedReference(ref);
|
|
4836
|
+
}
|
|
4837
|
+
if (eventType === "evidence.refs.created") {
|
|
4838
|
+
assertRefs(payload.evidence_refs, false);
|
|
4839
|
+
}
|
|
4840
|
+
if (eventType === "business.refs.resolved") {
|
|
4841
|
+
const unresolved = payload.resolver_status === "unresolved";
|
|
4842
|
+
assertRefs(payload.business_refs, unresolved);
|
|
4843
|
+
}
|
|
4844
|
+
if (eventType === "action.result_recorded" && !payload.task_ref && !payload.artifact_ref && !payload.result_artifact_ref) {
|
|
4845
|
+
throw new Error(
|
|
4846
|
+
"action.result_recorded requires task_ref, artifact_ref, or result_artifact_ref"
|
|
4847
|
+
);
|
|
4848
|
+
}
|
|
4849
|
+
if (eventType === "action.executed" && payload.status === "error") {
|
|
4850
|
+
for (const key of ["error_category", "error_hash"]) {
|
|
4851
|
+
if (!payload[key]) throw new Error(`action.executed error requires ${key}`);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
const fixedStatus = {
|
|
4855
|
+
"action.recommended": "recommended",
|
|
4856
|
+
"action.approval_requested": "approval_requested",
|
|
4857
|
+
"action.approved": "approved",
|
|
4858
|
+
"action.rejected": "rejected"
|
|
4859
|
+
};
|
|
4860
|
+
if (fixedStatus[eventType] && payload.status !== fixedStatus[eventType]) {
|
|
4861
|
+
throw new Error(`${eventType} requires status=${fixedStatus[eventType]}`);
|
|
4862
|
+
}
|
|
4863
|
+
scanSafeValue(payload, "payload");
|
|
4864
|
+
}
|
|
4865
|
+
function assertRefs(value, allowEmpty) {
|
|
4866
|
+
if (!Array.isArray(value) || !allowEmpty && value.length === 0) {
|
|
4867
|
+
throw new Error("reference list must be a non-empty array");
|
|
4868
|
+
}
|
|
4869
|
+
for (const item of value) {
|
|
4870
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
4871
|
+
throw new Error("reference must be an object");
|
|
4872
|
+
}
|
|
4873
|
+
const ref = item;
|
|
4874
|
+
for (const key of Object.keys(ref)) {
|
|
4875
|
+
if (!REF_FIELDS.has(key)) throw new Error(`reference field is not registered: ${key}`);
|
|
4876
|
+
}
|
|
4877
|
+
for (const key of [
|
|
4878
|
+
"ref_id",
|
|
4879
|
+
"ref_type",
|
|
4880
|
+
"source_system",
|
|
4881
|
+
"validity",
|
|
4882
|
+
"version_status",
|
|
4883
|
+
"visibility"
|
|
4884
|
+
]) {
|
|
4885
|
+
if (!ref[key]) throw new Error(`reference requires ${key}`);
|
|
4886
|
+
}
|
|
4887
|
+
assertQualifiedReference(String(ref.ref_id));
|
|
4888
|
+
assertEnum(ref, "validity", ["observed", "available", "unavailable", "expired", "partial"]);
|
|
4889
|
+
assertEnum(ref, "version_status", ["versioned", "unversioned", "not_auditable"]);
|
|
4890
|
+
assertEnum(ref, "visibility", [
|
|
4891
|
+
"visible",
|
|
4892
|
+
"redacted",
|
|
4893
|
+
"hidden",
|
|
4894
|
+
"omitted",
|
|
4895
|
+
"unresolved",
|
|
4896
|
+
"unauthorized"
|
|
4897
|
+
]);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
function assertQualifiedReference(value) {
|
|
4901
|
+
const parts = value.trim().split(":");
|
|
4902
|
+
const namespace = parts[0] ?? "";
|
|
4903
|
+
let valid = parts.every((part) => part.length > 0);
|
|
4904
|
+
if (["kn", "resource"].includes(namespace)) valid = valid && parts.length === 2;
|
|
4905
|
+
if (["object", "relation", "action_type", "metric", "field"].includes(namespace)) {
|
|
4906
|
+
valid = valid && parts.length === 3;
|
|
4907
|
+
}
|
|
4908
|
+
if (namespace === "property") valid = valid && parts.length === 4;
|
|
4909
|
+
if (!valid) {
|
|
4910
|
+
throw new Error("business reference id must include its knowledge-network or resource scope");
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
function assertEnum(value, key, allowed) {
|
|
4914
|
+
if (!allowed.includes(String(value[key]))) {
|
|
4915
|
+
throw new Error(`${key} must be one of ${allowed.join(", ")}`);
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
function scanSafeValue(value, path) {
|
|
4919
|
+
if (Array.isArray(value)) {
|
|
4920
|
+
value.forEach((child, index) => scanSafeValue(child, `${path}[${index}]`));
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
4923
|
+
if (value && typeof value === "object") {
|
|
4924
|
+
for (const [key, child] of Object.entries(value)) {
|
|
4925
|
+
if (RAW_KEYS.has(key.toLowerCase())) throw new Error(`raw sensitive payload field: ${key}`);
|
|
4926
|
+
if (key.endsWith("_hash") && child !== "" && !HASH_RE.test(String(child))) {
|
|
4927
|
+
throw new Error(`${path}.${key} must be a sha256 hash`);
|
|
4928
|
+
}
|
|
4929
|
+
scanSafeValue(child, `${path}.${key}`);
|
|
4930
|
+
}
|
|
4931
|
+
return;
|
|
4932
|
+
}
|
|
4933
|
+
if (typeof value === "string" && RAW_VALUE_PATTERNS.some((pattern) => pattern.test(value))) {
|
|
4934
|
+
throw new Error(`raw sensitive payload value at ${path}`);
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
var TraceSession = class {
|
|
4938
|
+
interactionId;
|
|
4939
|
+
trace;
|
|
4940
|
+
producerModule;
|
|
4941
|
+
spanId;
|
|
4942
|
+
emit;
|
|
4943
|
+
contractVersion;
|
|
4944
|
+
idFactory;
|
|
4945
|
+
now;
|
|
4946
|
+
events = [];
|
|
4947
|
+
eventIDs = /* @__PURE__ */ new Set();
|
|
4948
|
+
operationIDs = /* @__PURE__ */ new Set();
|
|
4949
|
+
claimEventIDs = /* @__PURE__ */ new Map();
|
|
4950
|
+
actions = /* @__PURE__ */ new WeakMap();
|
|
4951
|
+
flushTail = Promise.resolve();
|
|
4952
|
+
constructor(options) {
|
|
4953
|
+
assertSessionOptions(options);
|
|
4954
|
+
this.trace = clone(options.trace);
|
|
4955
|
+
if (options.conversationId) {
|
|
4956
|
+
this.trace["bkn.conversation.id"] = options.conversationId;
|
|
4957
|
+
}
|
|
4958
|
+
this.producerModule = options.producerModule;
|
|
4959
|
+
this.spanId = options.spanId;
|
|
4960
|
+
this.emit = options.emit;
|
|
4961
|
+
this.contractVersion = options.contractVersion ?? "2.1.0";
|
|
4962
|
+
this.idFactory = options.idFactory ?? randomUUID2;
|
|
4963
|
+
this.now = options.now ?? defaultNow;
|
|
4964
|
+
this.interactionId = options.interactionId ?? this.idFactory();
|
|
4965
|
+
}
|
|
4966
|
+
startInteraction(input) {
|
|
4967
|
+
return this.append("agent.interaction.started", {
|
|
4968
|
+
operationName: input.operationName,
|
|
4969
|
+
payload: {
|
|
4970
|
+
intent_hash: input.intentHash,
|
|
4971
|
+
mode: input.mode,
|
|
4972
|
+
...input.agentId ? { agent_id: input.agentId } : {},
|
|
4973
|
+
...input.appRef ? { app_ref: input.appRef } : {},
|
|
4974
|
+
...input.questionArtifactRef ? { question_artifact_ref: input.questionArtifactRef } : {}
|
|
4975
|
+
}
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
observeOperation(eventType, input) {
|
|
4979
|
+
const operationId = input.operationId ?? this.idFactory();
|
|
4980
|
+
this.operationIDs.add(operationId);
|
|
4981
|
+
return this.append(eventType, { ...input, operationId });
|
|
4982
|
+
}
|
|
4983
|
+
createClaim(input) {
|
|
4984
|
+
if (input.sourceEventIds.length === 0 || input.operationIds.length === 0) {
|
|
4985
|
+
throw new Error("claim requires at least one source event and operation");
|
|
4986
|
+
}
|
|
4987
|
+
this.assertKnownRefs(input.sourceEventIds, this.eventIDs, "event");
|
|
4988
|
+
this.assertKnownRefs(input.operationIds, this.operationIDs, "operation");
|
|
4989
|
+
const event = this.append("claim.created", {
|
|
4990
|
+
operationName: input.operationName,
|
|
4991
|
+
causationEventId: input.causationEventId,
|
|
4992
|
+
claimId: input.claimId,
|
|
4993
|
+
payload: {
|
|
4994
|
+
claim_id: input.claimId,
|
|
4995
|
+
claim_type: input.claimType,
|
|
4996
|
+
claim_hash: input.claimHash,
|
|
4997
|
+
source_event_ids: input.sourceEventIds,
|
|
4998
|
+
operation_ids: input.operationIds,
|
|
4999
|
+
visibility: input.visibility ?? "visible",
|
|
5000
|
+
version_status: input.versionStatus ?? "unversioned",
|
|
5001
|
+
...input.resultArtifactRef ? { result_artifact_ref: input.resultArtifactRef } : {}
|
|
5002
|
+
}
|
|
5003
|
+
});
|
|
5004
|
+
this.claimEventIDs.set(input.claimId, event.event_id);
|
|
5005
|
+
return event;
|
|
5006
|
+
}
|
|
5007
|
+
createEvidenceRefs(input) {
|
|
5008
|
+
const claimEventID = this.requireClaim(input.claimId);
|
|
5009
|
+
if (input.refs.length === 0) throw new Error("evidence refs must not be empty");
|
|
5010
|
+
const operationId = this.idFactory();
|
|
5011
|
+
this.operationIDs.add(operationId);
|
|
5012
|
+
const event = this.append("evidence.refs.created", {
|
|
5013
|
+
operationName: input.operationName,
|
|
5014
|
+
operationId,
|
|
5015
|
+
causationEventId: input.causationEventId ?? claimEventID,
|
|
5016
|
+
claimId: input.claimId,
|
|
5017
|
+
payload: {
|
|
5018
|
+
claim_id: input.claimId,
|
|
5019
|
+
evidence_refs: input.refs.map((ref) => ({
|
|
5020
|
+
ref_id: ref.refId,
|
|
5021
|
+
ref_type: ref.refType,
|
|
5022
|
+
source_system: ref.sourceSystem,
|
|
5023
|
+
validity: ref.validity,
|
|
5024
|
+
version_status: ref.versionStatus,
|
|
5025
|
+
visibility: ref.visibility,
|
|
5026
|
+
...ref.summaryHash ? { summary_hash: ref.summaryHash } : {}
|
|
5027
|
+
}))
|
|
5028
|
+
}
|
|
5029
|
+
});
|
|
5030
|
+
this.claimEventIDs.set(input.claimId, event.event_id);
|
|
5031
|
+
return event;
|
|
5032
|
+
}
|
|
5033
|
+
resolveBusinessRefs(input) {
|
|
5034
|
+
const claimEventID = this.requireClaim(input.claimId);
|
|
5035
|
+
if (input.resolverStatus === "resolved" && input.refs.length === 0) {
|
|
5036
|
+
throw new Error("resolved business refs must not be empty");
|
|
5037
|
+
}
|
|
5038
|
+
const operationId = this.idFactory();
|
|
5039
|
+
this.operationIDs.add(operationId);
|
|
5040
|
+
const event = this.append("business.refs.resolved", {
|
|
5041
|
+
operationName: input.operationName,
|
|
5042
|
+
operationId,
|
|
5043
|
+
causationEventId: input.causationEventId ?? claimEventID,
|
|
5044
|
+
claimId: input.claimId,
|
|
5045
|
+
payload: {
|
|
5046
|
+
claim_id: input.claimId,
|
|
5047
|
+
resolver_status: input.resolverStatus,
|
|
5048
|
+
business_refs: input.refs.map((ref) => ({
|
|
5049
|
+
ref_id: ref.refId,
|
|
5050
|
+
ref_type: ref.refType,
|
|
5051
|
+
source_system: ref.sourceSystem,
|
|
5052
|
+
validity: ref.validity,
|
|
5053
|
+
version_status: ref.versionStatus,
|
|
5054
|
+
visibility: ref.visibility
|
|
5055
|
+
}))
|
|
5056
|
+
}
|
|
5057
|
+
});
|
|
5058
|
+
this.claimEventIDs.set(input.claimId, event.event_id);
|
|
5059
|
+
return event;
|
|
5060
|
+
}
|
|
5061
|
+
recommendAction(input) {
|
|
5062
|
+
const claimEventID = this.requireClaim(input.claimId);
|
|
5063
|
+
if (input.targetRefs.length === 0) throw new Error("action target refs must not be empty");
|
|
5064
|
+
const operationId = this.idFactory();
|
|
5065
|
+
const actionInstanceId = this.idFactory();
|
|
5066
|
+
this.operationIDs.add(operationId);
|
|
5067
|
+
const event = this.append("action.recommended", {
|
|
5068
|
+
operationName: input.operationName,
|
|
5069
|
+
operationId,
|
|
5070
|
+
causationEventId: input.causationEventId ?? claimEventID,
|
|
5071
|
+
claimId: input.claimId,
|
|
5072
|
+
payload: {
|
|
5073
|
+
action_instance_id: actionInstanceId,
|
|
5074
|
+
action_type: input.actionType,
|
|
5075
|
+
target_refs: input.targetRefs,
|
|
5076
|
+
reason_hash: input.reasonHash,
|
|
5077
|
+
...input.reasonArtifactRef ? { reason_artifact_ref: input.reasonArtifactRef } : {},
|
|
5078
|
+
...input.inputArtifactRef ? { input_artifact_ref: input.inputArtifactRef } : {},
|
|
5079
|
+
status: "recommended"
|
|
5080
|
+
}
|
|
5081
|
+
});
|
|
5082
|
+
const internal = {
|
|
5083
|
+
actionInstanceId,
|
|
5084
|
+
claimId: input.claimId,
|
|
5085
|
+
operationId,
|
|
5086
|
+
lastEventId: event.event_id,
|
|
5087
|
+
state: "recommended"
|
|
5088
|
+
};
|
|
5089
|
+
const handle = Object.freeze({
|
|
5090
|
+
get actionInstanceId() {
|
|
5091
|
+
return internal.actionInstanceId;
|
|
5092
|
+
},
|
|
5093
|
+
get claimId() {
|
|
5094
|
+
return internal.claimId;
|
|
5095
|
+
},
|
|
5096
|
+
get operationId() {
|
|
5097
|
+
return internal.operationId;
|
|
5098
|
+
},
|
|
5099
|
+
get lastEventId() {
|
|
5100
|
+
return internal.lastEventId;
|
|
5101
|
+
},
|
|
5102
|
+
get state() {
|
|
5103
|
+
return internal.state;
|
|
5104
|
+
}
|
|
5105
|
+
});
|
|
5106
|
+
this.actions.set(handle, internal);
|
|
5107
|
+
return handle;
|
|
5108
|
+
}
|
|
5109
|
+
requestActionApproval(action, input) {
|
|
5110
|
+
const internal = this.expectActionState(action, "recommended");
|
|
5111
|
+
const event = this.appendAction(internal, "action.approval_requested", {
|
|
5112
|
+
action_instance_id: internal.actionInstanceId,
|
|
5113
|
+
policy_ref: input.policyRef,
|
|
5114
|
+
status: "approval_requested"
|
|
5115
|
+
});
|
|
5116
|
+
internal.state = "approval_requested";
|
|
5117
|
+
internal.lastEventId = event.event_id;
|
|
5118
|
+
return event;
|
|
5119
|
+
}
|
|
5120
|
+
approveAction(action, input) {
|
|
5121
|
+
const internal = this.expectActionState(action, "approval_requested");
|
|
5122
|
+
const event = this.appendAction(internal, "action.approved", {
|
|
5123
|
+
action_instance_id: internal.actionInstanceId,
|
|
5124
|
+
actor_ref: input.actorRef,
|
|
5125
|
+
policy_decision_ref: input.policyDecisionRef,
|
|
5126
|
+
status: "approved"
|
|
5127
|
+
});
|
|
5128
|
+
internal.state = "approved";
|
|
5129
|
+
internal.lastEventId = event.event_id;
|
|
5130
|
+
return event;
|
|
5131
|
+
}
|
|
5132
|
+
rejectAction(action, input) {
|
|
5133
|
+
const internal = this.expectActionState(action, "approval_requested");
|
|
5134
|
+
const event = this.appendAction(internal, "action.rejected", {
|
|
5135
|
+
action_instance_id: internal.actionInstanceId,
|
|
5136
|
+
actor_ref: input.actorRef,
|
|
5137
|
+
policy_decision_ref: input.policyDecisionRef,
|
|
5138
|
+
status: "rejected"
|
|
5139
|
+
});
|
|
5140
|
+
internal.state = "rejected";
|
|
5141
|
+
internal.lastEventId = event.event_id;
|
|
5142
|
+
return event;
|
|
5143
|
+
}
|
|
5144
|
+
executeAction(action, input) {
|
|
5145
|
+
const internal = this.expectActionState(
|
|
5146
|
+
action,
|
|
5147
|
+
"approved",
|
|
5148
|
+
"requires approval before execution"
|
|
5149
|
+
);
|
|
5150
|
+
const event = this.appendAction(internal, "action.executed", {
|
|
5151
|
+
action_instance_id: internal.actionInstanceId,
|
|
5152
|
+
status: input.status,
|
|
5153
|
+
invocation_ref: input.invocationRef,
|
|
5154
|
+
...input.status === "error" ? { error_category: input.errorCategory, error_hash: input.errorHash } : {}
|
|
5155
|
+
});
|
|
5156
|
+
internal.state = "executed";
|
|
5157
|
+
internal.lastEventId = event.event_id;
|
|
5158
|
+
return event;
|
|
5159
|
+
}
|
|
5160
|
+
recordActionResult(action, input) {
|
|
5161
|
+
const internal = this.expectActionState(action, "executed");
|
|
5162
|
+
const event = this.appendAction(internal, "action.result_recorded", {
|
|
5163
|
+
action_instance_id: internal.actionInstanceId,
|
|
5164
|
+
result_hash: input.resultHash,
|
|
5165
|
+
status: input.status,
|
|
5166
|
+
...input.taskRef ? { task_ref: input.taskRef } : {},
|
|
5167
|
+
...input.artifactRef ? { artifact_ref: input.artifactRef } : {},
|
|
5168
|
+
...input.resultArtifactRef ? { result_artifact_ref: input.resultArtifactRef } : {}
|
|
5169
|
+
});
|
|
5170
|
+
internal.state = "result_recorded";
|
|
5171
|
+
internal.lastEventId = event.event_id;
|
|
5172
|
+
return event;
|
|
5173
|
+
}
|
|
5174
|
+
pendingEvents() {
|
|
5175
|
+
return clone(this.events);
|
|
5176
|
+
}
|
|
5177
|
+
flush() {
|
|
5178
|
+
const requestedIDs = new Set(this.events.map((event) => event.event_id));
|
|
5179
|
+
const operation = this.flushTail.then(() => this.flushEvents(requestedIDs));
|
|
5180
|
+
this.flushTail = operation.then(
|
|
5181
|
+
() => void 0,
|
|
5182
|
+
() => void 0
|
|
5183
|
+
);
|
|
5184
|
+
return operation;
|
|
5185
|
+
}
|
|
5186
|
+
appendAction(action, eventType, payload) {
|
|
5187
|
+
return this.append(eventType, {
|
|
5188
|
+
operationName: eventType,
|
|
5189
|
+
operationId: action.operationId,
|
|
5190
|
+
causationEventId: action.lastEventId,
|
|
5191
|
+
claimId: action.claimId,
|
|
5192
|
+
payload
|
|
5193
|
+
});
|
|
5194
|
+
}
|
|
5195
|
+
append(eventType, input) {
|
|
5196
|
+
assertSafePayload(eventType, input.payload);
|
|
5197
|
+
this.assertContractPayload(eventType, input.payload);
|
|
5198
|
+
if (eventType !== "agent.interaction.started") {
|
|
5199
|
+
if (!input.causationEventId) throw new Error(`${eventType} requires causation_event_id`);
|
|
5200
|
+
if (!this.eventIDs.has(input.causationEventId)) {
|
|
5201
|
+
throw new Error(`unknown event reference: ${input.causationEventId}`);
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
if (eventType !== "agent.interaction.started" && eventType !== "claim.created" && !input.operationId) {
|
|
5205
|
+
throw new Error(`${eventType} requires operation_id`);
|
|
5206
|
+
}
|
|
5207
|
+
const eventID = this.idFactory();
|
|
5208
|
+
if (this.eventIDs.has(eventID)) throw new Error(`duplicate event id: ${eventID}`);
|
|
5209
|
+
const timestamp = this.now();
|
|
5210
|
+
const event = {
|
|
5211
|
+
event_id: eventID,
|
|
5212
|
+
event_type: eventType,
|
|
5213
|
+
"bkn.trace.schema.version": this.contractVersion,
|
|
5214
|
+
observed_at: timestamp,
|
|
5215
|
+
emitted_at: timestamp,
|
|
5216
|
+
producer_module: this.producerModule,
|
|
5217
|
+
trace_id: this.trace.trace_id,
|
|
5218
|
+
span_id: this.spanId,
|
|
5219
|
+
"bkn.request.id": this.trace["bkn.request.id"],
|
|
5220
|
+
"bkn.operation.name": input.operationName,
|
|
5221
|
+
interaction_id: this.interactionId,
|
|
5222
|
+
...input.operationId ? { operation_id: input.operationId } : {},
|
|
5223
|
+
...input.causationEventId ? { causation_event_id: input.causationEventId } : {},
|
|
5224
|
+
...input.claimId ? { claim_id: input.claimId } : {},
|
|
5225
|
+
...input.attempt ? { attempt: input.attempt } : {},
|
|
5226
|
+
payload: clone(input.payload)
|
|
5227
|
+
};
|
|
5228
|
+
this.events.push(clone(event));
|
|
5229
|
+
this.eventIDs.add(eventID);
|
|
5230
|
+
return clone(event);
|
|
5231
|
+
}
|
|
5232
|
+
async flushEvents(requestedIDs) {
|
|
5233
|
+
const batch = this.events.filter((event) => requestedIDs.has(event.event_id));
|
|
5234
|
+
if (batch.length === 0) return void 0;
|
|
5235
|
+
const response = await this.emit({
|
|
5236
|
+
"bkn.trace.schema.version": this.contractVersion,
|
|
5237
|
+
trace: clone(this.trace),
|
|
5238
|
+
events: clone(batch)
|
|
5239
|
+
});
|
|
5240
|
+
const remaining = this.events.filter((event) => !requestedIDs.has(event.event_id));
|
|
5241
|
+
this.events.splice(0, this.events.length, ...remaining);
|
|
5242
|
+
return response;
|
|
5243
|
+
}
|
|
5244
|
+
assertContractPayload(eventType, payload) {
|
|
5245
|
+
if (this.contractVersion !== "2.2.0") return;
|
|
5246
|
+
const requiredArtifactFields = {
|
|
5247
|
+
"agent.interaction.started": ["question_artifact_ref"],
|
|
5248
|
+
"data.query.observed": ["query_artifact_ref", "result_artifact_ref"],
|
|
5249
|
+
"logic.execution.observed": ["input_artifact_ref", "result_artifact_ref"],
|
|
5250
|
+
"claim.created": ["result_artifact_ref"],
|
|
5251
|
+
"action.recommended": ["input_artifact_ref"],
|
|
5252
|
+
"action.result_recorded": ["result_artifact_ref"]
|
|
5253
|
+
};
|
|
5254
|
+
for (const field of requiredArtifactFields[eventType] ?? []) {
|
|
5255
|
+
const value = payload[field];
|
|
5256
|
+
if (typeof value !== "string" || !/^artifact:[0-9A-Za-z][0-9A-Za-z_.:-]{0,127}$/.test(value)) {
|
|
5257
|
+
throw new Error(`${eventType} payload requires valid ${field}`);
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
for (const [field, value] of Object.entries(payload)) {
|
|
5261
|
+
if (field.endsWith("_artifact_ref") && value !== void 0) {
|
|
5262
|
+
if (typeof value !== "string" || !/^artifact:[0-9A-Za-z][0-9A-Za-z_.:-]{0,127}$/.test(value)) {
|
|
5263
|
+
throw new Error(`${eventType} payload requires valid ${field}`);
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
if (eventType === "action.result_recorded" && payload.artifact_ref !== void 0) {
|
|
5268
|
+
throw new Error("action.result_recorded 2.2 does not accept legacy artifact_ref");
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
assertKnownRefs(values, known, kind) {
|
|
5272
|
+
for (const value of values) {
|
|
5273
|
+
if (!known.has(value)) throw new Error(`unknown ${kind} reference: ${value}`);
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
requireClaim(claimID) {
|
|
5277
|
+
const eventID = this.claimEventIDs.get(claimID);
|
|
5278
|
+
if (!eventID) throw new Error(`unknown claim reference: ${claimID}`);
|
|
5279
|
+
return eventID;
|
|
5280
|
+
}
|
|
5281
|
+
expectActionState(action, expected, message) {
|
|
5282
|
+
const internal = this.actions.get(action);
|
|
5283
|
+
if (!internal) throw new Error("action handle does not belong to this trace session");
|
|
5284
|
+
if (internal.state !== expected) {
|
|
5285
|
+
if (message) throw new Error(`action ${internal.actionInstanceId} ${message}`);
|
|
5286
|
+
throw new Error(
|
|
5287
|
+
`action ${internal.actionInstanceId} must be ${expected}, got ${internal.state}`
|
|
5288
|
+
);
|
|
5289
|
+
}
|
|
5290
|
+
return internal;
|
|
5291
|
+
}
|
|
5292
|
+
};
|
|
5293
|
+
|
|
4455
5294
|
// src/api/trace.ts
|
|
4456
5295
|
var SEARCH = "/api/agent-observability/v1/traces/_search";
|
|
5296
|
+
var EVIDENCE_EVENTS = "/api/agent-observability/v1/evidence/events";
|
|
5297
|
+
var EVIDENCE_ARTIFACTS = "/api/agent-observability/v1/evidence/artifacts";
|
|
5298
|
+
var REQUESTS = "/api/agent-observability/v1/requests";
|
|
5299
|
+
var INTERACTIONS = "/api/agent-observability/v1/interactions";
|
|
5300
|
+
var TRACES = "/api/agent-observability/v1/traces";
|
|
4457
5301
|
function isoToNanos(iso) {
|
|
4458
5302
|
const ms = Date.parse(iso);
|
|
4459
5303
|
if (Number.isNaN(ms)) return void 0;
|
|
@@ -4495,6 +5339,67 @@ async function getRawSpansByConversation(ctx, conversationId, opts = {}) {
|
|
|
4495
5339
|
function traceSearch(ctx, body) {
|
|
4496
5340
|
return request(ctx, SEARCH, { method: "POST", body });
|
|
4497
5341
|
}
|
|
5342
|
+
function emitEvidenceEvents(ctx, body) {
|
|
5343
|
+
return request(ctx, EVIDENCE_EVENTS, {
|
|
5344
|
+
method: "POST",
|
|
5345
|
+
body,
|
|
5346
|
+
headers: evidenceWriteHeaders(ctx),
|
|
5347
|
+
redirect: "manual"
|
|
5348
|
+
});
|
|
5349
|
+
}
|
|
5350
|
+
function emitEvidenceArtifact(ctx, body) {
|
|
5351
|
+
return request(ctx, EVIDENCE_ARTIFACTS, {
|
|
5352
|
+
method: "POST",
|
|
5353
|
+
body,
|
|
5354
|
+
headers: evidenceWriteHeaders(ctx),
|
|
5355
|
+
redirect: "manual"
|
|
5356
|
+
});
|
|
5357
|
+
}
|
|
5358
|
+
function evidenceWriteHeaders(ctx) {
|
|
5359
|
+
return ctx.evidenceIngestToken ? { "x-bkn-trace-ingest-token": ctx.evidenceIngestToken } : void 0;
|
|
5360
|
+
}
|
|
5361
|
+
function getEvidenceArtifact(ctx, artifactId) {
|
|
5362
|
+
return request(ctx, `${EVIDENCE_ARTIFACTS}/${encodeURIComponent(artifactId)}`);
|
|
5363
|
+
}
|
|
5364
|
+
function listRequestSummaries(ctx, query = {}) {
|
|
5365
|
+
return request(ctx, REQUESTS, {
|
|
5366
|
+
query: summaryQuery(query)
|
|
5367
|
+
});
|
|
5368
|
+
}
|
|
5369
|
+
function getRequestSummary(ctx, requestId) {
|
|
5370
|
+
return request(ctx, `${REQUESTS}/${encodeURIComponent(requestId)}`);
|
|
5371
|
+
}
|
|
5372
|
+
function getInteractionSummary(ctx, interactionId) {
|
|
5373
|
+
return request(ctx, `${INTERACTIONS}/${encodeURIComponent(interactionId)}`);
|
|
5374
|
+
}
|
|
5375
|
+
function getRequestTraces(ctx, requestId, query = {}) {
|
|
5376
|
+
return request(
|
|
5377
|
+
ctx,
|
|
5378
|
+
`${REQUESTS}/${encodeURIComponent(requestId)}/traces`,
|
|
5379
|
+
{ query: summaryQuery(query) }
|
|
5380
|
+
);
|
|
5381
|
+
}
|
|
5382
|
+
function getTraceGraph(ctx, traceId) {
|
|
5383
|
+
return request(ctx, `${TRACES}/${encodeURIComponent(traceId)}/trace-graph`);
|
|
5384
|
+
}
|
|
5385
|
+
function getEvidenceChain(ctx, scope, opts = {}) {
|
|
5386
|
+
const target = traceTarget(scope, "evidence-chain");
|
|
5387
|
+
return request(ctx, target.path, {
|
|
5388
|
+
query: queryWithLimit(target.query, opts)
|
|
5389
|
+
});
|
|
5390
|
+
}
|
|
5391
|
+
function getBusinessGraph(ctx, scope, opts = {}) {
|
|
5392
|
+
const target = traceTarget(scope, "business-graph");
|
|
5393
|
+
return request(ctx, target.path, {
|
|
5394
|
+
query: queryWithLimit(target.query, opts)
|
|
5395
|
+
});
|
|
5396
|
+
}
|
|
5397
|
+
function getSnapshotPreview(ctx, scope, opts = {}) {
|
|
5398
|
+
const target = traceTarget(scope, "snapshot-preview");
|
|
5399
|
+
return request(ctx, target.path, {
|
|
5400
|
+
query: queryWithLimit(target.query, opts)
|
|
5401
|
+
});
|
|
5402
|
+
}
|
|
4498
5403
|
async function getSpansByConversation(ctx, conversationId, opts = {}) {
|
|
4499
5404
|
const agg = await request(ctx, SEARCH, {
|
|
4500
5405
|
method: "POST",
|
|
@@ -4519,6 +5424,38 @@ async function getSpansByConversation(ctx, conversationId, opts = {}) {
|
|
|
4519
5424
|
}) ?? {};
|
|
4520
5425
|
return (spans.hits?.hits ?? []).map((h) => h._source ?? {});
|
|
4521
5426
|
}
|
|
5427
|
+
function traceTarget(scope, subresource) {
|
|
5428
|
+
if (typeof scope === "string") {
|
|
5429
|
+
return { path: `${TRACES}/${encodeURIComponent(scope)}/${subresource}` };
|
|
5430
|
+
}
|
|
5431
|
+
if ("traceId" in scope) {
|
|
5432
|
+
return { path: `${TRACES}/${encodeURIComponent(scope.traceId)}/${subresource}` };
|
|
5433
|
+
}
|
|
5434
|
+
const requestPath = subresource === "evidence-chain" ? `${TRACES}/by-request` : `${TRACES}/by-request/${subresource}`;
|
|
5435
|
+
return { path: requestPath, query: { request_id: scope.requestId } };
|
|
5436
|
+
}
|
|
5437
|
+
function queryWithLimit(query, opts) {
|
|
5438
|
+
if (opts.limit === void 0 || !Number.isFinite(opts.limit)) return query;
|
|
5439
|
+
return { ...query ?? {}, limit: opts.limit };
|
|
5440
|
+
}
|
|
5441
|
+
function summaryQuery(query) {
|
|
5442
|
+
const result = {};
|
|
5443
|
+
if (query.limit !== void 0 && Number.isFinite(query.limit)) result.limit = query.limit;
|
|
5444
|
+
if (query.cursor) result.cursor = query.cursor;
|
|
5445
|
+
if (query.from) result.from = query.from;
|
|
5446
|
+
if (query.to) result.to = query.to;
|
|
5447
|
+
if (query.status) result.status = query.status;
|
|
5448
|
+
if (query.agentOrApp) result.agent_or_app = query.agentOrApp;
|
|
5449
|
+
if (query.businessDomain) result.business_domain = query.businessDomain;
|
|
5450
|
+
if (query.conversationId) result.conversation_id = query.conversationId;
|
|
5451
|
+
if (query.interactionId) result.interaction_id = query.interactionId;
|
|
5452
|
+
if (query.knowledgeNetwork) result.knowledge_network = query.knowledgeNetwork;
|
|
5453
|
+
if (query.evidenceCompleteness) {
|
|
5454
|
+
result.evidence_completeness = query.evidenceCompleteness;
|
|
5455
|
+
}
|
|
5456
|
+
if (query.keyword) result.keyword = query.keyword;
|
|
5457
|
+
return Object.keys(result).length ? result : void 0;
|
|
5458
|
+
}
|
|
4522
5459
|
|
|
4523
5460
|
// src/bkn-trace/claude-judge.ts
|
|
4524
5461
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
@@ -4580,10 +5517,10 @@ async function judgeJson(prompt, opts = {}) {
|
|
|
4580
5517
|
child.stdout.on("data", (d) => {
|
|
4581
5518
|
out += d;
|
|
4582
5519
|
});
|
|
4583
|
-
child.on("error", (
|
|
5520
|
+
child.on("error", (err2) => {
|
|
4584
5521
|
clearTimeout(killer);
|
|
4585
5522
|
reject(
|
|
4586
|
-
|
|
5523
|
+
err2.code === "ENOENT" ? new ClaudeJudgeError("`claude` not found on PATH", "not_available") : err2
|
|
4587
5524
|
);
|
|
4588
5525
|
});
|
|
4589
5526
|
child.on("close", (code) => {
|
|
@@ -4593,8 +5530,8 @@ async function judgeJson(prompt, opts = {}) {
|
|
|
4593
5530
|
if (code !== 0) return reject(new ClaudeJudgeError(`claude exited ${code}`, "exit"));
|
|
4594
5531
|
resolve7(out);
|
|
4595
5532
|
});
|
|
4596
|
-
child.stdin.on("error", (
|
|
4597
|
-
if (
|
|
5533
|
+
child.stdin.on("error", (err2) => {
|
|
5534
|
+
if (err2.code !== "EPIPE") reject(err2);
|
|
4598
5535
|
});
|
|
4599
5536
|
child.stdin.end(prompt);
|
|
4600
5537
|
});
|
|
@@ -5144,6 +6081,747 @@ async function runEvalSet(agentId, cases, deps) {
|
|
|
5144
6081
|
};
|
|
5145
6082
|
}
|
|
5146
6083
|
|
|
6084
|
+
// src/bkn-trace/fixture-validate.ts
|
|
6085
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
6086
|
+
import { join as join4 } from "path";
|
|
6087
|
+
var CONTRACT_VERSIONS = /* @__PURE__ */ new Set(["1.0.0", "2.0.0", "2.1.0"]);
|
|
6088
|
+
var BUSINESS_CONTRACT_VERSION = "2.1.0";
|
|
6089
|
+
var TRACEPARENT_RE2 = /^00-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/;
|
|
6090
|
+
var REQUEST_ID_RE2 = /^req_[0-9A-Za-z_.-]+$/;
|
|
6091
|
+
var RFC3339_NANO_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/;
|
|
6092
|
+
var ALLOWED_BAGGAGE2 = /* @__PURE__ */ new Set(["bkn.account.type", "bkn.runtime.env"]);
|
|
6093
|
+
var REQUIRED_LOG_FIELDS = [
|
|
6094
|
+
"trace_id",
|
|
6095
|
+
"span_id",
|
|
6096
|
+
"bkn.request.id",
|
|
6097
|
+
"bkn.module.name",
|
|
6098
|
+
"bkn.operation.name",
|
|
6099
|
+
"bkn.status",
|
|
6100
|
+
"bkn.timestamp",
|
|
6101
|
+
"bkn.trace.schema.version"
|
|
6102
|
+
];
|
|
6103
|
+
var REQUIRED_SPAN_FIELDS = [
|
|
6104
|
+
"span_id",
|
|
6105
|
+
"name",
|
|
6106
|
+
"bkn.module.name",
|
|
6107
|
+
"bkn.operation.name",
|
|
6108
|
+
"bkn.status",
|
|
6109
|
+
"bkn.timestamp"
|
|
6110
|
+
];
|
|
6111
|
+
var REQUIRED_EVENT_FIELDS = [
|
|
6112
|
+
"trace_id",
|
|
6113
|
+
"span_id",
|
|
6114
|
+
"bkn.request.id",
|
|
6115
|
+
"bkn.operation.name",
|
|
6116
|
+
"event_id",
|
|
6117
|
+
"event_type",
|
|
6118
|
+
"bkn.trace.schema.version",
|
|
6119
|
+
"observed_at",
|
|
6120
|
+
"emitted_at",
|
|
6121
|
+
"producer_module",
|
|
6122
|
+
"payload"
|
|
6123
|
+
];
|
|
6124
|
+
var SENSITIVE_PATTERNS = [
|
|
6125
|
+
/authorization/i,
|
|
6126
|
+
/bearer\s+[A-Za-z0-9._-]+/i,
|
|
6127
|
+
/access[_-]?token/i,
|
|
6128
|
+
/api[_-]?key/i,
|
|
6129
|
+
/cookie/i,
|
|
6130
|
+
/\bselect\s+.+\s+from\b/is,
|
|
6131
|
+
/prompt\s*[:=]/i,
|
|
6132
|
+
/https?:\/\/[^\s"']+/i,
|
|
6133
|
+
/[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}/
|
|
6134
|
+
];
|
|
6135
|
+
var BUSINESS_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
6136
|
+
"agent.interaction.started",
|
|
6137
|
+
"retrieval.completed",
|
|
6138
|
+
"knowledge.read.observed",
|
|
6139
|
+
"data.query.observed",
|
|
6140
|
+
"model.call.observed",
|
|
6141
|
+
"tool.called",
|
|
6142
|
+
"tool.result.observed",
|
|
6143
|
+
"claim.created",
|
|
6144
|
+
"evidence.refs.created",
|
|
6145
|
+
"business.refs.resolved",
|
|
6146
|
+
"action.recommended",
|
|
6147
|
+
"action.approval_requested",
|
|
6148
|
+
"action.approved",
|
|
6149
|
+
"action.rejected",
|
|
6150
|
+
"action.executed",
|
|
6151
|
+
"action.result_recorded"
|
|
6152
|
+
]);
|
|
6153
|
+
var CLAIM_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
6154
|
+
"claim.created",
|
|
6155
|
+
"evidence.refs.created",
|
|
6156
|
+
"business.refs.resolved",
|
|
6157
|
+
"action.recommended",
|
|
6158
|
+
"action.approval_requested",
|
|
6159
|
+
"action.approved",
|
|
6160
|
+
"action.rejected",
|
|
6161
|
+
"action.executed",
|
|
6162
|
+
"action.result_recorded"
|
|
6163
|
+
]);
|
|
6164
|
+
var ACTION_TRANSITIONS = {
|
|
6165
|
+
recommended: /* @__PURE__ */ new Set(["approval_requested"]),
|
|
6166
|
+
approval_requested: /* @__PURE__ */ new Set(["approved", "rejected"]),
|
|
6167
|
+
approved: /* @__PURE__ */ new Set(["executed"]),
|
|
6168
|
+
executed: /* @__PURE__ */ new Set(["result_recorded"]),
|
|
6169
|
+
rejected: /* @__PURE__ */ new Set(),
|
|
6170
|
+
result_recorded: /* @__PURE__ */ new Set()
|
|
6171
|
+
};
|
|
6172
|
+
var ACTION_STATE_BY_EVENT = {
|
|
6173
|
+
"action.recommended": "recommended",
|
|
6174
|
+
"action.approval_requested": "approval_requested",
|
|
6175
|
+
"action.approved": "approved",
|
|
6176
|
+
"action.rejected": "rejected",
|
|
6177
|
+
"action.executed": "executed",
|
|
6178
|
+
"action.result_recorded": "result_recorded"
|
|
6179
|
+
};
|
|
6180
|
+
var EVENT_PAYLOAD_FIELDS = {
|
|
6181
|
+
"agent.interaction.started": /* @__PURE__ */ new Set(["intent_hash", "mode", "agent_id", "app_ref"]),
|
|
6182
|
+
"retrieval.completed": /* @__PURE__ */ new Set([
|
|
6183
|
+
"query_hash",
|
|
6184
|
+
"candidate_count",
|
|
6185
|
+
"truncated",
|
|
6186
|
+
"version_status",
|
|
6187
|
+
"source_refs"
|
|
6188
|
+
]),
|
|
6189
|
+
"knowledge.read.observed": /* @__PURE__ */ new Set([
|
|
6190
|
+
"kn_id",
|
|
6191
|
+
"read_kind",
|
|
6192
|
+
"version_status",
|
|
6193
|
+
"schema_version",
|
|
6194
|
+
"business_refs"
|
|
6195
|
+
]),
|
|
6196
|
+
"data.query.observed": /* @__PURE__ */ new Set([
|
|
6197
|
+
"query_hash",
|
|
6198
|
+
"query_type",
|
|
6199
|
+
"row_count",
|
|
6200
|
+
"truncated",
|
|
6201
|
+
"as_of",
|
|
6202
|
+
"version_status",
|
|
6203
|
+
"resource_refs",
|
|
6204
|
+
"field_refs"
|
|
6205
|
+
]),
|
|
6206
|
+
"model.call.observed": /* @__PURE__ */ new Set([
|
|
6207
|
+
"model_name",
|
|
6208
|
+
"model_provider",
|
|
6209
|
+
"status",
|
|
6210
|
+
"input_token_count",
|
|
6211
|
+
"output_token_count",
|
|
6212
|
+
"prompt_hash",
|
|
6213
|
+
"output_hash",
|
|
6214
|
+
"error_category",
|
|
6215
|
+
"error_hash"
|
|
6216
|
+
]),
|
|
6217
|
+
"tool.called": /* @__PURE__ */ new Set(["tool_id", "tool_name", "args_hash", "visibility", "version_status"]),
|
|
6218
|
+
"tool.result.observed": /* @__PURE__ */ new Set([
|
|
6219
|
+
"tool_id",
|
|
6220
|
+
"tool_name",
|
|
6221
|
+
"status",
|
|
6222
|
+
"result_hash",
|
|
6223
|
+
"result_length",
|
|
6224
|
+
"result_count",
|
|
6225
|
+
"error_hash",
|
|
6226
|
+
"error_category",
|
|
6227
|
+
"visibility",
|
|
6228
|
+
"version_status"
|
|
6229
|
+
]),
|
|
6230
|
+
"claim.created": /* @__PURE__ */ new Set([
|
|
6231
|
+
"claim_id",
|
|
6232
|
+
"claim_type",
|
|
6233
|
+
"claim_hash",
|
|
6234
|
+
"source_event_ids",
|
|
6235
|
+
"operation_ids",
|
|
6236
|
+
"visibility",
|
|
6237
|
+
"version_status"
|
|
6238
|
+
]),
|
|
6239
|
+
"evidence.refs.created": /* @__PURE__ */ new Set(["claim_id", "evidence_refs"]),
|
|
6240
|
+
"business.refs.resolved": /* @__PURE__ */ new Set(["claim_id", "resolver_status", "business_refs"]),
|
|
6241
|
+
"action.recommended": /* @__PURE__ */ new Set([
|
|
6242
|
+
"action_instance_id",
|
|
6243
|
+
"action_type",
|
|
6244
|
+
"target_refs",
|
|
6245
|
+
"reason_hash",
|
|
6246
|
+
"status"
|
|
6247
|
+
]),
|
|
6248
|
+
"action.approval_requested": /* @__PURE__ */ new Set(["action_instance_id", "policy_ref", "status"]),
|
|
6249
|
+
"action.approved": /* @__PURE__ */ new Set(["action_instance_id", "actor_ref", "policy_decision_ref", "status"]),
|
|
6250
|
+
"action.rejected": /* @__PURE__ */ new Set(["action_instance_id", "actor_ref", "policy_decision_ref", "status"]),
|
|
6251
|
+
"action.executed": /* @__PURE__ */ new Set([
|
|
6252
|
+
"action_instance_id",
|
|
6253
|
+
"invocation_ref",
|
|
6254
|
+
"tool_ref",
|
|
6255
|
+
"status",
|
|
6256
|
+
"error_category",
|
|
6257
|
+
"error_hash"
|
|
6258
|
+
]),
|
|
6259
|
+
"action.result_recorded": /* @__PURE__ */ new Set([
|
|
6260
|
+
"action_instance_id",
|
|
6261
|
+
"result_hash",
|
|
6262
|
+
"artifact_ref",
|
|
6263
|
+
"task_ref",
|
|
6264
|
+
"status"
|
|
6265
|
+
])
|
|
6266
|
+
};
|
|
6267
|
+
var REFERENCE_FIELDS = /* @__PURE__ */ new Set([
|
|
6268
|
+
"ref_id",
|
|
6269
|
+
"ref_type",
|
|
6270
|
+
"source_system",
|
|
6271
|
+
"validity",
|
|
6272
|
+
"version_status",
|
|
6273
|
+
"visibility",
|
|
6274
|
+
"summary_hash"
|
|
6275
|
+
]);
|
|
6276
|
+
var FORBIDDEN_RAW_KEYS = /* @__PURE__ */ new Set([
|
|
6277
|
+
"authorization",
|
|
6278
|
+
"cookie",
|
|
6279
|
+
"access_token",
|
|
6280
|
+
"refresh_token",
|
|
6281
|
+
"id_token",
|
|
6282
|
+
"api_key",
|
|
6283
|
+
"password",
|
|
6284
|
+
"private_key",
|
|
6285
|
+
"prompt",
|
|
6286
|
+
"user_question",
|
|
6287
|
+
"approval_comment",
|
|
6288
|
+
"sql",
|
|
6289
|
+
"query_params",
|
|
6290
|
+
"rows"
|
|
6291
|
+
]);
|
|
6292
|
+
function err(code, path, message) {
|
|
6293
|
+
return { code, path, message };
|
|
6294
|
+
}
|
|
6295
|
+
function asRecord(value) {
|
|
6296
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
6297
|
+
}
|
|
6298
|
+
function jsonFiles(path) {
|
|
6299
|
+
const stat = statSync4(path);
|
|
6300
|
+
if (stat.isFile()) return [path];
|
|
6301
|
+
return readdirSync5(path).filter((name) => name.endsWith(".json")).sort().map((name) => join4(path, name));
|
|
6302
|
+
}
|
|
6303
|
+
function validTraceparent(value) {
|
|
6304
|
+
if (typeof value !== "string") return false;
|
|
6305
|
+
const match = TRACEPARENT_RE2.exec(value);
|
|
6306
|
+
if (!match) return false;
|
|
6307
|
+
const [, traceId, spanId] = match;
|
|
6308
|
+
return traceId !== "0".repeat(32) && spanId !== "0".repeat(16);
|
|
6309
|
+
}
|
|
6310
|
+
function checkRequired(item, fields, basePath, errors) {
|
|
6311
|
+
for (const field of fields) {
|
|
6312
|
+
if (item[field] === void 0 || item[field] === "") {
|
|
6313
|
+
errors.push(
|
|
6314
|
+
err(
|
|
6315
|
+
"BKN_TRACE_REQUIRED_FIELD_MISSING",
|
|
6316
|
+
`${basePath}.${field}`,
|
|
6317
|
+
`missing required field ${field}`
|
|
6318
|
+
)
|
|
6319
|
+
);
|
|
6320
|
+
}
|
|
6321
|
+
}
|
|
6322
|
+
}
|
|
6323
|
+
function checkTimestamp(value, path, errors) {
|
|
6324
|
+
if (typeof value !== "string" || !RFC3339_NANO_RE.test(value)) {
|
|
6325
|
+
errors.push(err("BKN_TRACE_INVALID_TIMESTAMP", path, "timestamp must be UTC RFC3339Nano"));
|
|
6326
|
+
}
|
|
6327
|
+
}
|
|
6328
|
+
function checkSensitive(value, path, errors) {
|
|
6329
|
+
if (Array.isArray(value)) {
|
|
6330
|
+
value.forEach((child, index) => checkSensitive(child, `${path}[${index}]`, errors));
|
|
6331
|
+
return;
|
|
6332
|
+
}
|
|
6333
|
+
if (value && typeof value === "object") {
|
|
6334
|
+
for (const [key, child] of Object.entries(value)) {
|
|
6335
|
+
if (FORBIDDEN_RAW_KEYS.has(key.toLowerCase())) {
|
|
6336
|
+
errors.push(
|
|
6337
|
+
err(
|
|
6338
|
+
"BKN_TRACE_SENSITIVE_VALUE_LEAKED",
|
|
6339
|
+
`${path}.${key}`,
|
|
6340
|
+
"raw sensitive field is forbidden"
|
|
6341
|
+
)
|
|
6342
|
+
);
|
|
6343
|
+
}
|
|
6344
|
+
if (key.endsWith("_hash") && child !== "" && (typeof child !== "string" || !/^sha256:[0-9a-f]{64}$/.test(child))) {
|
|
6345
|
+
errors.push(
|
|
6346
|
+
err("BKN_TRACE_REQUIRED_FIELD_MISSING", `${path}.${key}`, `${key} must be a sha256 hash`)
|
|
6347
|
+
);
|
|
6348
|
+
}
|
|
6349
|
+
checkSensitive(child, `${path}.${key}`, errors);
|
|
6350
|
+
}
|
|
6351
|
+
return;
|
|
6352
|
+
}
|
|
6353
|
+
if (typeof value !== "string") return;
|
|
6354
|
+
if (SENSITIVE_PATTERNS.some((pattern) => pattern.test(value))) {
|
|
6355
|
+
errors.push(
|
|
6356
|
+
err(
|
|
6357
|
+
"BKN_TRACE_SENSITIVE_VALUE_LEAKED",
|
|
6358
|
+
path,
|
|
6359
|
+
"sensitive value must be redacted, hashed, or referenced"
|
|
6360
|
+
)
|
|
6361
|
+
);
|
|
6362
|
+
}
|
|
6363
|
+
}
|
|
6364
|
+
function validateFixture(data) {
|
|
6365
|
+
const root = asRecord(data);
|
|
6366
|
+
const errors = [];
|
|
6367
|
+
const fixtureId = typeof root.fixture_id === "string" ? root.fixture_id : "<unknown>";
|
|
6368
|
+
const contractVersion = typeof root["bkn.trace.schema.version"] === "string" ? root["bkn.trace.schema.version"] : null;
|
|
6369
|
+
if (!contractVersion) {
|
|
6370
|
+
errors.push(
|
|
6371
|
+
err(
|
|
6372
|
+
"BKN_TRACE_SCHEMA_VERSION_MISSING",
|
|
6373
|
+
"$.bkn.trace.schema.version",
|
|
6374
|
+
"missing contract version"
|
|
6375
|
+
)
|
|
6376
|
+
);
|
|
6377
|
+
} else if (!CONTRACT_VERSIONS.has(contractVersion)) {
|
|
6378
|
+
errors.push(
|
|
6379
|
+
err(
|
|
6380
|
+
"BKN_TRACE_SCHEMA_VERSION_UNSUPPORTED",
|
|
6381
|
+
"$.bkn.trace.schema.version",
|
|
6382
|
+
`unsupported contract version ${contractVersion}`
|
|
6383
|
+
)
|
|
6384
|
+
);
|
|
6385
|
+
}
|
|
6386
|
+
const trace2 = asRecord(root.trace);
|
|
6387
|
+
const traceId = trace2.trace_id;
|
|
6388
|
+
const requestId = trace2["bkn.request.id"];
|
|
6389
|
+
if (typeof traceId !== "string" || !/^[0-9a-f]{32}$/.test(traceId)) {
|
|
6390
|
+
errors.push(
|
|
6391
|
+
err("BKN_TRACE_REQUIRED_FIELD_MISSING", "$.trace.trace_id", "missing valid trace id")
|
|
6392
|
+
);
|
|
6393
|
+
}
|
|
6394
|
+
if (typeof requestId !== "string" || !REQUEST_ID_RE2.test(requestId)) {
|
|
6395
|
+
errors.push(
|
|
6396
|
+
err(
|
|
6397
|
+
"BKN_TRACE_REQUIRED_FIELD_MISSING",
|
|
6398
|
+
"$.trace.bkn.request.id",
|
|
6399
|
+
"missing valid bkn.request.id"
|
|
6400
|
+
)
|
|
6401
|
+
);
|
|
6402
|
+
}
|
|
6403
|
+
if (!validTraceparent(trace2.traceparent)) {
|
|
6404
|
+
errors.push(err("BKN_TRACE_INVALID_TRACEPARENT", "$.trace.traceparent", "invalid traceparent"));
|
|
6405
|
+
}
|
|
6406
|
+
const spans = Array.isArray(root.spans) ? root.spans : [];
|
|
6407
|
+
const spanIds = /* @__PURE__ */ new Set();
|
|
6408
|
+
spans.forEach((item, index) => {
|
|
6409
|
+
const span = asRecord(item);
|
|
6410
|
+
checkRequired(span, REQUIRED_SPAN_FIELDS, `$.spans[${index}]`, errors);
|
|
6411
|
+
checkTimestamp(span["bkn.timestamp"], `$.spans[${index}].bkn.timestamp`, errors);
|
|
6412
|
+
if (typeof span.span_id === "string") spanIds.add(span.span_id);
|
|
6413
|
+
const parent = span.parent_span_id;
|
|
6414
|
+
if (parent !== null && parent !== void 0 && !spanIds.has(String(parent))) {
|
|
6415
|
+
errors.push(
|
|
6416
|
+
err(
|
|
6417
|
+
"BKN_TRACE_ORPHAN_SPAN",
|
|
6418
|
+
`$.spans[${index}].parent_span_id`,
|
|
6419
|
+
"parent span must appear before child span or be represented as a link"
|
|
6420
|
+
)
|
|
6421
|
+
);
|
|
6422
|
+
}
|
|
6423
|
+
});
|
|
6424
|
+
if (spans.length === 0) {
|
|
6425
|
+
errors.push(err("BKN_TRACE_REQUIRED_FIELD_MISSING", "$.spans", "at least one span required"));
|
|
6426
|
+
}
|
|
6427
|
+
const logs = Array.isArray(root.logs) ? root.logs : [];
|
|
6428
|
+
logs.forEach((item, index) => {
|
|
6429
|
+
const log = asRecord(item);
|
|
6430
|
+
checkRequired(log, REQUIRED_LOG_FIELDS, `$.logs[${index}]`, errors);
|
|
6431
|
+
checkTimestamp(log["bkn.timestamp"], `$.logs[${index}].bkn.timestamp`, errors);
|
|
6432
|
+
if (log.trace_id !== traceId || log["bkn.request.id"] !== requestId) {
|
|
6433
|
+
errors.push(
|
|
6434
|
+
err("BKN_TRACE_JOIN_FAILED", `$.logs[${index}]`, "log cannot join trace/request")
|
|
6435
|
+
);
|
|
6436
|
+
}
|
|
6437
|
+
if (!spanIds.has(String(log.span_id))) {
|
|
6438
|
+
errors.push(
|
|
6439
|
+
err("BKN_TRACE_JOIN_FAILED", `$.logs[${index}].span_id`, "log span_id not found")
|
|
6440
|
+
);
|
|
6441
|
+
}
|
|
6442
|
+
});
|
|
6443
|
+
const events = Array.isArray(root.events) ? root.events : [];
|
|
6444
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
6445
|
+
const knownEventIds = /* @__PURE__ */ new Set();
|
|
6446
|
+
const knownOperationIds = /* @__PURE__ */ new Set();
|
|
6447
|
+
const knownClaimIds = /* @__PURE__ */ new Set();
|
|
6448
|
+
const actionStates = /* @__PURE__ */ new Map();
|
|
6449
|
+
events.forEach((item, index) => {
|
|
6450
|
+
const event = asRecord(item);
|
|
6451
|
+
const eventPath = `$.events[${index}]`;
|
|
6452
|
+
checkRequired(event, REQUIRED_EVENT_FIELDS, `$.events[${index}]`, errors);
|
|
6453
|
+
checkTimestamp(event.observed_at, `$.events[${index}].observed_at`, errors);
|
|
6454
|
+
checkTimestamp(event.emitted_at, `$.events[${index}].emitted_at`, errors);
|
|
6455
|
+
if (event.trace_id !== traceId || event["bkn.request.id"] !== requestId) {
|
|
6456
|
+
errors.push(
|
|
6457
|
+
err("BKN_TRACE_JOIN_FAILED", `$.events[${index}]`, "event cannot join trace/request")
|
|
6458
|
+
);
|
|
6459
|
+
}
|
|
6460
|
+
if (!spanIds.has(String(event.span_id))) {
|
|
6461
|
+
errors.push(
|
|
6462
|
+
err("BKN_TRACE_JOIN_FAILED", `$.events[${index}].span_id`, "event span_id not found")
|
|
6463
|
+
);
|
|
6464
|
+
}
|
|
6465
|
+
if (typeof event.event_id === "string") {
|
|
6466
|
+
if (eventIds.has(event.event_id)) {
|
|
6467
|
+
errors.push(
|
|
6468
|
+
err("BKN_TRACE_EVENT_ID_CONFLICT", `${eventPath}.event_id`, "duplicate event_id")
|
|
6469
|
+
);
|
|
6470
|
+
}
|
|
6471
|
+
eventIds.add(event.event_id);
|
|
6472
|
+
}
|
|
6473
|
+
if (contractVersion !== BUSINESS_CONTRACT_VERSION) return;
|
|
6474
|
+
if (event["bkn.trace.schema.version"] !== contractVersion) {
|
|
6475
|
+
errors.push(
|
|
6476
|
+
err(
|
|
6477
|
+
"BKN_TRACE_SCHEMA_VERSION_UNSUPPORTED",
|
|
6478
|
+
`${eventPath}.bkn.trace.schema.version`,
|
|
6479
|
+
"event contract version must match the fixture envelope"
|
|
6480
|
+
)
|
|
6481
|
+
);
|
|
6482
|
+
}
|
|
6483
|
+
validateBusinessEvent(
|
|
6484
|
+
event,
|
|
6485
|
+
eventPath,
|
|
6486
|
+
knownEventIds,
|
|
6487
|
+
knownOperationIds,
|
|
6488
|
+
knownClaimIds,
|
|
6489
|
+
actionStates,
|
|
6490
|
+
errors
|
|
6491
|
+
);
|
|
6492
|
+
if (typeof event.event_id === "string") knownEventIds.add(event.event_id);
|
|
6493
|
+
if (typeof event.operation_id === "string") knownOperationIds.add(event.operation_id);
|
|
6494
|
+
if (event.event_type === "claim.created" && typeof event.claim_id === "string") {
|
|
6495
|
+
knownClaimIds.add(event.claim_id);
|
|
6496
|
+
}
|
|
6497
|
+
});
|
|
6498
|
+
if (contractVersion !== "1.0.0" && events.length === 0) {
|
|
6499
|
+
errors.push(err("BKN_TRACE_REQUIRED_FIELD_MISSING", "$.events", "at least one event required"));
|
|
6500
|
+
}
|
|
6501
|
+
const baggage = asRecord(root.baggage);
|
|
6502
|
+
for (const key of Object.keys(baggage)) {
|
|
6503
|
+
if (!ALLOWED_BAGGAGE2.has(key)) {
|
|
6504
|
+
errors.push(
|
|
6505
|
+
err(
|
|
6506
|
+
"BKN_TRACE_BAGGAGE_FORBIDDEN_FIELD",
|
|
6507
|
+
`$.baggage.${key}`,
|
|
6508
|
+
`baggage field ${key} is forbidden`
|
|
6509
|
+
)
|
|
6510
|
+
);
|
|
6511
|
+
}
|
|
6512
|
+
}
|
|
6513
|
+
checkSensitive(root, "$", errors);
|
|
6514
|
+
const result = errors.length > 0 ? "fail" : "pass";
|
|
6515
|
+
const expectedResult = root.expected_result === "pass" || root.expected_result === "fail" ? root.expected_result : null;
|
|
6516
|
+
return {
|
|
6517
|
+
fixtureId,
|
|
6518
|
+
result,
|
|
6519
|
+
contractVersion,
|
|
6520
|
+
errors,
|
|
6521
|
+
warnings: [],
|
|
6522
|
+
expectedResult,
|
|
6523
|
+
expectationMatched: expectedResult === null ? result === "pass" : expectedResult === result
|
|
6524
|
+
};
|
|
6525
|
+
}
|
|
6526
|
+
function validateBusinessEvent(event, path, knownEventIds, knownOperationIds, knownClaimIds, actionStates, errors) {
|
|
6527
|
+
const eventType = typeof event.event_type === "string" ? event.event_type : "";
|
|
6528
|
+
if (!BUSINESS_EVENT_TYPES.has(eventType)) {
|
|
6529
|
+
errors.push(
|
|
6530
|
+
err(
|
|
6531
|
+
"BKN_TRACE_EVENT_TYPE_UNSUPPORTED",
|
|
6532
|
+
`${path}.event_type`,
|
|
6533
|
+
`unsupported event ${eventType}`
|
|
6534
|
+
)
|
|
6535
|
+
);
|
|
6536
|
+
return;
|
|
6537
|
+
}
|
|
6538
|
+
checkRequired(event, ["interaction_id"], path, errors);
|
|
6539
|
+
if (eventType !== "agent.interaction.started" && eventType !== "claim.created") {
|
|
6540
|
+
checkRequired(event, ["operation_id"], path, errors);
|
|
6541
|
+
}
|
|
6542
|
+
if (eventType !== "agent.interaction.started") {
|
|
6543
|
+
checkRequired(event, ["causation_event_id"], path, errors);
|
|
6544
|
+
if (typeof event.causation_event_id === "string" && !knownEventIds.has(event.causation_event_id)) {
|
|
6545
|
+
errors.push(
|
|
6546
|
+
err(
|
|
6547
|
+
"BKN_TRACE_CAUSATION_INVALID",
|
|
6548
|
+
`${path}.causation_event_id`,
|
|
6549
|
+
"causation_event_id must reference an earlier event"
|
|
6550
|
+
)
|
|
6551
|
+
);
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
if (CLAIM_EVENT_TYPES.has(eventType)) {
|
|
6555
|
+
checkRequired(event, ["claim_id"], path, errors);
|
|
6556
|
+
if (eventType !== "claim.created" && typeof event.claim_id === "string" && !knownClaimIds.has(event.claim_id)) {
|
|
6557
|
+
errors.push(
|
|
6558
|
+
err(
|
|
6559
|
+
"BKN_TRACE_UNKNOWN_CLAIM_ID",
|
|
6560
|
+
`${path}.claim_id`,
|
|
6561
|
+
"event must reference an earlier claim"
|
|
6562
|
+
)
|
|
6563
|
+
);
|
|
6564
|
+
}
|
|
6565
|
+
}
|
|
6566
|
+
const payload = asRecord(event.payload);
|
|
6567
|
+
checkAllowedKeys(
|
|
6568
|
+
payload,
|
|
6569
|
+
EVENT_PAYLOAD_FIELDS[eventType] ?? /* @__PURE__ */ new Set(),
|
|
6570
|
+
`${path}.payload`,
|
|
6571
|
+
errors
|
|
6572
|
+
);
|
|
6573
|
+
if (eventType === "agent.interaction.started") {
|
|
6574
|
+
checkRequired(payload, ["intent_hash", "mode"], `${path}.payload`, errors);
|
|
6575
|
+
checkOneOf(payload, ["agent_id", "app_ref"], `${path}.payload`, errors);
|
|
6576
|
+
}
|
|
6577
|
+
if (eventType === "retrieval.completed") {
|
|
6578
|
+
checkRequired(
|
|
6579
|
+
payload,
|
|
6580
|
+
["query_hash", "candidate_count", "truncated"],
|
|
6581
|
+
`${path}.payload`,
|
|
6582
|
+
errors
|
|
6583
|
+
);
|
|
6584
|
+
}
|
|
6585
|
+
if (eventType === "knowledge.read.observed") {
|
|
6586
|
+
checkRequired(payload, ["kn_id", "read_kind", "version_status"], `${path}.payload`, errors);
|
|
6587
|
+
}
|
|
6588
|
+
if (eventType === "data.query.observed") {
|
|
6589
|
+
checkRequired(payload, ["query_hash", "query_type", "row_count"], `${path}.payload`, errors);
|
|
6590
|
+
}
|
|
6591
|
+
if (eventType === "model.call.observed") {
|
|
6592
|
+
checkRequired(
|
|
6593
|
+
payload,
|
|
6594
|
+
[
|
|
6595
|
+
"model_name",
|
|
6596
|
+
"model_provider",
|
|
6597
|
+
"status",
|
|
6598
|
+
"input_token_count",
|
|
6599
|
+
"output_token_count",
|
|
6600
|
+
"prompt_hash",
|
|
6601
|
+
"output_hash"
|
|
6602
|
+
],
|
|
6603
|
+
`${path}.payload`,
|
|
6604
|
+
errors
|
|
6605
|
+
);
|
|
6606
|
+
if (payload.status === "error") {
|
|
6607
|
+
checkRequired(payload, ["error_category", "error_hash"], `${path}.payload`, errors);
|
|
6608
|
+
}
|
|
6609
|
+
}
|
|
6610
|
+
if (eventType === "claim.created") {
|
|
6611
|
+
checkRequired(
|
|
6612
|
+
payload,
|
|
6613
|
+
[
|
|
6614
|
+
"claim_id",
|
|
6615
|
+
"claim_type",
|
|
6616
|
+
"claim_hash",
|
|
6617
|
+
"source_event_ids",
|
|
6618
|
+
"operation_ids",
|
|
6619
|
+
"visibility",
|
|
6620
|
+
"version_status"
|
|
6621
|
+
],
|
|
6622
|
+
`${path}.payload`,
|
|
6623
|
+
errors
|
|
6624
|
+
);
|
|
6625
|
+
checkNonEmptyArray(payload, "source_event_ids", `${path}.payload`, errors);
|
|
6626
|
+
checkNonEmptyArray(payload, "operation_ids", `${path}.payload`, errors);
|
|
6627
|
+
checkKnownArray(payload, "source_event_ids", knownEventIds, `${path}.payload`, errors);
|
|
6628
|
+
checkKnownArray(payload, "operation_ids", knownOperationIds, `${path}.payload`, errors);
|
|
6629
|
+
}
|
|
6630
|
+
if (eventType === "evidence.refs.created") {
|
|
6631
|
+
checkReferenceList(payload, "evidence_refs", `${path}.payload`, errors);
|
|
6632
|
+
}
|
|
6633
|
+
if (eventType === "business.refs.resolved") {
|
|
6634
|
+
checkRequired(payload, ["resolver_status"], `${path}.payload`, errors);
|
|
6635
|
+
checkReferenceList(
|
|
6636
|
+
payload,
|
|
6637
|
+
"business_refs",
|
|
6638
|
+
`${path}.payload`,
|
|
6639
|
+
errors,
|
|
6640
|
+
payload.resolver_status === "unresolved"
|
|
6641
|
+
);
|
|
6642
|
+
}
|
|
6643
|
+
const actionState = ACTION_STATE_BY_EVENT[eventType];
|
|
6644
|
+
if (!actionState) return;
|
|
6645
|
+
checkRequired(payload, ["action_instance_id", "status"], `${path}.payload`, errors);
|
|
6646
|
+
const fixedStatus = {
|
|
6647
|
+
"action.recommended": "recommended",
|
|
6648
|
+
"action.approval_requested": "approval_requested",
|
|
6649
|
+
"action.approved": "approved",
|
|
6650
|
+
"action.rejected": "rejected"
|
|
6651
|
+
};
|
|
6652
|
+
if (fixedStatus[eventType] && payload.status !== fixedStatus[eventType]) {
|
|
6653
|
+
errors.push(
|
|
6654
|
+
err(
|
|
6655
|
+
"BKN_TRACE_ACTION_TRANSITION_INVALID",
|
|
6656
|
+
`${path}.payload.status`,
|
|
6657
|
+
`${eventType} requires status=${fixedStatus[eventType]}`
|
|
6658
|
+
)
|
|
6659
|
+
);
|
|
6660
|
+
}
|
|
6661
|
+
if (eventType === "action.recommended") {
|
|
6662
|
+
checkRequired(
|
|
6663
|
+
payload,
|
|
6664
|
+
["action_type", "target_refs", "reason_hash"],
|
|
6665
|
+
`${path}.payload`,
|
|
6666
|
+
errors
|
|
6667
|
+
);
|
|
6668
|
+
checkNonEmptyArray(payload, "target_refs", `${path}.payload`, errors);
|
|
6669
|
+
checkQualifiedStringRefs(payload, "target_refs", `${path}.payload`, errors);
|
|
6670
|
+
}
|
|
6671
|
+
if (eventType === "action.approval_requested") {
|
|
6672
|
+
checkRequired(payload, ["policy_ref"], `${path}.payload`, errors);
|
|
6673
|
+
}
|
|
6674
|
+
if (eventType === "action.approved" || eventType === "action.rejected") {
|
|
6675
|
+
checkRequired(payload, ["actor_ref", "policy_decision_ref"], `${path}.payload`, errors);
|
|
6676
|
+
}
|
|
6677
|
+
if (eventType === "action.executed") {
|
|
6678
|
+
checkOneOf(payload, ["invocation_ref", "tool_ref"], `${path}.payload`, errors);
|
|
6679
|
+
if (payload.status === "error") {
|
|
6680
|
+
checkRequired(payload, ["error_category", "error_hash"], `${path}.payload`, errors);
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6683
|
+
if (eventType === "action.result_recorded") {
|
|
6684
|
+
checkRequired(payload, ["result_hash"], `${path}.payload`, errors);
|
|
6685
|
+
checkOneOf(payload, ["artifact_ref", "task_ref"], `${path}.payload`, errors);
|
|
6686
|
+
}
|
|
6687
|
+
const actionID = typeof payload.action_instance_id === "string" ? payload.action_instance_id : "";
|
|
6688
|
+
if (!actionID) return;
|
|
6689
|
+
const claimID = typeof event.claim_id === "string" ? event.claim_id : "";
|
|
6690
|
+
const operationID = typeof event.operation_id === "string" ? event.operation_id : "";
|
|
6691
|
+
const previous = actionStates.get(actionID);
|
|
6692
|
+
if (!previous && actionState !== "recommended" || previous && (!ACTION_TRANSITIONS[previous.state]?.has(actionState) || event.causation_event_id !== previous.lastEventID || claimID !== previous.claimID || operationID !== previous.operationID)) {
|
|
6693
|
+
errors.push(
|
|
6694
|
+
err(
|
|
6695
|
+
"BKN_TRACE_ACTION_TRANSITION_INVALID",
|
|
6696
|
+
`${path}.event_type`,
|
|
6697
|
+
`invalid action transition ${previous?.state ?? "<none>"} -> ${actionState}`
|
|
6698
|
+
)
|
|
6699
|
+
);
|
|
6700
|
+
return;
|
|
6701
|
+
}
|
|
6702
|
+
actionStates.set(actionID, {
|
|
6703
|
+
state: actionState,
|
|
6704
|
+
claimID,
|
|
6705
|
+
operationID,
|
|
6706
|
+
lastEventID: String(event.event_id ?? "")
|
|
6707
|
+
});
|
|
6708
|
+
}
|
|
6709
|
+
function checkOneOf(payload, fields, path, errors) {
|
|
6710
|
+
if (fields.some((field) => typeof payload[field] === "string" && payload[field] !== "")) return;
|
|
6711
|
+
errors.push(
|
|
6712
|
+
err(
|
|
6713
|
+
"BKN_TRACE_REQUIRED_FIELD_MISSING",
|
|
6714
|
+
`${path}.${fields[0]}`,
|
|
6715
|
+
`one of ${fields.join(" or ")} is required`
|
|
6716
|
+
)
|
|
6717
|
+
);
|
|
6718
|
+
}
|
|
6719
|
+
function checkNonEmptyArray(payload, field, path, errors) {
|
|
6720
|
+
if (Array.isArray(payload[field]) && payload[field].length > 0) return;
|
|
6721
|
+
errors.push(
|
|
6722
|
+
err(
|
|
6723
|
+
"BKN_TRACE_REQUIRED_FIELD_MISSING",
|
|
6724
|
+
`${path}.${field}`,
|
|
6725
|
+
`${field} must be a non-empty array`
|
|
6726
|
+
)
|
|
6727
|
+
);
|
|
6728
|
+
}
|
|
6729
|
+
function checkReferenceList(payload, field, path, errors, allowEmpty = false) {
|
|
6730
|
+
if (!allowEmpty) checkNonEmptyArray(payload, field, path, errors);
|
|
6731
|
+
const refs = Array.isArray(payload[field]) ? payload[field] : [];
|
|
6732
|
+
refs.forEach((value, index) => {
|
|
6733
|
+
const ref = asRecord(value);
|
|
6734
|
+
checkRequired(
|
|
6735
|
+
ref,
|
|
6736
|
+
["ref_id", "ref_type", "source_system", "validity", "version_status", "visibility"],
|
|
6737
|
+
`${path}.${field}[${index}]`,
|
|
6738
|
+
errors
|
|
6739
|
+
);
|
|
6740
|
+
checkAllowedKeys(ref, REFERENCE_FIELDS, `${path}.${field}[${index}]`, errors);
|
|
6741
|
+
if (typeof ref.ref_id === "string" && !isQualifiedReference(ref.ref_id)) {
|
|
6742
|
+
errors.push(
|
|
6743
|
+
err(
|
|
6744
|
+
"BKN_TRACE_REFERENCE_ID_INVALID",
|
|
6745
|
+
`${path}.${field}[${index}].ref_id`,
|
|
6746
|
+
"business reference id must include its knowledge-network or resource scope"
|
|
6747
|
+
)
|
|
6748
|
+
);
|
|
6749
|
+
}
|
|
6750
|
+
});
|
|
6751
|
+
}
|
|
6752
|
+
function checkQualifiedStringRefs(payload, field, path, errors) {
|
|
6753
|
+
const refs = Array.isArray(payload[field]) ? payload[field] : [];
|
|
6754
|
+
refs.forEach((value, index) => {
|
|
6755
|
+
if (typeof value !== "string" || isQualifiedReference(value)) return;
|
|
6756
|
+
errors.push(
|
|
6757
|
+
err(
|
|
6758
|
+
"BKN_TRACE_REFERENCE_ID_INVALID",
|
|
6759
|
+
`${path}.${field}[${index}]`,
|
|
6760
|
+
"business reference id must include its knowledge-network or resource scope"
|
|
6761
|
+
)
|
|
6762
|
+
);
|
|
6763
|
+
});
|
|
6764
|
+
}
|
|
6765
|
+
function isQualifiedReference(value) {
|
|
6766
|
+
const parts = value.trim().split(":");
|
|
6767
|
+
if (parts.some((part) => part.length === 0)) return false;
|
|
6768
|
+
if (["kn", "resource"].includes(parts[0] ?? "")) return parts.length === 2;
|
|
6769
|
+
if (["object", "relation", "action_type", "metric", "field"].includes(parts[0] ?? "")) {
|
|
6770
|
+
return parts.length === 3;
|
|
6771
|
+
}
|
|
6772
|
+
if (parts[0] === "property") return parts.length === 4;
|
|
6773
|
+
return true;
|
|
6774
|
+
}
|
|
6775
|
+
function checkKnownArray(payload, field, known, path, errors) {
|
|
6776
|
+
if (!Array.isArray(payload[field])) return;
|
|
6777
|
+
for (const value of payload[field]) {
|
|
6778
|
+
if (typeof value === "string" && known.has(value)) continue;
|
|
6779
|
+
errors.push(
|
|
6780
|
+
err(
|
|
6781
|
+
"BKN_TRACE_CAUSATION_INVALID",
|
|
6782
|
+
`${path}.${field}`,
|
|
6783
|
+
`${field} must reference earlier events or operations`
|
|
6784
|
+
)
|
|
6785
|
+
);
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6788
|
+
function checkAllowedKeys(value, allowed, path, errors) {
|
|
6789
|
+
for (const key of Object.keys(value)) {
|
|
6790
|
+
if (allowed.has(key)) continue;
|
|
6791
|
+
errors.push(
|
|
6792
|
+
err(
|
|
6793
|
+
"BKN_TRACE_EVENT_PAYLOAD_FIELD_UNSUPPORTED",
|
|
6794
|
+
`${path}.${key}`,
|
|
6795
|
+
`payload field ${key} is not registered for this event`
|
|
6796
|
+
)
|
|
6797
|
+
);
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6800
|
+
function validateFixturePath(path) {
|
|
6801
|
+
const results = jsonFiles(path).map((file) => {
|
|
6802
|
+
try {
|
|
6803
|
+
return validateFixture(JSON.parse(readFileSync4(file, "utf8")));
|
|
6804
|
+
} catch (e) {
|
|
6805
|
+
return {
|
|
6806
|
+
fixtureId: file,
|
|
6807
|
+
result: "fail",
|
|
6808
|
+
contractVersion: null,
|
|
6809
|
+
errors: [
|
|
6810
|
+
err(
|
|
6811
|
+
"BKN_TRACE_FIXTURE_PARSE_FAILED",
|
|
6812
|
+
"$",
|
|
6813
|
+
`failed to parse JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
6814
|
+
)
|
|
6815
|
+
],
|
|
6816
|
+
warnings: [],
|
|
6817
|
+
expectedResult: null,
|
|
6818
|
+
expectationMatched: false
|
|
6819
|
+
};
|
|
6820
|
+
}
|
|
6821
|
+
});
|
|
6822
|
+
return { ok: results.every((r) => r.expectationMatched), results };
|
|
6823
|
+
}
|
|
6824
|
+
|
|
5147
6825
|
// src/resources/trace.ts
|
|
5148
6826
|
async function semanticJudge(question, answer, reference) {
|
|
5149
6827
|
const prompt = [
|
|
@@ -5190,6 +6868,32 @@ function trace(ctx) {
|
|
|
5190
6868
|
return {
|
|
5191
6869
|
/** Raw trace search (OpenSearch-style body). */
|
|
5192
6870
|
search: (body) => traceSearch(ctx, body),
|
|
6871
|
+
/** Submit BKN Trace phase-two claim/evidence/business events. */
|
|
6872
|
+
emitEvidenceEvents: (body) => emitEvidenceEvents(ctx, body),
|
|
6873
|
+
/** Store one authorized BKN Trace 2.2 business-content artifact. */
|
|
6874
|
+
emitArtifact: (body) => emitEvidenceArtifact(ctx, body),
|
|
6875
|
+
/** Read one authorized BKN Trace 2.2 business-content artifact. */
|
|
6876
|
+
artifact: (artifactId) => getEvidenceArtifact(ctx, artifactId),
|
|
6877
|
+
/** Product-facing business request list and request-to-trace drilldown. */
|
|
6878
|
+
requests: {
|
|
6879
|
+
get: (requestId) => getRequestSummary(ctx, requestId),
|
|
6880
|
+
list: (query) => listRequestSummaries(ctx, query),
|
|
6881
|
+
traces: (requestId, query) => getRequestTraces(ctx, requestId, query)
|
|
6882
|
+
},
|
|
6883
|
+
/** Aggregate all OpenBKN requests and traces for one caller-owned interaction. */
|
|
6884
|
+
interactions: {
|
|
6885
|
+
get: (interactionId) => getInteractionSummary(ctx, interactionId)
|
|
6886
|
+
},
|
|
6887
|
+
/** Create a typed BKN Trace 2.1 session for an Agent or AI application. */
|
|
6888
|
+
createSession: (options) => new TraceSession({ ...options, emit: (body) => emitEvidenceEvents(ctx, body) }),
|
|
6889
|
+
/** Normalized trace tree/status graph by trace id. */
|
|
6890
|
+
graph: (traceId) => getTraceGraph(ctx, traceId),
|
|
6891
|
+
/** Claim -> evidence/business refs graph by trace id or BKN request id. */
|
|
6892
|
+
evidenceChain: (scope, opts) => getEvidenceChain(ctx, scope, opts),
|
|
6893
|
+
/** Business semantic graph by trace id or BKN request id. */
|
|
6894
|
+
businessGraph: (scope, opts) => getBusinessGraph(ctx, scope, opts),
|
|
6895
|
+
/** Metadata-only evidence snapshot preview by trace id or BKN request id. */
|
|
6896
|
+
snapshotPreview: (scope, opts) => getSnapshotPreview(ctx, scope, opts),
|
|
5193
6897
|
/** All span source docs for a conversation. */
|
|
5194
6898
|
spans: (conversationId, opts) => getSpansByConversation(ctx, conversationId, opts),
|
|
5195
6899
|
diagnose: diagnoseOne,
|
|
@@ -5227,6 +6931,8 @@ function trace(ctx) {
|
|
|
5227
6931
|
},
|
|
5228
6932
|
/** Build eval cases from a loosely-shaped queries object/array. */
|
|
5229
6933
|
evalSetBuild: (raw) => buildCasesFromQueries(raw),
|
|
6934
|
+
/** Validate BKN Trace phase-one fixture files or directories. */
|
|
6935
|
+
validateFixture: (path) => validateFixturePath(path),
|
|
5230
6936
|
/**
|
|
5231
6937
|
* Run an eval set against an agent: each case's query is sent to the agent,
|
|
5232
6938
|
* the resulting trace is fetched, and assertions are checked. `llm` enables
|
|
@@ -5266,7 +6972,7 @@ function vega(ctx) {
|
|
|
5266
6972
|
deleteCatalog: (id) => deleteCatalog(ctx, id),
|
|
5267
6973
|
testCatalogConnection: (id) => testCatalogConnection(ctx, id),
|
|
5268
6974
|
discoverCatalog: (id, wait = false) => discoverCatalog(ctx, id, wait),
|
|
5269
|
-
catalogResources: (id, category) => listCatalogResources(ctx, id, category),
|
|
6975
|
+
catalogResources: (id, category, limit, offset) => listCatalogResources(ctx, id, category, limit, offset),
|
|
5270
6976
|
catalogHealth: (ids) => catalogHealthStatus(ctx, ids),
|
|
5271
6977
|
connectorTypes: () => listConnectorTypes(ctx),
|
|
5272
6978
|
connectorType: (type) => getConnectorType(ctx, type),
|
|
@@ -5300,7 +7006,7 @@ function sleep(ms) {
|
|
|
5300
7006
|
}
|
|
5301
7007
|
|
|
5302
7008
|
// src/api/call.ts
|
|
5303
|
-
import { readFileSync as
|
|
7009
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
5304
7010
|
function parseHeader(raw) {
|
|
5305
7011
|
const idx = raw.indexOf(":");
|
|
5306
7012
|
if (idx <= 0) return null;
|
|
@@ -5329,7 +7035,7 @@ async function rawCall(ctx, path, opts = {}) {
|
|
|
5329
7035
|
const fd = new FormData();
|
|
5330
7036
|
for (const field of opts.form) {
|
|
5331
7037
|
const [key, value, isFile] = parseFormField(field);
|
|
5332
|
-
if (isFile) fd.append(key, new Blob([
|
|
7038
|
+
if (isFile) fd.append(key, new Blob([readFileSync5(value)]), value.split("/").pop());
|
|
5333
7039
|
else fd.append(key, value);
|
|
5334
7040
|
}
|
|
5335
7041
|
body = fd;
|
|
@@ -5639,6 +7345,8 @@ export {
|
|
|
5639
7345
|
skills,
|
|
5640
7346
|
toolboxes,
|
|
5641
7347
|
renderReportMarkdown,
|
|
7348
|
+
validateFixturePath,
|
|
7349
|
+
TraceSession,
|
|
5642
7350
|
trace,
|
|
5643
7351
|
vega,
|
|
5644
7352
|
createClient,
|
|
@@ -5655,4 +7363,4 @@ export {
|
|
|
5655
7363
|
exportCreds,
|
|
5656
7364
|
auth_exports
|
|
5657
7365
|
};
|
|
5658
|
-
//# sourceMappingURL=chunk-
|
|
7366
|
+
//# sourceMappingURL=chunk-LH3ONZGQ.js.map
|