@openbkn/bkn-sdk 0.1.1-alpha.9 → 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.
@@ -9,12 +9,15 @@ var HttpError = class extends Error {
9
9
  status;
10
10
  statusText;
11
11
  body;
12
- constructor(status2, statusText, body) {
12
+ /** Optional next-step guidance, overriding the status default (e.g. AppKey re-issue). */
13
+ hint;
14
+ constructor(status2, statusText, body, hint) {
13
15
  super(`HTTP ${status2} ${statusText}`);
14
16
  this.name = "HttpError";
15
17
  this.status = status2;
16
18
  this.statusText = statusText;
17
19
  this.body = body;
20
+ this.hint = hint;
18
21
  }
19
22
  };
20
23
  var InputError = class extends Error {
@@ -23,37 +26,38 @@ var InputError = class extends Error {
23
26
  this.name = "InputError";
24
27
  }
25
28
  };
26
- function toExitCode(err) {
27
- if (err instanceof InputError) return 2;
28
- if (err instanceof HttpError) {
29
- if (err.status === 401 || err.status === 403) return 3;
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;
30
33
  return 1;
31
34
  }
32
35
  return 1;
33
36
  }
34
- function formatError(err) {
35
- if (err instanceof HttpError) {
36
- const serverMsg = serverError(err.body);
37
- if (err.status === 401) {
38
- return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. Run \`openbkn auth login\` and retry.`;
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
+ return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. ${next}`;
39
43
  }
40
- if (err.status === 403) {
44
+ if (err2.status === 403) {
41
45
  return `Forbidden (HTTP 403)${serverMsg ? `: ${serverMsg}` : " \u2014 admin privileges required"}.`;
42
46
  }
43
- const detail = err.body ? `: ${truncate(err.body, 500)}` : "";
44
- return `Request failed (HTTP ${err.status} ${err.statusText})${detail}`;
47
+ const detail = err2.body ? `: ${truncate(err2.body, 500)}` : "";
48
+ return `Request failed (HTTP ${err2.status} ${err2.statusText})${detail}`;
45
49
  }
46
- if (err instanceof Error) {
47
- const cause = err.cause;
50
+ if (err2 instanceof Error) {
51
+ const cause = err2.cause;
48
52
  if (cause?.code && isTlsCertError(cause.code)) {
49
53
  return `TLS certificate rejected (${cause.code}). The platform is likely self-signed \u2014 retry with \`-k\`/\`--insecure\`.`;
50
54
  }
51
- if (err.message === "fetch failed" && cause?.message) {
55
+ if (err2.message === "fetch failed" && cause?.message) {
52
56
  return `Request failed: ${cause.message}${cause.code ? ` (${cause.code})` : ""}`;
53
57
  }
54
- return err.message;
58
+ return err2.message;
55
59
  }
56
- return String(err);
60
+ return String(err2);
57
61
  }
58
62
  function serverError(body) {
59
63
  if (!body) return "";
@@ -74,6 +78,36 @@ function truncate(s, n) {
74
78
 
75
79
  // src/auth/oauth.ts
76
80
  import { spawn } from "child_process";
81
+
82
+ // src/api/tls.ts
83
+ import { Agent, FormData as UndiciFormData, fetch as undiciFetch } from "undici";
84
+ var insecureAgent;
85
+ function insecureDispatcher() {
86
+ insecureAgent ??= new Agent({ connect: { rejectUnauthorized: false } });
87
+ return insecureAgent;
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
+ }
101
+ function tlsFetch(insecure, url, init) {
102
+ if (!insecure) return fetch(url, init);
103
+ return undiciFetch(url, {
104
+ ...init,
105
+ ...init?.body === void 0 || init?.body === null ? {} : { body: toUndiciBody(init.body) },
106
+ dispatcher: insecureDispatcher()
107
+ });
108
+ }
109
+
110
+ // src/auth/oauth.ts
77
111
  function normalizeBaseUrl(value) {
78
112
  return value.replace(/\/+$/, "");
79
113
  }
@@ -114,22 +148,9 @@ function mergeCookies(existing, res) {
114
148
  for (const sc of setCookies) add(sc.split(";")[0]?.trim() ?? "");
115
149
  return [...map.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
116
150
  }
117
- async function fetchAuthStatus(baseUrl) {
118
- try {
119
- const res = await fetch(`${normalizeBaseUrl(baseUrl)}/install-status.json`, {
120
- headers: { Accept: "application/json" }
121
- });
122
- if (!res.ok) return null;
123
- const j = await res.json();
124
- if (!j.auth) return null;
125
- return { enabled: Boolean(j.auth.enabled), stack: j.auth.stack };
126
- } catch {
127
- return null;
128
- }
129
- }
130
- async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk") {
151
+ async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk", insecure) {
131
152
  const base = normalizeBaseUrl(baseUrl);
132
- const res = await fetch(`${base}/oauth2/token`, {
153
+ const res = await tlsFetch(insecure, `${base}/oauth2/token`, {
133
154
  method: "POST",
134
155
  headers: {
135
156
  "Content-Type": "application/x-www-form-urlencoded",
@@ -159,10 +180,10 @@ var form = (body) => ({
159
180
  },
160
181
  body: new URLSearchParams(body).toString()
161
182
  });
162
- async function requestDeviceCode(base, clientId, scope, audience) {
183
+ async function requestDeviceCode(base, clientId, scope, audience, insecure) {
163
184
  const params = { client_id: clientId, scope };
164
185
  if (audience) params.audience = audience;
165
- const res = await fetch(`${base}/oauth2/device/auth`, form(params));
186
+ const res = await tlsFetch(insecure, `${base}/oauth2/device/auth`, form(params));
166
187
  if (!res.ok) {
167
188
  throw new Error(`Device auth failed (${res.status}): ${await res.text() || res.statusText}`);
168
189
  }
@@ -174,12 +195,13 @@ async function requestDeviceCode(base, clientId, scope, audience) {
174
195
  }
175
196
  return da;
176
197
  }
177
- async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs) {
198
+ async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs, insecure) {
178
199
  let interval = intervalMs;
179
200
  const deadline = Date.now() + windowMs;
180
201
  while (Date.now() < deadline) {
181
202
  await new Promise((r) => setTimeout(r, interval));
182
- const tokRes = await fetch(
203
+ const tokRes = await tlsFetch(
204
+ insecure,
183
205
  `${base}/oauth2/token`,
184
206
  form({ grant_type: DEVICE_GRANT, device_code: deviceCode, client_id: clientId })
185
207
  );
@@ -205,14 +227,27 @@ async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs)
205
227
  async function deviceLogin(baseUrl, opts = {}) {
206
228
  const base = normalizeBaseUrl(baseUrl);
207
229
  const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
208
- const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
230
+ const da = await requestDeviceCode(
231
+ base,
232
+ clientId,
233
+ opts.scope ?? DEVICE_SCOPE,
234
+ opts.audience,
235
+ opts.insecure
236
+ );
209
237
  opts.onPrompt?.({
210
238
  userCode: da.user_code,
211
239
  verificationUri: onBaseHost(base, da.verification_uri),
212
240
  verificationUriComplete: da.verification_uri_complete ? onBaseHost(base, da.verification_uri_complete) : void 0
213
241
  });
214
242
  const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
215
- return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
243
+ return pollDeviceToken(
244
+ base,
245
+ da.device_code,
246
+ clientId,
247
+ (da.interval ?? 5) * 1e3,
248
+ windowMs,
249
+ opts.insecure
250
+ );
216
251
  }
217
252
  function onBaseHost(base, uri) {
218
253
  try {
@@ -226,10 +261,16 @@ function onBaseHost(base, uri) {
226
261
  async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
227
262
  const base = normalizeBaseUrl(baseUrl);
228
263
  const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
229
- const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
264
+ const da = await requestDeviceCode(
265
+ base,
266
+ clientId,
267
+ opts.scope ?? DEVICE_SCOPE,
268
+ opts.audience,
269
+ opts.insecure
270
+ );
230
271
  let jar = "";
231
272
  const hop = async (url, init) => {
232
- const r = await fetch(url, {
273
+ const r = await tlsFetch(opts.insecure, url, {
233
274
  method: init?.method ?? "GET",
234
275
  headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8", ...init?.headers ?? {} },
235
276
  body: init?.body,
@@ -267,44 +308,132 @@ async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
267
308
  loc = r.headers.get("location");
268
309
  }
269
310
  const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
270
- return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
311
+ return pollDeviceToken(
312
+ base,
313
+ da.device_code,
314
+ clientId,
315
+ (da.interval ?? 5) * 1e3,
316
+ windowMs,
317
+ opts.insecure
318
+ );
319
+ }
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;
271
392
  }
272
393
 
273
394
  // src/api/headers.ts
274
395
  function buildHeaders(ctx, extra) {
396
+ const baggage = serializeBaggage(ctx.trace?.baggage);
275
397
  return {
276
- // No token = a no-auth platform (no bkn-safe); send no Authorization.
277
- ...ctx.token ? { authorization: `Bearer ${ctx.token}`, token: ctx.token } : {},
398
+ authorization: `Bearer ${ctx.token}`,
278
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 } : {},
279
411
  ...extra
280
412
  };
281
413
  }
282
414
 
283
- // src/api/tls.ts
284
- function applyTls(ctx) {
285
- if (ctx.insecure && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0") {
286
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
287
- }
288
- }
289
-
290
415
  // src/api/http.ts
291
416
  var DEFAULT_TIMEOUT_MS = 3e4;
292
417
  async function request(ctx, path, init = {}) {
293
418
  const url = new URL(path.startsWith("http") ? path : `${ctx.baseUrl}${path}`);
294
419
  for (const [k, v] of Object.entries(init.query ?? {})) {
295
- if (v !== void 0) url.searchParams.set(k, String(v));
420
+ if (Array.isArray(v)) {
421
+ for (const item of v) url.searchParams.append(k, String(item));
422
+ } else if (v !== void 0) {
423
+ url.searchParams.set(k, String(v));
424
+ }
296
425
  }
297
- applyTls(ctx);
298
426
  const hasBody = init.body !== void 0;
299
427
  const controller = new AbortController();
300
428
  const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
301
- const send = () => fetch(url, {
429
+ const send = () => tlsFetch(ctx.insecure, url, {
302
430
  method: init.method ?? (hasBody ? "POST" : "GET"),
303
431
  headers: buildHeaders(ctx, {
304
432
  ...hasBody ? { "content-type": "application/json" } : {},
305
433
  ...init.headers
306
434
  }),
307
435
  body: hasBody ? JSON.stringify(init.body) : void 0,
436
+ redirect: init.redirect,
308
437
  signal: controller.signal
309
438
  });
310
439
  try {
@@ -313,16 +442,27 @@ async function request(ctx, path, init = {}) {
313
442
  res = await send();
314
443
  }
315
444
  const text = await res.text();
316
- if (!res.ok) throw new HttpError(res.status, res.statusText, text);
445
+ if (!res.ok) throw new HttpError(res.status, res.statusText, text, hintFor(ctx, res.status));
317
446
  return text ? JSON.parse(text) : void 0;
318
447
  } finally {
319
448
  clearTimeout(timer);
320
449
  }
321
450
  }
451
+ function hintFor(ctx, status2) {
452
+ if (status2 === 401 && ctx.token.startsWith("bak_")) {
453
+ return "AppKey invalid / expired / revoked / owner disabled \u2014 re-issue with `openbkn appkey create` (or `appkey regenerate <id>`). Do not auto-retry.";
454
+ }
455
+ return void 0;
456
+ }
322
457
  async function tryRefresh(ctx) {
323
458
  if (!ctx.refresh) return false;
324
459
  try {
325
- const t = await refreshAccessToken(ctx.baseUrl, ctx.refresh.refreshToken, ctx.refresh.clientId);
460
+ const t = await refreshAccessToken(
461
+ ctx.baseUrl,
462
+ ctx.refresh.refreshToken,
463
+ ctx.refresh.clientId,
464
+ ctx.insecure
465
+ );
326
466
  ctx.token = t.accessToken;
327
467
  if (t.refreshToken) ctx.refresh.refreshToken = t.refreshToken;
328
468
  ctx.refresh.persist(t);
@@ -367,6 +507,7 @@ function isExpired(claims, nowMs = Date.now()) {
367
507
 
368
508
  // src/config/store.ts
369
509
  var PROFILE_RE = /^[A-Za-z0-9_-]{1,64}$/;
510
+ var USER_ID_RE = /^[A-Za-z0-9._@-]{1,128}$/;
370
511
  var IS_WIN = process.platform === "win32";
371
512
  function configDir() {
372
513
  return process.env.BKN_CONFIG_DIR ?? join(homedir(), ".bkn");
@@ -393,7 +534,14 @@ function userDir(baseUrl, userId) {
393
534
  return join(platformDir(baseUrl), "users", userId);
394
535
  }
395
536
  function userIdFromToken(token) {
396
- return decodeJwt(token.idToken ?? "")?.sub ?? decodeJwt(token.accessToken)?.sub ?? "default";
537
+ const sub = decodeJwt(token.idToken ?? "")?.sub ?? decodeJwt(token.accessToken)?.sub;
538
+ if (sub === void 0) return "default";
539
+ if (typeof sub !== "string" || !USER_ID_RE.test(sub) || sub === "." || sub === "..") {
540
+ throw new Error(
541
+ `Token subject '${String(sub)}' is not a usable user id (expected 1-128 chars from [A-Za-z0-9._@-]). Refusing to store this token.`
542
+ );
543
+ }
544
+ return sub;
397
545
  }
398
546
  function readState() {
399
547
  return readJson(statePath()) ?? {};
@@ -418,10 +566,10 @@ function readToken(baseUrl, userId = activeUserId(baseUrl)) {
418
566
  if (!userId) return void 0;
419
567
  return readJson(join(userDir(baseUrl, userId), "token.json")) ?? void 0;
420
568
  }
421
- function writeToken(baseUrl, token) {
569
+ function writeToken(baseUrl, token, opts = {}) {
422
570
  const userId = userIdFromToken(token);
423
571
  writeJson(join(userDir(baseUrl, userId), "token.json"), token, 384);
424
- setActiveUser(baseUrl, userId);
572
+ if (opts.setActive !== false) setActiveUser(baseUrl, userId);
425
573
  return userId;
426
574
  }
427
575
  function deleteToken(baseUrl, userId = activeUserId(baseUrl)) {
@@ -460,6 +608,14 @@ function listPlatforms() {
460
608
  }
461
609
  return out;
462
610
  }
611
+ function findUserId(baseUrl, userOrName) {
612
+ const users = listPlatforms().find((p) => p.baseUrl === baseUrl)?.users ?? [];
613
+ const match = users.find((u) => u.userId === userOrName) ?? users.find((u) => (u.username ?? u.displayName) === userOrName);
614
+ return match?.userId ?? null;
615
+ }
616
+ function usersOfPlatform(baseUrl) {
617
+ return listPlatforms().find((p) => p.baseUrl === baseUrl)?.users ?? [];
618
+ }
463
619
  function decodeKey(key) {
464
620
  try {
465
621
  const b64 = key.replace(/-/g, "+").replace(/_/g, "/");
@@ -485,6 +641,14 @@ function writeJson(path, value, mode = 384) {
485
641
  }
486
642
 
487
643
  // src/config/resolve.ts
644
+ function resolveUserId(baseUrl, userOrName) {
645
+ const id = findUserId(baseUrl, userOrName);
646
+ if (id) return id;
647
+ const known = usersOfPlatform(baseUrl).map((u) => u.username ?? u.userId).join(", ");
648
+ throw new InputError(
649
+ `No saved user '${userOrName}' on ${baseUrl}. Saved: ${known || "(none)"}. See \`openbkn auth users ${baseUrl}\`.`
650
+ );
651
+ }
488
652
  function resolveContext(opts = {}) {
489
653
  const baseUrl = opts.baseUrl ?? process.env.BKN_BASE_URL ?? activePlatform();
490
654
  if (!baseUrl) {
@@ -493,22 +657,29 @@ function resolveContext(opts = {}) {
493
657
  );
494
658
  }
495
659
  const normalized = baseUrl.replace(/\/+$/, "");
496
- const stored = readToken(normalized);
660
+ const user = opts.user ?? process.env.BKN_USER;
661
+ const stored = user ? readToken(normalized, resolveUserId(normalized, user)) : readToken(normalized);
497
662
  const explicit = opts.token ?? process.env.BKN_TOKEN;
498
663
  const token = explicit ?? stored?.accessToken ?? "";
499
- if (!token && !stored?.noAuth) {
664
+ if (!token) {
500
665
  throw new InputError("No access token. Set BKN_TOKEN or run `openbkn auth login`.");
501
666
  }
502
667
  const insecure = opts.insecure ?? stored?.tlsInsecure ?? false;
503
668
  const refresh = !explicit && stored?.refreshToken ? {
504
669
  refreshToken: stored.refreshToken,
505
670
  persist: (t) => {
506
- writeToken(normalized, {
507
- ...stored,
508
- accessToken: t.accessToken,
509
- refreshToken: t.refreshToken ?? stored.refreshToken,
510
- idToken: t.idToken ?? stored.idToken
511
- });
671
+ writeToken(
672
+ normalized,
673
+ {
674
+ ...stored,
675
+ accessToken: t.accessToken,
676
+ refreshToken: t.refreshToken ?? stored.refreshToken,
677
+ idToken: t.idToken ?? stored.idToken
678
+ },
679
+ // `--user` picks an identity for this command only; a refresh
680
+ // must not promote it to the default for the next one.
681
+ { setActive: !user }
682
+ );
512
683
  }
513
684
  } : void 0;
514
685
  return {
@@ -516,6 +687,13 @@ function resolveContext(opts = {}) {
516
687
  token,
517
688
  businessDomain: opts.businessDomain ?? readPlatformConfig(normalized).businessDomain ?? DEFAULT_BUSINESS_DOMAIN,
518
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),
519
697
  ...refresh ? { refresh } : {}
520
698
  };
521
699
  }
@@ -571,6 +749,13 @@ async function setUserPasswordSafe(ctx, userId, password) {
571
749
  });
572
750
  return { ok: true };
573
751
  }
752
+ async function changePasswordSafe(ctx, account, oldPassword, newPassword) {
753
+ await request(ctx, "/api/safe/v1/auth/change-password", {
754
+ method: "POST",
755
+ body: { account, old_password: oldPassword, new_password: newPassword }
756
+ });
757
+ return { ok: true };
758
+ }
574
759
  function getUserRolesSafe(ctx, userId) {
575
760
  return request(ctx, `${ADMIN}/role-bindings`, { query: { accessor_id: userId } });
576
761
  }
@@ -684,6 +869,43 @@ async function setRolePermissionSafe(ctx, roleId, grant, perm) {
684
869
  });
685
870
  return { ok: true };
686
871
  }
872
+ function getLicenseSafe(ctx) {
873
+ return request(ctx, `${ADMIN}/license`);
874
+ }
875
+ async function importLicenseSafe(ctx, licenseText, opts = {}) {
876
+ const text = licenseText.trim();
877
+ if (!text) throw new InputError("license text is empty");
878
+ try {
879
+ return await request(ctx, `${ADMIN}/license/${opts.receipt ? "receipt" : "import"}`, {
880
+ method: "POST",
881
+ body: { license: text }
882
+ });
883
+ } catch (err2) {
884
+ if (err2 instanceof HttpError) {
885
+ const stored = storedImport(err2.body);
886
+ if (stored) return stored;
887
+ }
888
+ throw err2;
889
+ }
890
+ }
891
+ function storedImport(body) {
892
+ try {
893
+ const parsed = JSON.parse(body);
894
+ if (parsed && parsed.stored === true) return parsed;
895
+ } catch {
896
+ }
897
+ return null;
898
+ }
899
+ function activateLicenseSafe(ctx) {
900
+ return request(ctx, `${ADMIN}/license/activate`, { method: "POST" });
901
+ }
902
+ async function removeLicenseSafe(ctx) {
903
+ await request(ctx, `${ADMIN}/license`, { method: "DELETE" });
904
+ return { ok: true };
905
+ }
906
+ function getLicenseFingerprintSafe(ctx) {
907
+ return request(ctx, `${ADMIN}/license/fingerprint`);
908
+ }
687
909
 
688
910
  // src/resources/admin.ts
689
911
  var DEFAULT_NEW_USER_PASSWORD = "openbkn";
@@ -744,7 +966,13 @@ function admin(ctx) {
744
966
  roleUpdate: (roleId, input) => updateRoleSafe(ctx, roleId, input),
745
967
  roleDelete: (roleId) => deleteRoleSafe(ctx, roleId),
746
968
  rolePermission: (roleId, grant, resourceType, resourceId, operations) => setRolePermissionSafe(ctx, roleId, grant, { resourceType, resourceId, operations }),
747
- auditList: (_opts) => notOnSafe("audit list")
969
+ auditList: (_opts) => notOnSafe("audit list"),
970
+ // ── license (cluster license hub; weak judgements — display/ops only) ──
971
+ licenseGet: () => getLicenseSafe(ctx),
972
+ licenseImport: (licenseText, opts) => importLicenseSafe(ctx, licenseText, opts),
973
+ licenseActivate: () => activateLicenseSafe(ctx),
974
+ licenseRemove: () => removeLicenseSafe(ctx),
975
+ licenseFingerprint: () => getLicenseFingerprintSafe(ctx)
748
976
  };
749
977
  }
750
978
 
@@ -816,7 +1044,6 @@ function extractText(result) {
816
1044
  return "";
817
1045
  }
818
1046
  async function sendChat(ctx, info, query, opts = {}) {
819
- applyTls(ctx);
820
1047
  const body = {
821
1048
  agent_id: info.id,
822
1049
  agent_key: info.key,
@@ -827,7 +1054,7 @@ async function sendChat(ctx, info, query, opts = {}) {
827
1054
  if (opts.conversationId) body.conversation_id = opts.conversationId;
828
1055
  const res = await authFetch(
829
1056
  ctx,
830
- () => fetch(`${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
1057
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
831
1058
  method: "POST",
832
1059
  headers: {
833
1060
  ...buildHeaders(ctx),
@@ -1029,16 +1256,20 @@ function nextId() {
1029
1256
  function mcpUrl(ctx) {
1030
1257
  return `${ctx.baseUrl}${MCP_PATH}`;
1031
1258
  }
1259
+ function operationContext(ctx) {
1260
+ return ctx.trace ? { ...ctx, trace: createOperationTraceContext(ctx.trace) } : ctx;
1261
+ }
1262
+ function mcpInfo(ctx) {
1263
+ return request(ctx, `${MCP_PATH}/info`);
1264
+ }
1032
1265
  function headers(ctx, knId, sessionId) {
1033
- const h = {
1266
+ return buildHeaders(ctx, {
1034
1267
  "content-type": "application/json",
1035
1268
  accept: "application/json, text/event-stream",
1036
1269
  "x-kn-id": knId,
1037
1270
  "mcp-protocol-version": PROTOCOL,
1038
- authorization: `Bearer ${ctx.token}`
1039
- };
1040
- if (sessionId) h["mcp-session-id"] = sessionId;
1041
- return h;
1271
+ ...sessionId ? { "mcp-session-id": sessionId } : {}
1272
+ });
1042
1273
  }
1043
1274
  function parseBody(text) {
1044
1275
  try {
@@ -1050,10 +1281,9 @@ function parseBody(text) {
1050
1281
  }
1051
1282
  }
1052
1283
  async function post(ctx, knId, sessionId, body) {
1053
- applyTls(ctx);
1054
1284
  const res = await authFetch(
1055
1285
  ctx,
1056
- () => fetch(mcpUrl(ctx), {
1286
+ () => tlsFetch(ctx.insecure, mcpUrl(ctx), {
1057
1287
  method: "POST",
1058
1288
  headers: headers(ctx, knId, sessionId),
1059
1289
  body: JSON.stringify(body)
@@ -1100,8 +1330,9 @@ function unwrap(parsed) {
1100
1330
  return result;
1101
1331
  }
1102
1332
  async function callTool(ctx, knId, name, args) {
1103
- const sessionId = await ensureSession(ctx, knId);
1104
- const { text } = await post(ctx, knId, sessionId, {
1333
+ const operationCtx = operationContext(ctx);
1334
+ const sessionId = await ensureSession(operationCtx, knId);
1335
+ const { text } = await post(operationCtx, knId, sessionId, {
1105
1336
  jsonrpc: "2.0",
1106
1337
  method: "tools/call",
1107
1338
  params: { name, arguments: args },
@@ -1110,8 +1341,9 @@ async function callTool(ctx, knId, name, args) {
1110
1341
  return unwrap(parseBody(text));
1111
1342
  }
1112
1343
  async function callMethod(ctx, knId, method, params = {}) {
1113
- const sessionId = await ensureSession(ctx, knId);
1114
- const { text } = await post(ctx, knId, sessionId, {
1344
+ const operationCtx = operationContext(ctx);
1345
+ const sessionId = await ensureSession(operationCtx, knId);
1346
+ const { text } = await post(operationCtx, knId, sessionId, {
1115
1347
  jsonrpc: "2.0",
1116
1348
  method,
1117
1349
  params: Object.keys(params).length > 0 ? params : void 0,
@@ -1135,6 +1367,17 @@ function findSkills(ctx, knId, objectTypeId, topK) {
1135
1367
  if (topK !== void 0) args.top_k = topK;
1136
1368
  return callTool(ctx, knId, "find_skills", args);
1137
1369
  }
1370
+ function getKnDetail(ctx, knId, detailLevel) {
1371
+ const args = { response_format: "json" };
1372
+ if (detailLevel) args.detail_level = detailLevel;
1373
+ return callTool(ctx, knId, "get_kn_detail", args);
1374
+ }
1375
+ function getObjectTypes(ctx, knId, ids) {
1376
+ return callTool(ctx, knId, "get_object_types", { ids, response_format: "json" });
1377
+ }
1378
+ function getRelationTypes(ctx, knId, ids) {
1379
+ return callTool(ctx, knId, "get_relation_types", { ids, response_format: "json" });
1380
+ }
1138
1381
  function listTools(ctx, knId) {
1139
1382
  return callMethod(ctx, knId, "tools/list");
1140
1383
  }
@@ -1169,8 +1412,16 @@ function context(ctx) {
1169
1412
  searchSchema: (knId, query, opts) => searchSchema(ctx, knId, query, opts),
1170
1413
  queryObjectInstance: (knId, args) => queryObjectInstance(ctx, knId, args),
1171
1414
  findSkills: (knId, objectTypeId, topK) => findSkills(ctx, knId, objectTypeId, topK),
1415
+ // Progressive schema disclosure: skeleton first (summary), then drill down.
1416
+ knDetail: (knId, detailLevel) => getKnDetail(ctx, knId, detailLevel),
1417
+ objectTypes: (knId, ids) => getObjectTypes(ctx, knId, ids),
1418
+ relationTypes: (knId, ids) => getRelationTypes(ctx, knId, ids),
1419
+ info: () => mcpInfo(ctx),
1172
1420
  tools: (knId) => listTools(ctx, knId),
1173
1421
  toolCall: (knId, name, args) => callTool(ctx, knId, name, args),
1422
+ // Generic MCP method passthrough — covers methods not yet wrapped, so the
1423
+ // surface doesn't have to grow every time the server adds one.
1424
+ callMethod: (knId, method, params) => callMethod(ctx, knId, method, params),
1174
1425
  queryInstanceSubgraph: (knId, args) => queryInstanceSubgraph(ctx, knId, args),
1175
1426
  logicProperties: (knId, args) => getLogicProperties(ctx, knId, args),
1176
1427
  actionInfo: (knId, args) => getActionInfo(ctx, knId, args),
@@ -1227,7 +1478,11 @@ async function executeDataflow(ctx, body, opts = {}) {
1227
1478
  }
1228
1479
  function listDataflowRuns(ctx, dagId, opts = {}) {
1229
1480
  return request(ctx, `${BASE2}/dag/${encodeURIComponent(dagId)}/results`, {
1230
- query: { since: opts.since || void 0 }
1481
+ query: {
1482
+ since: opts.since || void 0,
1483
+ page: opts.page,
1484
+ limit: opts.limit && opts.limit > 0 ? opts.limit : void 0
1485
+ }
1231
1486
  });
1232
1487
  }
1233
1488
  function runDataflowRemote(ctx, dagId, url, name) {
@@ -1283,9 +1538,11 @@ function deleteKnowledgeNetwork(ctx, knId) {
1283
1538
  function updateKnowledgeNetwork(ctx, knId, body) {
1284
1539
  return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}`, { method: "PUT", body });
1285
1540
  }
1541
+ var QUERY_OVER_POST = { "X-HTTP-Method-Override": "GET" };
1286
1542
  function querySubgraph(ctx, knId, body) {
1287
1543
  return request(ctx, `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/subgraph`, {
1288
1544
  method: "POST",
1545
+ headers: QUERY_OVER_POST,
1289
1546
  body
1290
1547
  });
1291
1548
  }
@@ -1317,13 +1574,7 @@ function queryObjectTypeInstances(ctx, knId, otId, body) {
1317
1574
  return request(
1318
1575
  ctx,
1319
1576
  `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/object-types/${encodeURIComponent(otId)}`,
1320
- { method: "POST", body }
1321
- );
1322
- }
1323
- function getObjectTypeProperties(ctx, knId, otId) {
1324
- return request(
1325
- ctx,
1326
- `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/object-types/${encodeURIComponent(otId)}/properties`
1577
+ { method: "POST", headers: QUERY_OVER_POST, body }
1327
1578
  );
1328
1579
  }
1329
1580
  function queryActionType(ctx, knId, atId, body) {
@@ -1490,7 +1741,19 @@ function listResources2(ctx, opts = {}) {
1490
1741
  catalog_id: opts.datasourceId || void 0,
1491
1742
  name: opts.name || void 0,
1492
1743
  category: opts.category || void 0,
1493
- limit: opts.limit && opts.limit > 0 ? opts.limit : void 0
1744
+ status: opts.status || void 0,
1745
+ database: opts.database || void 0,
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,
1750
+ offset: opts.offset,
1751
+ sort: opts.sort,
1752
+ direction: opts.direction,
1753
+ include_extensions: opts.includeExtensions === void 0 ? void 0 : String(opts.includeExtensions),
1754
+ include_extension_keys: opts.includeExtensionKeys || void 0,
1755
+ extension_key: opts.extensionPairs?.map((p) => p.key),
1756
+ extension_value: opts.extensionPairs?.map((p) => p.value)
1494
1757
  }
1495
1758
  });
1496
1759
  }
@@ -1500,32 +1763,130 @@ function getResource(ctx, id) {
1500
1763
  function createResourceRaw(ctx, body) {
1501
1764
  return request(ctx, BASE3, { method: "POST", body });
1502
1765
  }
1503
- function createResource(ctx, opts) {
1766
+ function updateResourceRaw(ctx, id, body) {
1767
+ return request(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "PUT", body });
1768
+ }
1769
+ async function updateResource(ctx, id, patch) {
1770
+ const current = firstResource(await getResource(ctx, id));
1771
+ return updateResourceRaw(ctx, id, resourceUpdateBody(id, current, patch));
1772
+ }
1773
+ async function configureResourceIndex(ctx, id, opts) {
1774
+ const current = firstResource(await getResource(ctx, id));
1775
+ const schema = (current.schema_definition ?? []).map((prop) => ({ ...prop }));
1776
+ const indexConfig = {
1777
+ ...current.index_config ?? {},
1778
+ ...opts.buildKeyFields?.length ? { build_key_fields: opts.buildKeyFields } : {},
1779
+ ...opts.embeddingModel ? { default_embedding_model: opts.embeddingModel } : {},
1780
+ ...opts.fulltextAnalyzer ? { default_fulltext_analyzer: opts.fulltextAnalyzer } : {}
1781
+ };
1782
+ for (const field of opts.embeddingFields ?? []) {
1783
+ ensureFeature(
1784
+ schema,
1785
+ field,
1786
+ "vector",
1787
+ opts.embeddingModel ? { embedding_model: opts.embeddingModel } : void 0
1788
+ );
1789
+ }
1790
+ for (const field of opts.fulltextFields ?? []) {
1791
+ ensureFeature(
1792
+ schema,
1793
+ field,
1794
+ "fulltext",
1795
+ opts.fulltextAnalyzer ? { analyzer: opts.fulltextAnalyzer } : void 0
1796
+ );
1797
+ }
1798
+ return updateResourceRaw(
1799
+ ctx,
1800
+ id,
1801
+ resourceUpdateBody(id, current, { schemaDefinition: schema, indexConfig })
1802
+ );
1803
+ }
1804
+ function resourceUpdateBody(id, current, patch) {
1504
1805
  const body = {
1505
- name: opts.name,
1506
- catalog_id: opts.catalogId,
1507
- category: "table",
1508
- source_identifier: opts.sourceIdentifier
1806
+ id,
1807
+ name: patch.name ?? current.name,
1808
+ catalog_id: patch.catalogId ?? current.catalog_id,
1809
+ tags: patch.tags ?? current.tags ?? [],
1810
+ description: patch.description ?? current.description ?? "",
1811
+ category: patch.category ?? current.category,
1812
+ status: patch.status ?? current.status,
1813
+ database: patch.database ?? current.database,
1814
+ source_identifier: patch.sourceIdentifier ?? current.source_identifier,
1815
+ source_metadata: patch.sourceMetadata ?? current.source_metadata,
1816
+ schema_definition: patch.schemaDefinition ?? current.schema_definition,
1817
+ index_config: patch.indexConfig === void 0 ? current.index_config : patch.indexConfig,
1818
+ logic_definition: patch.logicDefinition ?? current.logic_definition
1509
1819
  };
1510
- if (opts.fields && opts.fields.length > 0) body.schema_definition = opts.fields;
1511
- return request(ctx, BASE3, { method: "POST", body });
1820
+ if (patch.extensions !== void 0 || current.extensions !== void 0) {
1821
+ body.extensions = patch.extensions ?? current.extensions;
1822
+ }
1823
+ return body;
1824
+ }
1825
+ function ensureFeature(schema, field, featureType, config) {
1826
+ const prop = schema.find((p) => p.name === field);
1827
+ if (!prop) throw new Error(`resource field '${field}' not found in schema_definition`);
1828
+ const features = [...prop.features ?? []];
1829
+ const existing = features.find(
1830
+ (f) => f.feature_type === featureType && (f.ref_property || field) === field
1831
+ );
1832
+ if (existing) {
1833
+ existing.ref_property = existing.ref_property || field;
1834
+ existing.config = { ...existing.config ?? {}, ...config ?? {} };
1835
+ } else {
1836
+ features.push({
1837
+ name: `${field}_${featureType}`,
1838
+ feature_type: featureType,
1839
+ ref_property: field,
1840
+ is_default: false,
1841
+ is_native: false,
1842
+ ...config ? { config } : {}
1843
+ });
1844
+ }
1845
+ prop.features = features;
1512
1846
  }
1513
- function deleteResource(ctx, id) {
1514
- return request(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "DELETE" });
1847
+ function firstResource(result) {
1848
+ if (result && typeof result === "object") {
1849
+ const o = result;
1850
+ if (Array.isArray(o.entries)) return o.entries[0] ?? {};
1851
+ return o;
1852
+ }
1853
+ return {};
1854
+ }
1855
+ function deleteResource(ctx, id, opts = {}) {
1856
+ const ids = Array.isArray(id) ? id : [id];
1857
+ return request(ctx, `${BASE3}/${ids.map(encodeURIComponent).join(",")}`, {
1858
+ method: "DELETE",
1859
+ query: {
1860
+ ignore_missing: opts.ignoreMissing === void 0 ? void 0 : String(opts.ignoreMissing)
1861
+ }
1862
+ });
1515
1863
  }
1516
1864
  async function findResource(ctx, name, opts = {}) {
1517
- const result = await listResources2(ctx, { name, datasourceId: opts.datasourceId });
1865
+ const result = await listResources2(ctx, {
1866
+ name,
1867
+ datasourceId: opts.datasourceId,
1868
+ limit: opts.limit
1869
+ });
1518
1870
  const list = Array.isArray(result) ? result : result.entries ?? [];
1519
1871
  return opts.exact ? list.filter((r) => r.name === name) : list;
1520
1872
  }
1521
1873
  function queryResource(ctx, id, opts = {}) {
1522
- return request(ctx, `${BASE3}/${encodeURIComponent(id)}/data`, {
1523
- method: "POST",
1524
- body: {
1874
+ const body = opts.cursor ? {
1875
+ paging: { cursor: opts.cursor },
1876
+ need_total: opts.needTotal ?? false
1877
+ } : {
1878
+ paging: {
1879
+ mode: opts.pagingMode ?? "single",
1525
1880
  limit: opts.limit ?? 50,
1526
1881
  offset: opts.offset ?? 0,
1527
- need_total: opts.needTotal ?? false
1528
- }
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
1529
1890
  });
1530
1891
  }
1531
1892
 
@@ -2733,7 +3094,6 @@ function knPath(knId, path) {
2733
3094
  return `${BASE4}/${encodeURIComponent(knId)}/${path}`;
2734
3095
  }
2735
3096
  async function uploadBkn(ctx, tarBuffer, opts = {}) {
2736
- applyTls(ctx);
2737
3097
  const url = new URL(`${ctx.baseUrl}${BKNS}`);
2738
3098
  url.searchParams.set("branch", opts.branch ?? "main");
2739
3099
  const form2 = new FormData();
@@ -2744,17 +3104,19 @@ async function uploadBkn(ctx, tarBuffer, opts = {}) {
2744
3104
  );
2745
3105
  const res = await authFetch(
2746
3106
  ctx,
2747
- () => fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form2 })
3107
+ () => tlsFetch(ctx.insecure, url, { method: "POST", headers: buildHeaders(ctx), body: form2 })
2748
3108
  );
2749
3109
  const text = await res.text();
2750
3110
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
2751
3111
  return text ? JSON.parse(text) : void 0;
2752
3112
  }
2753
3113
  async function downloadBkn(ctx, knId, opts = {}) {
2754
- applyTls(ctx);
2755
3114
  const url = new URL(`${ctx.baseUrl}${BKNS}/${encodeURIComponent(knId)}`);
2756
3115
  url.searchParams.set("branch", opts.branch ?? "main");
2757
- const res = await authFetch(ctx, () => fetch(url, { method: "GET", headers: buildHeaders(ctx) }));
3116
+ const res = await authFetch(
3117
+ ctx,
3118
+ () => tlsFetch(ctx.insecure, url, { method: "GET", headers: buildHeaders(ctx) })
3119
+ );
2758
3120
  if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
2759
3121
  return Buffer.from(await res.arrayBuffer());
2760
3122
  }
@@ -2762,7 +3124,11 @@ function listBknResources(ctx) {
2762
3124
  return request(ctx, "/api/bkn-backend/v1/resources");
2763
3125
  }
2764
3126
  function relationTypePaths(ctx, knId, body) {
2765
- return request(ctx, knPath(knId, "relation-type-paths"), { method: "POST", body });
3127
+ return request(ctx, knPath(knId, "relation-type-paths"), {
3128
+ method: "POST",
3129
+ headers: { "X-HTTP-Method-Override": "GET" },
3130
+ body
3131
+ });
2766
3132
  }
2767
3133
  function listConceptGroups(ctx, knId) {
2768
3134
  return request(ctx, knPath(knId, "concept-groups"));
@@ -2823,18 +3189,6 @@ function setActionScheduleStatus(ctx, knId, scheduleId, body) {
2823
3189
  function deleteActionSchedules(ctx, knId, ids) {
2824
3190
  return request(ctx, knPath(knId, `action-schedules/${ids}`), { method: "DELETE" });
2825
3191
  }
2826
- function listJobs(ctx, knId) {
2827
- return request(ctx, knPath(knId, "jobs"));
2828
- }
2829
- function getJob(ctx, knId, jobId) {
2830
- return request(ctx, knPath(knId, `jobs/${encodeURIComponent(jobId)}`));
2831
- }
2832
- function getJobTasks(ctx, knId, jobId) {
2833
- return request(ctx, knPath(knId, `jobs/${encodeURIComponent(jobId)}/tasks`));
2834
- }
2835
- function deleteJobs(ctx, knId, ids) {
2836
- return request(ctx, knPath(knId, `jobs/${ids}`), { method: "DELETE" });
2837
- }
2838
3192
 
2839
3193
  // src/api/vega.ts
2840
3194
  import { z } from "zod";
@@ -2843,10 +3197,7 @@ var BuildMode = z.enum(["batch", "streaming"]);
2843
3197
  var CreateBuildTaskRequest = z.object({
2844
3198
  resource_id: z.string().min(1),
2845
3199
  mode: BuildMode,
2846
- embedding_fields: z.array(z.string()).optional(),
2847
- build_key_fields: z.array(z.string()).optional(),
2848
- embedding_model: z.string().optional(),
2849
- model_dimensions: z.number().int().positive().optional()
3200
+ execute_type: z.enum(["incremental", "full"]).optional()
2850
3201
  });
2851
3202
  var BuildTask = z.object({
2852
3203
  id: z.string(),
@@ -2857,31 +3208,83 @@ var BuildTask = z.object({
2857
3208
  total_count: z.number().optional(),
2858
3209
  synced_count: z.number().optional(),
2859
3210
  vectorized_count: z.number().optional(),
2860
- embedding_fields: z.string().optional(),
2861
- build_key_fields: z.string().optional(),
2862
- embedding_model: z.string().optional(),
2863
- model_dimensions: z.number().optional()
3211
+ index_config: z.unknown().optional(),
3212
+ catalog_id: z.string().optional(),
3213
+ index_health: z.object({
3214
+ embedding: z.string(),
3215
+ fulltext: z.string(),
3216
+ usable: z.boolean()
3217
+ }).passthrough().optional()
2864
3218
  }).passthrough();
2865
3219
  async function createBuildTask(ctx, req) {
2866
3220
  const p = CreateBuildTaskRequest.parse(req);
2867
3221
  const body = {
2868
3222
  resource_id: p.resource_id,
2869
3223
  mode: p.mode,
2870
- ...p.embedding_fields?.length ? { embedding_fields: p.embedding_fields.join(",") } : {},
2871
- ...p.build_key_fields?.length ? { build_key_fields: p.build_key_fields.join(",") } : {},
2872
- ...p.embedding_model ? { embedding_model: p.embedding_model } : {},
2873
- ...p.model_dimensions ? { model_dimensions: p.model_dimensions } : {}
3224
+ ...p.execute_type ? { execute_type: p.execute_type } : {}
2874
3225
  };
2875
3226
  const res = await request(ctx, `${VEGA_BASE}/build-tasks`, { method: "POST", body });
2876
3227
  return BuildTask.parse(res);
2877
3228
  }
3229
+ function listBuildTasks(ctx, opts = {}) {
3230
+ return request(ctx, `${VEGA_BASE}/build-tasks`, {
3231
+ query: {
3232
+ limit: opts.limit,
3233
+ offset: opts.offset,
3234
+ resource_id: opts.resourceId || void 0,
3235
+ catalog_id: opts.catalogId || void 0,
3236
+ status: Array.isArray(opts.status) ? opts.status.join(",") : opts.status || void 0,
3237
+ active: opts.active === void 0 ? void 0 : String(opts.active),
3238
+ mode: opts.mode,
3239
+ order_by: opts.orderBy,
3240
+ order: opts.order
3241
+ }
3242
+ });
3243
+ }
2878
3244
  async function getBuildTask(ctx, taskId) {
2879
3245
  const res = await request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}`);
2880
3246
  return BuildTask.parse(res);
2881
3247
  }
3248
+ function deleteBuildTasks(ctx, ids, opts = {}) {
3249
+ return request(ctx, `${VEGA_BASE}/build-tasks/${ids.map(encodeURIComponent).join(",")}`, {
3250
+ method: "DELETE",
3251
+ query: {
3252
+ ignore_missing: opts.ignoreMissing === void 0 ? void 0 : String(opts.ignoreMissing),
3253
+ delete_active_index: opts.deleteActiveIndex === void 0 ? void 0 : String(opts.deleteActiveIndex)
3254
+ }
3255
+ });
3256
+ }
3257
+ function startBuildTask(ctx, taskId, opts = {}) {
3258
+ return request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}/start`, {
3259
+ method: "POST",
3260
+ body: opts.reset === void 0 ? {} : { reset: opts.reset }
3261
+ });
3262
+ }
3263
+ function stopBuildTask(ctx, taskId) {
3264
+ return request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}/stop`, {
3265
+ method: "POST"
3266
+ });
3267
+ }
3268
+ function runSql(ctx, body) {
3269
+ return request(ctx, `${VEGA_BASE}/resources/query`, { method: "POST", body });
3270
+ }
2882
3271
  async function listCatalogs(ctx, opts = {}) {
2883
3272
  return request(ctx, `${VEGA_BASE}/catalogs`, {
2884
- query: { limit: opts.limit, offset: opts.offset }
3273
+ query: {
3274
+ limit: opts.limit,
3275
+ offset: opts.offset,
3276
+ name: opts.name || void 0,
3277
+ tag: opts.tag || void 0,
3278
+ type: opts.type || void 0,
3279
+ enabled: opts.enabled === void 0 ? void 0 : String(opts.enabled),
3280
+ health_check_status: opts.healthCheckStatus || void 0,
3281
+ include_extensions: opts.includeExtensions === void 0 ? void 0 : String(opts.includeExtensions),
3282
+ include_extension_keys: opts.includeExtensionKeys || void 0,
3283
+ extension_key: opts.extensionPairs?.map((p) => p.key),
3284
+ extension_value: opts.extensionPairs?.map((p) => p.value),
3285
+ sort: opts.sort,
3286
+ direction: opts.direction
3287
+ }
2885
3288
  });
2886
3289
  }
2887
3290
  function getCatalog(ctx, id) {
@@ -2891,18 +3294,49 @@ function createCatalog(ctx, req) {
2891
3294
  return request(ctx, `${VEGA_BASE}/catalogs`, {
2892
3295
  method: "POST",
2893
3296
  body: {
3297
+ ...req.id ? { id: req.id } : {},
2894
3298
  name: req.name,
2895
3299
  connector_type: req.connectorType,
2896
3300
  connector_config: req.connectorConfig,
2897
3301
  ...req.tags ? { tags: req.tags } : {},
2898
3302
  ...req.description ? { description: req.description } : {},
2899
- ...req.enabled !== void 0 ? { enabled: req.enabled } : {}
3303
+ ...req.enabled !== void 0 ? { enabled: req.enabled } : {},
3304
+ ...req.internal !== void 0 ? { internal: req.internal } : {},
3305
+ ...req.extensions ? { extensions: req.extensions } : {}
3306
+ }
3307
+ });
3308
+ }
3309
+ function updateCatalog(ctx, id, req) {
3310
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}`, {
3311
+ method: "PUT",
3312
+ body: {
3313
+ ...req.id ? { id: req.id } : {},
3314
+ ...req.name ? { name: req.name } : {},
3315
+ ...req.connectorType ? { connector_type: req.connectorType } : {},
3316
+ ...req.connectorConfig !== void 0 ? { connector_config: req.connectorConfig } : {},
3317
+ ...req.tags ? { tags: req.tags } : {},
3318
+ ...req.description !== void 0 ? { description: req.description } : {},
3319
+ ...req.enabled !== void 0 ? { enabled: req.enabled } : {},
3320
+ ...req.extensions ? { extensions: req.extensions } : {}
2900
3321
  }
2901
3322
  });
2902
3323
  }
2903
3324
  function enableCatalog(ctx, id) {
2904
3325
  return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/enable`, { method: "POST" });
2905
3326
  }
3327
+ function disableCatalog(ctx, id) {
3328
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/disable`, {
3329
+ method: "POST"
3330
+ });
3331
+ }
3332
+ function deleteCatalog(ctx, id) {
3333
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}`, { method: "DELETE" });
3334
+ }
3335
+ function testCatalogConnection(ctx, id) {
3336
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/test-connection`, {
3337
+ method: "POST"
3338
+ });
3339
+ }
2906
3340
  function discoverCatalog(ctx, id, wait = true) {
2907
3341
  return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/discover`, {
2908
3342
  method: "POST",
@@ -2910,9 +3344,15 @@ function discoverCatalog(ctx, id, wait = true) {
2910
3344
  timeoutMs: 12e4
2911
3345
  });
2912
3346
  }
2913
- function listCatalogResources(ctx, id, category) {
3347
+ function listCatalogResources(ctx, id, category, limit, offset) {
2914
3348
  return request(ctx, `${VEGA_BASE}/resources`, {
2915
- query: { catalog_id: id, category: category || void 0 }
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
+ }
2916
3356
  });
2917
3357
  }
2918
3358
  function catalogHealthStatus(ctx, ids) {
@@ -3372,7 +3812,7 @@ async function createFromCatalog(ctx, opts) {
3372
3812
  }
3373
3813
  tablePk[t.name] = res.pk;
3374
3814
  }
3375
- log(`Creating resources for ${targets.length} table(s)...`);
3815
+ log(`Resolving discovered resources for ${targets.length} table(s)...`);
3376
3816
  const viewMap = {};
3377
3817
  for (const t of targets) {
3378
3818
  const found = asArray(
@@ -3382,13 +3822,9 @@ async function createFromCatalog(ctx, opts) {
3382
3822
  if (existingId) {
3383
3823
  viewMap[t.name] = existingId;
3384
3824
  } else {
3385
- const created = await createResource(ctx, {
3386
- name: t.name,
3387
- catalogId: opts.catalogId,
3388
- sourceIdentifier: t.name,
3389
- fields: t.columns.map((c) => ({ name: c.name, type: c.type }))
3390
- });
3391
- viewMap[t.name] = String(created.id ?? "");
3825
+ throw new Error(
3826
+ `Table '${t.name}' has no discovered Vega resource. Run catalog discover and retry.`
3827
+ );
3392
3828
  }
3393
3829
  }
3394
3830
  const knCreated = await createKnowledgeNetwork(ctx, { name: opts.name });
@@ -3420,12 +3856,14 @@ async function createFromCatalog(ctx, opts) {
3420
3856
  log("Submitting build tasks...");
3421
3857
  for (const t of targets) {
3422
3858
  const embedding = opts.embeddingFields?.[t.name];
3859
+ await configureResourceIndex(ctx, viewMap[t.name], {
3860
+ buildKeyFields: [tablePk[t.name]],
3861
+ ...embedding && embedding.length > 0 ? { embeddingFields: embedding } : {},
3862
+ ...opts.embeddingModel ? { embeddingModel: opts.embeddingModel } : {}
3863
+ });
3423
3864
  const task = await createBuildTask(ctx, {
3424
3865
  resource_id: viewMap[t.name],
3425
- mode: "batch",
3426
- build_key_fields: [tablePk[t.name]],
3427
- ...embedding && embedding.length > 0 ? { embedding_fields: embedding } : {},
3428
- ...opts.embeddingModel ? { embedding_model: opts.embeddingModel } : {}
3866
+ mode: "batch"
3429
3867
  });
3430
3868
  builds.push({ table: t.name, taskId: String(task.id ?? "") });
3431
3869
  }
@@ -3556,7 +3994,6 @@ function kn(ctx) {
3556
3994
  metricValidate: (knId, body) => validateMetric(ctx, knId, body),
3557
3995
  objectTypes: (knId, opts) => listObjectTypes(ctx, knId, opts),
3558
3996
  objectTypeQuery: (knId, otId, body) => queryObjectTypeInstances(ctx, knId, otId, body),
3559
- objectTypeProperties: (knId, otId) => getObjectTypeProperties(ctx, knId, otId),
3560
3997
  objectTypeGet: (knId, id) => getSchemaItem(ctx, knId, "object-types", id),
3561
3998
  objectTypeCreate: (knId, body) => createSchemaItem(ctx, knId, "object-types", body),
3562
3999
  objectTypeUpdate: (knId, id, body) => updateSchemaItem(ctx, knId, "object-types", id, body),
@@ -3584,10 +4021,6 @@ function kn(ctx) {
3584
4021
  actionScheduleUpdate: (knId, scheduleId, body) => updateActionSchedule(ctx, knId, scheduleId, body),
3585
4022
  actionScheduleSetStatus: (knId, scheduleId, body) => setActionScheduleStatus(ctx, knId, scheduleId, body),
3586
4023
  actionScheduleDelete: (knId, ids) => deleteActionSchedules(ctx, knId, ids),
3587
- jobs: (knId) => listJobs(ctx, knId),
3588
- job: (knId, jobId) => getJob(ctx, knId, jobId),
3589
- jobTasks: (knId, jobId) => getJobTasks(ctx, knId, jobId),
3590
- jobDelete: (knId, ids) => deleteJobs(ctx, knId, ids),
3591
4024
  relationTypePaths: (knId, body) => relationTypePaths(ctx, knId, body),
3592
4025
  bknResources: () => listBknResources(ctx),
3593
4026
  createFromCatalog: (opts) => createFromCatalog(ctx, opts),
@@ -3603,12 +4036,19 @@ function kn(ctx) {
3603
4036
  const targets = collectIndexTargets(dir);
3604
4037
  const buildTasks = [];
3605
4038
  for (const t of targets) {
4039
+ if (!t.buildKey) {
4040
+ throw new Error(
4041
+ `Object type '${t.objectType}' declares a vector index but no build key; batch Vega builds require resource index_config.build_key_fields.`
4042
+ );
4043
+ }
4044
+ await configureResourceIndex(ctx, t.resourceId, {
4045
+ buildKeyFields: [t.buildKey],
4046
+ embeddingFields: t.embeddingFields,
4047
+ ...t.embeddingModel ?? opts.embeddingModel ? { embeddingModel: t.embeddingModel ?? opts.embeddingModel } : {}
4048
+ });
3606
4049
  const task = await createBuildTask(ctx, {
3607
4050
  resource_id: t.resourceId,
3608
- mode: "batch",
3609
- embedding_fields: t.embeddingFields,
3610
- ...t.buildKey ? { build_key_fields: [t.buildKey] } : {},
3611
- ...t.embeddingModel ?? opts.embeddingModel ? { embedding_model: t.embeddingModel ?? opts.embeddingModel } : {}
4051
+ mode: "batch"
3612
4052
  });
3613
4053
  buildTasks.push({
3614
4054
  objectType: t.objectType,
@@ -3663,10 +4103,9 @@ function deltaContent(chunk) {
3663
4103
  return typeof c === "string" ? c : "";
3664
4104
  }
3665
4105
  async function chatCompletionsStream(ctx, model, messages, onDelta) {
3666
- applyTls(ctx);
3667
4106
  const res = await authFetch(
3668
4107
  ctx,
3669
- () => fetch(`${ctx.baseUrl}${API}/chat/completions`, {
4108
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${API}/chat/completions`, {
3670
4109
  method: "POST",
3671
4110
  headers: {
3672
4111
  ...buildHeaders(ctx),
@@ -3725,6 +4164,23 @@ function deleteModels(ctx, kind, modelIds) {
3725
4164
  function testModel(ctx, kind, body) {
3726
4165
  return request(ctx, `${MANAGER}/${kind}/test`, { method: "POST", body });
3727
4166
  }
4167
+ function setDefaultLlm(ctx, modelId, isDefault = true) {
4168
+ return request(ctx, `${MANAGER}/llm/default/edit`, {
4169
+ method: "POST",
4170
+ body: { model_id: modelId, default: isDefault }
4171
+ });
4172
+ }
4173
+ function setDefaultSmallModel(ctx, modelId, isDefault = true) {
4174
+ return request(ctx, `${MANAGER}/small-model/set-default`, {
4175
+ method: "POST",
4176
+ body: { model_id: modelId, default: isDefault }
4177
+ });
4178
+ }
4179
+ function getDefaultSmallModel(ctx, modelType = "embedding") {
4180
+ return request(ctx, `${MANAGER}/small-model/get_default`, {
4181
+ query: { model_type: modelType }
4182
+ });
4183
+ }
3728
4184
  function rerank(ctx, model, query, documents) {
3729
4185
  return request(ctx, `${API}/small-model/reranker`, {
3730
4186
  method: "POST",
@@ -3743,7 +4199,9 @@ function models(ctx) {
3743
4199
  add: (body) => addModel(ctx, "llm", body),
3744
4200
  edit: (body) => editModel(ctx, "llm", body),
3745
4201
  delete: (modelIds) => deleteModels(ctx, "llm", modelIds),
3746
- test: (body) => testModel(ctx, "llm", body)
4202
+ test: (body) => testModel(ctx, "llm", body),
4203
+ /** Set (or clear) the system default LLM. */
4204
+ setDefault: (modelId, isDefault = true) => setDefaultLlm(ctx, modelId, isDefault)
3747
4205
  },
3748
4206
  small: {
3749
4207
  list: (opts) => listSmallModels(ctx, opts),
@@ -3753,7 +4211,11 @@ function models(ctx) {
3753
4211
  add: (body) => addModel(ctx, "small-model", body),
3754
4212
  edit: (body) => editModel(ctx, "small-model", body),
3755
4213
  delete: (modelIds) => deleteModels(ctx, "small-model", modelIds),
3756
- test: (body) => testModel(ctx, "small-model", body)
4214
+ test: (body) => testModel(ctx, "small-model", body),
4215
+ /** Set (or clear) the system default small model (type inferred from the model). */
4216
+ setDefault: (modelId, isDefault = true) => setDefaultSmallModel(ctx, modelId, isDefault),
4217
+ /** Get the system default small model for a type (default "embedding"). */
4218
+ getDefault: (modelType) => getDefaultSmallModel(ctx, modelType)
3757
4219
  }
3758
4220
  };
3759
4221
  }
@@ -3764,6 +4226,8 @@ function resources(ctx) {
3764
4226
  list: (opts) => listResources2(ctx, opts),
3765
4227
  get: (id) => getResource(ctx, id),
3766
4228
  delete: (id) => deleteResource(ctx, id),
4229
+ update: (id, patch) => updateResource(ctx, id, patch),
4230
+ configureIndex: (id, opts) => configureResourceIndex(ctx, id, opts),
3767
4231
  find: (name, opts) => findResource(ctx, name, opts),
3768
4232
  query: (id, opts) => queryResource(ctx, id, opts)
3769
4233
  };
@@ -3776,7 +4240,6 @@ import { basename as basename2, dirname as dirname3, resolve as resolve5 } from
3776
4240
  // src/api/skills.ts
3777
4241
  var BASE5 = "/api/agent-operator-integration/v1";
3778
4242
  async function registerSkillZip(ctx, bytes, opts = {}) {
3779
- applyTls(ctx);
3780
4243
  const form2 = new FormData();
3781
4244
  form2.set("file_type", "zip");
3782
4245
  form2.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
@@ -3784,7 +4247,7 @@ async function registerSkillZip(ctx, bytes, opts = {}) {
3784
4247
  if (opts.extendInfo) form2.set("extend_info", JSON.stringify(opts.extendInfo));
3785
4248
  const res = await authFetch(
3786
4249
  ctx,
3787
- () => fetch(`${ctx.baseUrl}${BASE5}/skills`, {
4250
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${BASE5}/skills`, {
3788
4251
  method: "POST",
3789
4252
  headers: buildHeaders(ctx),
3790
4253
  body: form2
@@ -3795,13 +4258,12 @@ async function registerSkillZip(ctx, bytes, opts = {}) {
3795
4258
  return text ? JSON.parse(text) : void 0;
3796
4259
  }
3797
4260
  async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip") {
3798
- applyTls(ctx);
3799
4261
  const form2 = new FormData();
3800
4262
  form2.set("file_type", "zip");
3801
4263
  form2.set("file", new Blob([bytes]), filename);
3802
4264
  const res = await authFetch(
3803
4265
  ctx,
3804
- () => fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
4266
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
3805
4267
  method: "PUT",
3806
4268
  headers: buildHeaders(ctx),
3807
4269
  body: form2
@@ -3812,10 +4274,9 @@ async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip"
3812
4274
  return text ? JSON.parse(text) : void 0;
3813
4275
  }
3814
4276
  async function downloadSkill(ctx, skillId) {
3815
- applyTls(ctx);
3816
4277
  const res = await authFetch(
3817
4278
  ctx,
3818
- () => fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
4279
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
3819
4280
  headers: buildHeaders(ctx)
3820
4281
  })
3821
4282
  );
@@ -3971,25 +4432,27 @@ import { basename as basename3 } from "path";
3971
4432
  var PATH = "/api/agent-operator-integration/v1/tool-box";
3972
4433
  var IMPEX = "/api/agent-operator-integration/v1/impex";
3973
4434
  async function exportConfig(ctx, id, type = "toolbox") {
3974
- applyTls(ctx);
3975
4435
  const res = await authFetch(
3976
4436
  ctx,
3977
- () => fetch(`${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, {
3978
- headers: buildHeaders(ctx)
3979
- })
4437
+ () => tlsFetch(
4438
+ ctx.insecure,
4439
+ `${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,
4440
+ {
4441
+ headers: buildHeaders(ctx)
4442
+ }
4443
+ )
3980
4444
  );
3981
4445
  const buf = new Uint8Array(await res.arrayBuffer());
3982
4446
  if (!res.ok) throw new HttpError(res.status, res.statusText, new TextDecoder().decode(buf));
3983
4447
  return buf;
3984
4448
  }
3985
4449
  async function importConfig(ctx, filePath, type = "toolbox") {
3986
- applyTls(ctx);
3987
4450
  const buf = await readFile2(filePath);
3988
4451
  const form2 = new FormData();
3989
4452
  form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
3990
4453
  const res = await authFetch(
3991
4454
  ctx,
3992
- () => fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
4455
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
3993
4456
  method: "POST",
3994
4457
  headers: buildHeaders(ctx),
3995
4458
  body: form2
@@ -4000,14 +4463,13 @@ async function importConfig(ctx, filePath, type = "toolbox") {
4000
4463
  return text ? JSON.parse(text) : text;
4001
4464
  }
4002
4465
  async function uploadTool(ctx, boxId, filePath, metadataType = "openapi") {
4003
- applyTls(ctx);
4004
4466
  const buf = await readFile2(filePath);
4005
4467
  const form2 = new FormData();
4006
4468
  form2.append("metadata_type", metadataType);
4007
4469
  form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
4008
4470
  const res = await authFetch(
4009
4471
  ctx,
4010
- () => fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
4472
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
4011
4473
  method: "POST",
4012
4474
  headers: buildHeaders(ctx),
4013
4475
  body: form2
@@ -4022,8 +4484,14 @@ function listToolboxes(ctx, opts = {}) {
4022
4484
  query: { keyword: opts.keyword || void 0, limit: opts.limit, offset: opts.offset ?? 0 }
4023
4485
  });
4024
4486
  }
4025
- function listTools2(ctx, boxId) {
4026
- 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
+ });
4027
4495
  }
4028
4496
  function createToolbox(ctx, opts) {
4029
4497
  return request(ctx, PATH, {
@@ -4082,7 +4550,7 @@ function setToolStatuses(ctx, boxId, updates) {
4082
4550
  function toolboxes(ctx) {
4083
4551
  return {
4084
4552
  list: (opts) => listToolboxes(ctx, opts),
4085
- tools: (boxId) => listTools2(ctx, boxId),
4553
+ tools: (boxId, opts) => listTools2(ctx, boxId, opts),
4086
4554
  create: (opts) => createToolbox(ctx, opts),
4087
4555
  delete: (boxId) => deleteToolbox(ctx, boxId),
4088
4556
  publish: (boxId) => setToolboxStatus(ctx, boxId, "published"),
@@ -4108,8 +4576,728 @@ function toolboxes(ctx) {
4108
4576
  };
4109
4577
  }
4110
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
+
4111
5294
  // src/api/trace.ts
4112
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";
4113
5301
  function isoToNanos(iso) {
4114
5302
  const ms = Date.parse(iso);
4115
5303
  if (Number.isNaN(ms)) return void 0;
@@ -4151,6 +5339,67 @@ async function getRawSpansByConversation(ctx, conversationId, opts = {}) {
4151
5339
  function traceSearch(ctx, body) {
4152
5340
  return request(ctx, SEARCH, { method: "POST", body });
4153
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
+ }
4154
5403
  async function getSpansByConversation(ctx, conversationId, opts = {}) {
4155
5404
  const agg = await request(ctx, SEARCH, {
4156
5405
  method: "POST",
@@ -4175,8 +5424,40 @@ async function getSpansByConversation(ctx, conversationId, opts = {}) {
4175
5424
  }) ?? {};
4176
5425
  return (spans.hits?.hits ?? []).map((h) => h._source ?? {});
4177
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
+ }
4178
5459
 
4179
- // src/trace-ai/claude-judge.ts
5460
+ // src/bkn-trace/claude-judge.ts
4180
5461
  import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
4181
5462
  var ClaudeJudgeError = class extends Error {
4182
5463
  constructor(message, reason) {
@@ -4236,10 +5517,10 @@ async function judgeJson(prompt, opts = {}) {
4236
5517
  child.stdout.on("data", (d) => {
4237
5518
  out += d;
4238
5519
  });
4239
- child.on("error", (err) => {
5520
+ child.on("error", (err2) => {
4240
5521
  clearTimeout(killer);
4241
5522
  reject(
4242
- err.code === "ENOENT" ? new ClaudeJudgeError("`claude` not found on PATH", "not_available") : err
5523
+ err2.code === "ENOENT" ? new ClaudeJudgeError("`claude` not found on PATH", "not_available") : err2
4243
5524
  );
4244
5525
  });
4245
5526
  child.on("close", (code) => {
@@ -4249,8 +5530,8 @@ async function judgeJson(prompt, opts = {}) {
4249
5530
  if (code !== 0) return reject(new ClaudeJudgeError(`claude exited ${code}`, "exit"));
4250
5531
  resolve7(out);
4251
5532
  });
4252
- child.stdin.on("error", (err) => {
4253
- if (err.code !== "EPIPE") reject(err);
5533
+ child.stdin.on("error", (err2) => {
5534
+ if (err2.code !== "EPIPE") reject(err2);
4254
5535
  });
4255
5536
  child.stdin.end(prompt);
4256
5537
  });
@@ -4258,7 +5539,7 @@ async function judgeJson(prompt, opts = {}) {
4258
5539
  return JSON.parse(extractJsonObject(text));
4259
5540
  }
4260
5541
 
4261
- // src/trace-ai/diagnose.ts
5542
+ // src/bkn-trace/diagnose.ts
4262
5543
  var KIND_MAP = {
4263
5544
  chat: "llm",
4264
5545
  text_completion: "llm",
@@ -4652,7 +5933,7 @@ function renderReportMarkdown(r) {
4652
5933
  return lines.join("\n");
4653
5934
  }
4654
5935
 
4655
- // src/trace-ai/eval-set.ts
5936
+ // src/bkn-trace/eval-set.ts
4656
5937
  function hashId(s) {
4657
5938
  let h = 5381;
4658
5939
  for (let i = 0; i < s.length; i++) h = h * 33 ^ s.charCodeAt(i);
@@ -4800,6 +6081,747 @@ async function runEvalSet(agentId, cases, deps) {
4800
6081
  };
4801
6082
  }
4802
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
+
4803
6825
  // src/resources/trace.ts
4804
6826
  async function semanticJudge(question, answer, reference) {
4805
6827
  const prompt = [
@@ -4846,6 +6868,32 @@ function trace(ctx) {
4846
6868
  return {
4847
6869
  /** Raw trace search (OpenSearch-style body). */
4848
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),
4849
6897
  /** All span source docs for a conversation. */
4850
6898
  spans: (conversationId, opts) => getSpansByConversation(ctx, conversationId, opts),
4851
6899
  diagnose: diagnoseOne,
@@ -4883,6 +6931,8 @@ function trace(ctx) {
4883
6931
  },
4884
6932
  /** Build eval cases from a loosely-shaped queries object/array. */
4885
6933
  evalSetBuild: (raw) => buildCasesFromQueries(raw),
6934
+ /** Validate BKN Trace phase-one fixture files or directories. */
6935
+ validateFixture: (path) => validateFixturePath(path),
4886
6936
  /**
4887
6937
  * Run an eval set against an agent: each case's query is sent to the agent,
4888
6938
  * the resulting trace is fetched, and assertions are checked. `llm` enables
@@ -4916,19 +6966,29 @@ function vega(ctx) {
4916
6966
  catalogs: (opts) => listCatalogs(ctx, opts),
4917
6967
  getCatalog: (id) => getCatalog(ctx, id),
4918
6968
  createCatalog: (req) => createCatalog(ctx, req),
6969
+ updateCatalog: (id, req) => updateCatalog(ctx, id, req),
4919
6970
  enableCatalog: (id) => enableCatalog(ctx, id),
6971
+ disableCatalog: (id) => disableCatalog(ctx, id),
6972
+ deleteCatalog: (id) => deleteCatalog(ctx, id),
6973
+ testCatalogConnection: (id) => testCatalogConnection(ctx, id),
4920
6974
  discoverCatalog: (id, wait = false) => discoverCatalog(ctx, id, wait),
4921
- catalogResources: (id, category) => listCatalogResources(ctx, id, category),
6975
+ catalogResources: (id, category, limit, offset) => listCatalogResources(ctx, id, category, limit, offset),
4922
6976
  catalogHealth: (ids) => catalogHealthStatus(ctx, ids),
4923
6977
  connectorTypes: () => listConnectorTypes(ctx),
4924
6978
  connectorType: (type) => getConnectorType(ctx, type),
6979
+ /** Run SQL / OpenSearch DSL directly against a data source. */
6980
+ sql: (body) => runSql(ctx, body),
4925
6981
  /** Build a resource's index. With `wait`, polls until terminal. */
4926
6982
  build: async (req, opts = {}) => {
4927
6983
  const task = await createBuildTask(ctx, req);
4928
6984
  if (!opts.wait) return task;
4929
6985
  return pollBuildTask(ctx, task.id, opts.timeoutMs ?? 3e5, opts.intervalMs ?? 2e3);
4930
6986
  },
4931
- buildStatus: (taskId) => getBuildTask(ctx, taskId)
6987
+ buildStatus: (taskId) => getBuildTask(ctx, taskId),
6988
+ buildTasks: (opts) => listBuildTasks(ctx, opts),
6989
+ deleteBuildTasks: (ids, opts) => deleteBuildTasks(ctx, ids, opts),
6990
+ startBuildTask: (taskId, opts) => startBuildTask(ctx, taskId, opts),
6991
+ stopBuildTask: (taskId) => stopBuildTask(ctx, taskId)
4932
6992
  };
4933
6993
  }
4934
6994
  async function pollBuildTask(ctx, taskId, timeoutMs, intervalMs) {
@@ -4946,7 +7006,7 @@ function sleep(ms) {
4946
7006
  }
4947
7007
 
4948
7008
  // src/api/call.ts
4949
- import { readFileSync as readFileSync4 } from "fs";
7009
+ import { readFileSync as readFileSync5 } from "fs";
4950
7010
  function parseHeader(raw) {
4951
7011
  const idx = raw.indexOf(":");
4952
7012
  if (idx <= 0) return null;
@@ -4964,7 +7024,6 @@ function resolveUrl(ctx, path) {
4964
7024
  return path.startsWith("http") ? path : `${ctx.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
4965
7025
  }
4966
7026
  async function rawCall(ctx, path, opts = {}) {
4967
- applyTls(ctx);
4968
7027
  const url = resolveUrl(ctx, path);
4969
7028
  const extra = {};
4970
7029
  for (const h of opts.header ?? []) {
@@ -4976,7 +7035,7 @@ async function rawCall(ctx, path, opts = {}) {
4976
7035
  const fd = new FormData();
4977
7036
  for (const field of opts.form) {
4978
7037
  const [key, value, isFile] = parseFormField(field);
4979
- if (isFile) fd.append(key, new Blob([readFileSync4(value)]), value.split("/").pop());
7038
+ if (isFile) fd.append(key, new Blob([readFileSync5(value)]), value.split("/").pop());
4980
7039
  else fd.append(key, value);
4981
7040
  }
4982
7041
  body = fd;
@@ -4993,7 +7052,12 @@ async function rawCall(ctx, path, opts = {}) {
4993
7052
  try {
4994
7053
  const res = await authFetch(
4995
7054
  ctx,
4996
- () => fetch(url, { method, headers: headersFor(), body, signal: controller.signal })
7055
+ () => tlsFetch(ctx.insecure, url, {
7056
+ method,
7057
+ headers: headersFor(),
7058
+ body,
7059
+ signal: controller.signal
7060
+ })
4997
7061
  );
4998
7062
  return { status: res.status, statusText: res.statusText, body: await res.text() };
4999
7063
  } finally {
@@ -5001,6 +7065,53 @@ async function rawCall(ctx, path, opts = {}) {
5001
7065
  }
5002
7066
  }
5003
7067
 
7068
+ // src/api/app-keys.ts
7069
+ var ME = "/api/safe/v1/me/api-keys";
7070
+ var ADMIN2 = "/api/safe/v1/admin/api-keys";
7071
+ function listMyApiKeys(ctx) {
7072
+ return request(ctx, ME);
7073
+ }
7074
+ function createMyApiKey(ctx, input) {
7075
+ return request(ctx, ME, {
7076
+ method: "POST",
7077
+ body: {
7078
+ name: input.name,
7079
+ ...input.expiresAt ? { expires_at: input.expiresAt } : {},
7080
+ ...input.neverExpire ? { never_expire: true } : {}
7081
+ }
7082
+ });
7083
+ }
7084
+ async function revokeMyApiKey(ctx, id) {
7085
+ await request(ctx, `${ME}/${encodeURIComponent(id)}`, { method: "DELETE" });
7086
+ }
7087
+ function regenerateMyApiKey(ctx, id) {
7088
+ return request(ctx, `${ME}/${encodeURIComponent(id)}/regenerate`, { method: "POST" });
7089
+ }
7090
+ function listApiKeysAdmin(ctx, ownerId) {
7091
+ return request(ctx, ADMIN2, { query: { owner_id: ownerId || void 0 } });
7092
+ }
7093
+ async function revokeApiKeyAdmin(ctx, id) {
7094
+ await request(ctx, `${ADMIN2}/${encodeURIComponent(id)}`, { method: "DELETE" });
7095
+ }
7096
+
7097
+ // src/resources/app-keys.ts
7098
+ function appKeys(ctx) {
7099
+ return {
7100
+ /** List the caller's own keys (no secrets). */
7101
+ list: () => listMyApiKeys(ctx),
7102
+ /** Issue a key — the result's `key` is the plaintext, shown only once. */
7103
+ create: (input) => createMyApiKey(ctx, input),
7104
+ /** Revoke one of the caller's keys (immediate). */
7105
+ revoke: (id) => revokeMyApiKey(ctx, id),
7106
+ /** Rotate a key in place — new plaintext (shown once); old secret dies now. */
7107
+ regenerate: (id) => regenerateMyApiKey(ctx, id),
7108
+ /** Admin: list all keys, or one owner's (adds `owner_user_id`). */
7109
+ adminList: (ownerId) => listApiKeysAdmin(ctx, ownerId),
7110
+ /** Admin: revoke any key. */
7111
+ adminRevoke: (id) => revokeApiKeyAdmin(ctx, id)
7112
+ };
7113
+ }
7114
+
5004
7115
  // src/client.ts
5005
7116
  function createClient(opts = {}) {
5006
7117
  const ctx = resolveContext(opts);
@@ -5016,6 +7127,7 @@ function createClient(opts = {}) {
5016
7127
  toolboxes: toolboxes(ctx),
5017
7128
  trace: trace(ctx),
5018
7129
  admin: admin(ctx),
7130
+ appKeys: appKeys(ctx),
5019
7131
  vega: vega(ctx),
5020
7132
  call: (path, callOpts) => rawCall(ctx, path, callOpts)
5021
7133
  };
@@ -5024,7 +7136,6 @@ function createClient(opts = {}) {
5024
7136
  // src/resources/auth.ts
5025
7137
  var auth_exports = {};
5026
7138
  __export(auth_exports, {
5027
- attachNoAuth: () => attachNoAuth,
5028
7139
  attachToken: () => attachToken,
5029
7140
  currentToken: () => currentToken,
5030
7141
  currentTokenFresh: () => currentTokenFresh,
@@ -5062,7 +7173,8 @@ function attachToken(baseUrl, accessToken, opts = {}) {
5062
7173
  accessToken,
5063
7174
  refreshToken: opts.refreshToken,
5064
7175
  idToken: opts.idToken,
5065
- tlsInsecure: opts.insecure,
7176
+ // Remember `-k` so a self-signed platform needn't repeat it every command.
7177
+ tlsInsecure: opts.insecure ? true : void 0,
5066
7178
  // Prefer the account the user typed (-u); device tokens carry no username.
5067
7179
  username: opts.username ?? decodeJwt(opts.idToken ?? accessToken)?.preferred_username
5068
7180
  };
@@ -5070,33 +7182,40 @@ function attachToken(baseUrl, accessToken, opts = {}) {
5070
7182
  setActivePlatform(url);
5071
7183
  return { baseUrl: url, userId, username: usernameOf(token) };
5072
7184
  }
5073
- function attachNoAuth(baseUrl, opts = {}) {
5074
- const url = normalize(baseUrl);
5075
- writeToken(url, { baseUrl: url, accessToken: "", noAuth: true, tlsInsecure: opts.insecure });
5076
- setActivePlatform(url);
5077
- return { baseUrl: url, noAuth: true };
7185
+ function targetUser(baseUrl, userOrName) {
7186
+ if (!userOrName) return activeUserId(baseUrl);
7187
+ const id = findUserId(baseUrl, userOrName);
7188
+ if (!id) {
7189
+ const known = usersOfPlatform(baseUrl).map((u) => u.username ?? u.userId).join(", ");
7190
+ throw new InputError(
7191
+ `No saved user '${userOrName}' on ${baseUrl}. Saved: ${known || "(none)"}.`
7192
+ );
7193
+ }
7194
+ return id;
5078
7195
  }
5079
- function status() {
7196
+ function status(opts = {}) {
5080
7197
  const baseUrl = activePlatform();
5081
7198
  if (!baseUrl) return { hasToken: false };
5082
- const token = readToken(baseUrl);
7199
+ const userId = targetUser(baseUrl, opts.user);
7200
+ const token = readToken(baseUrl, userId);
5083
7201
  return {
5084
7202
  baseUrl,
5085
- userId: activeUserId(baseUrl),
7203
+ userId,
5086
7204
  hasToken: token !== void 0,
5087
7205
  username: usernameOf(token),
5088
7206
  expired: token ? isExpired(decodeJwt(token.accessToken)) : void 0
5089
7207
  };
5090
7208
  }
5091
- function currentToken() {
7209
+ function currentToken(opts = {}) {
5092
7210
  const baseUrl = activePlatform();
5093
- const token = baseUrl ? readToken(baseUrl) : void 0;
7211
+ const token = baseUrl ? readToken(baseUrl, targetUser(baseUrl, opts.user)) : void 0;
5094
7212
  if (!token) throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
5095
7213
  return token.accessToken;
5096
7214
  }
5097
- async function currentTokenFresh() {
7215
+ async function currentTokenFresh(opts = {}) {
5098
7216
  const baseUrl = activePlatform();
5099
- const token = baseUrl ? readToken(baseUrl) : void 0;
7217
+ const userId = baseUrl ? targetUser(baseUrl, opts.user) : void 0;
7218
+ const token = baseUrl ? readToken(baseUrl, userId) : void 0;
5100
7219
  if (!baseUrl || !token) {
5101
7220
  throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
5102
7221
  }
@@ -5105,22 +7224,27 @@ async function currentTokenFresh() {
5105
7224
  const needsRefresh = decodable ? isExpired(claims) : true;
5106
7225
  if (token.refreshToken && needsRefresh) {
5107
7226
  try {
5108
- const t = await refreshAccessToken(baseUrl, token.refreshToken);
5109
- writeToken(baseUrl, {
5110
- ...token,
5111
- accessToken: t.accessToken,
5112
- refreshToken: t.refreshToken ?? token.refreshToken,
5113
- idToken: t.idToken ?? token.idToken
5114
- });
7227
+ const t = await refreshAccessToken(baseUrl, token.refreshToken, void 0, opts.insecure);
7228
+ writeToken(
7229
+ baseUrl,
7230
+ {
7231
+ ...token,
7232
+ accessToken: t.accessToken,
7233
+ refreshToken: t.refreshToken ?? token.refreshToken,
7234
+ idToken: t.idToken ?? token.idToken
7235
+ },
7236
+ { setActive: !opts.user }
7237
+ );
5115
7238
  return t.accessToken;
5116
7239
  } catch {
5117
7240
  }
5118
7241
  }
5119
7242
  return token.accessToken;
5120
7243
  }
5121
- function whoami() {
7244
+ function whoami(opts = {}) {
5122
7245
  const baseUrl = activePlatform();
5123
- const token = baseUrl ? readToken(baseUrl) : void 0;
7246
+ const userId = baseUrl ? targetUser(baseUrl, opts.user) : void 0;
7247
+ const token = baseUrl ? readToken(baseUrl, userId) : void 0;
5124
7248
  const claims = decodeJwt(token?.idToken ?? "") ?? decodeJwt(token?.accessToken ?? "");
5125
7249
  if (!claims) {
5126
7250
  throw new InputError(
@@ -5130,7 +7254,7 @@ function whoami() {
5130
7254
  return {
5131
7255
  ...claims,
5132
7256
  baseUrl: baseUrl ?? void 0,
5133
- userId: baseUrl ? activeUserId(baseUrl) : void 0,
7257
+ userId,
5134
7258
  username: usernameOf(token)
5135
7259
  };
5136
7260
  }
@@ -5194,7 +7318,6 @@ export {
5194
7318
  formatError,
5195
7319
  isHeadless,
5196
7320
  openBrowser,
5197
- fetchAuthStatus,
5198
7321
  deviceLogin,
5199
7322
  credentialDeviceLogin,
5200
7323
  request,
@@ -5209,6 +7332,7 @@ export {
5209
7332
  writePlatformConfig,
5210
7333
  resolveContext,
5211
7334
  getUserSafe,
7335
+ changePasswordSafe,
5212
7336
  admin,
5213
7337
  agents,
5214
7338
  context,
@@ -5221,11 +7345,12 @@ export {
5221
7345
  skills,
5222
7346
  toolboxes,
5223
7347
  renderReportMarkdown,
7348
+ validateFixturePath,
7349
+ TraceSession,
5224
7350
  trace,
5225
7351
  vega,
5226
7352
  createClient,
5227
7353
  attachToken,
5228
- attachNoAuth,
5229
7354
  status,
5230
7355
  currentToken,
5231
7356
  currentTokenFresh,
@@ -5238,4 +7363,4 @@ export {
5238
7363
  exportCreds,
5239
7364
  auth_exports
5240
7365
  };
5241
- //# sourceMappingURL=chunk-BFE4SU3V.js.map
7366
+ //# sourceMappingURL=chunk-LH3ONZGQ.js.map