@zanii/blackbox 0.21.2 → 0.23.0

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.
Files changed (95) hide show
  1. package/README.md +1 -0
  2. package/dist/actions/index.d.ts +29 -0
  3. package/dist/actions/index.js +114 -0
  4. package/dist/admission/index.d.ts +30 -0
  5. package/dist/admission/index.js +106 -0
  6. package/dist/analysis/accepted.d.ts +26 -0
  7. package/dist/analysis/accepted.js +61 -0
  8. package/dist/analysis/detectors.js +12 -9
  9. package/dist/analysis/faults.js +71 -0
  10. package/dist/analysis/grounding.js +5 -9
  11. package/dist/analysis/guard.d.ts +72 -0
  12. package/dist/analysis/guard.js +240 -0
  13. package/dist/analysis/hallucination.js +2 -2
  14. package/dist/analysis/index.d.ts +21 -1
  15. package/dist/analysis/index.js +101 -19
  16. package/dist/analysis/injection.d.ts +2 -1
  17. package/dist/analysis/injection.js +16 -16
  18. package/dist/analysis/landing.js +60 -14
  19. package/dist/analysis/plan-checks.d.ts +55 -0
  20. package/dist/analysis/plan-checks.js +748 -0
  21. package/dist/analysis/taxonomy.js +1 -0
  22. package/dist/analysis/waste.js +2 -3
  23. package/dist/approvals/index.d.ts +12 -3
  24. package/dist/approvals/index.js +36 -10
  25. package/dist/authority/index.js +3 -3
  26. package/dist/aviation/index.d.ts +422 -0
  27. package/dist/aviation/index.js +1014 -0
  28. package/dist/aviation/qrh.d.ts +4 -0
  29. package/dist/aviation/qrh.js +171 -0
  30. package/dist/budgets/index.d.ts +63 -0
  31. package/dist/budgets/index.js +161 -0
  32. package/dist/cli.js +199 -24
  33. package/dist/compliance/index.d.ts +2 -0
  34. package/dist/compliance/index.js +26 -2
  35. package/dist/cost/batch.d.ts +26 -0
  36. package/dist/cost/batch.js +173 -0
  37. package/dist/cost/index.d.ts +21 -0
  38. package/dist/cost/index.js +115 -30
  39. package/dist/cv/index.js +1 -1
  40. package/dist/duty/index.js +2 -1
  41. package/dist/evals/index.d.ts +233 -0
  42. package/dist/evals/index.js +488 -0
  43. package/dist/evidence/index.d.ts +9 -0
  44. package/dist/evidence/index.js +50 -0
  45. package/dist/federation/index.d.ts +9 -0
  46. package/dist/federation/index.js +8 -1
  47. package/dist/finance/index.js +3 -4
  48. package/dist/fleet/index.d.ts +25 -0
  49. package/dist/fleet/index.js +57 -17
  50. package/dist/fleet/insights.js +4 -1
  51. package/dist/health/index.js +2 -2
  52. package/dist/hooks/ai-sdk.d.ts +17 -19
  53. package/dist/hooks/ai-sdk.js +13 -7
  54. package/dist/hooks/index.d.ts +1 -0
  55. package/dist/hooks/index.js +1 -0
  56. package/dist/hooks/mastra.d.ts +26 -0
  57. package/dist/hooks/mastra.js +63 -0
  58. package/dist/index.d.ts +24 -9
  59. package/dist/index.js +25 -9
  60. package/dist/mcp-requests/index.d.ts +26 -0
  61. package/dist/mcp-requests/index.js +154 -0
  62. package/dist/mcp-server/index.d.ts +12 -0
  63. package/dist/mcp-server/index.js +84 -2
  64. package/dist/mcp-wrap/index.js +8 -4
  65. package/dist/otlp/index.d.ts +155 -0
  66. package/dist/otlp/index.js +468 -0
  67. package/dist/otlp/protobuf.d.ts +7 -0
  68. package/dist/otlp/protobuf.js +314 -0
  69. package/dist/packs/index.d.ts +2 -0
  70. package/dist/packs/index.js +5 -1
  71. package/dist/pdf/arabic.d.ts +45 -0
  72. package/dist/pdf/arabic.js +373 -0
  73. package/dist/pdf/index.d.ts +77 -0
  74. package/dist/pdf/index.js +673 -0
  75. package/dist/policy/index.d.ts +0 -4
  76. package/dist/policy/index.js +13 -5
  77. package/dist/realtime/index.d.ts +33 -0
  78. package/dist/realtime/index.js +204 -0
  79. package/dist/reconcile/record.d.ts +14 -0
  80. package/dist/reconcile/record.js +161 -66
  81. package/dist/session/index.d.ts +43 -0
  82. package/dist/session/index.js +83 -0
  83. package/dist/undo/index.js +3 -3
  84. package/dist/verify/anchor.d.ts +5 -1
  85. package/dist/verify/anchor.js +13 -4
  86. package/dist/verify/chain.d.ts +4 -0
  87. package/dist/verify/chain.js +4 -3
  88. package/dist/verify/envelope.d.ts +1 -1
  89. package/dist/verify/envelope.js +5 -0
  90. package/dist/version.d.ts +1 -1
  91. package/dist/version.js +1 -1
  92. package/dist/walls/index.js +2 -2
  93. package/dist/warehouse/index.d.ts +62 -0
  94. package/dist/warehouse/index.js +142 -0
  95. package/package.json +6 -3
@@ -0,0 +1,26 @@
1
+ import type { Json } from "../verify/envelope.ts";
2
+ type Meta = {
3
+ [key: string]: Json;
4
+ };
5
+ /** The server's request methods and the event kind each is recorded as. */
6
+ export declare const SERVER_REQUESTS: Readonly<Record<string, ServerRequestKind>>;
7
+ export type ServerRequestKind = "tool.sampling" | "tool.elicitation" | "tool.roots";
8
+ /** A JSON-RPC id as recorded: a string of at most 256 characters, or a safe integer. */
9
+ export declare function rpcIdOf(id: unknown): string | number | undefined;
10
+ /** The field names that ask for a credential: the name, lowercased with everything but letters
11
+ * and digits removed, contains one of the credential words, or is `pin`. In the order given. */
12
+ export declare function secretFields(names: readonly string[]): string[];
13
+ /**
14
+ * A server → client message that is one of the server's requests: its kind and `meta`, or null
15
+ * for anything else (a notification, a response, another method, a batch, a request without a
16
+ * usable id).
17
+ */
18
+ export declare function serverRequestMeta(message: unknown): {
19
+ kind: ServerRequestKind;
20
+ meta: Meta;
21
+ } | null;
22
+ /** The client's reply to a server request of `method`: its `meta` (spec/mcp.md §6). */
23
+ export declare function serverReplyMeta(method: string, message: unknown): Meta;
24
+ /** Whether a client → server message is a reply (a JSON-RPC response) rather than a request. */
25
+ export declare function isRpcResponse(message: unknown): boolean;
26
+ export {};
@@ -0,0 +1,154 @@
1
+ // MCP server-to-client requests (spec/mcp.md §6): `sampling/createMessage`, `elicitation/create` and
2
+ // `roots/list`, and the client's replies, as their own event kinds. Pure functions, pinned by
3
+ // spec/vectors/mcp-requests.json. Mirrors sdks/python/src/zanii_blackbox/mcp_requests.py.
4
+ /** The server's request methods and the event kind each is recorded as. */
5
+ export const SERVER_REQUESTS = {
6
+ "sampling/createMessage": "tool.sampling",
7
+ "elicitation/create": "tool.elicitation",
8
+ "roots/list": "tool.roots",
9
+ };
10
+ const obj = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
11
+ const str = (v, max) => (typeof v === "string" && v.length <= max ? v : undefined);
12
+ const count = (v) => (Array.isArray(v) ? v.length : undefined);
13
+ const nonNegative = (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0 ? v : undefined;
14
+ const put = (meta, key, value) => {
15
+ if (value !== undefined)
16
+ meta[key] = value;
17
+ };
18
+ const keys = (v) => {
19
+ const o = obj(v);
20
+ return Object.keys(o)
21
+ .slice(0, 50)
22
+ .map((k) => k.slice(0, 64));
23
+ };
24
+ /** A JSON-RPC id as recorded: a string of at most 256 characters, or a safe integer. */
25
+ export function rpcIdOf(id) {
26
+ if (typeof id === "string" && id.length <= 256)
27
+ return id;
28
+ if (typeof id === "number" && Number.isSafeInteger(id))
29
+ return id;
30
+ return undefined;
31
+ }
32
+ // ponytail: a name list, not a classifier; a field named for a credential is what a phishing
33
+ // elicitation asks for. Extend the list when a real one slips through.
34
+ const SECRET_NAMES = [
35
+ "password",
36
+ "passwd",
37
+ "passphrase",
38
+ "secret",
39
+ "token",
40
+ "apikey",
41
+ "accesskey",
42
+ "privatekey",
43
+ "credential",
44
+ "otp",
45
+ "totp",
46
+ "mfacode",
47
+ "pincode",
48
+ "cvv",
49
+ "cvc",
50
+ "cardnumber",
51
+ "iban",
52
+ "ssn",
53
+ "seedphrase",
54
+ "mnemonic",
55
+ ];
56
+ /** The field names that ask for a credential: the name, lowercased with everything but letters
57
+ * and digits removed, contains one of the credential words, or is `pin`. In the order given. */
58
+ export function secretFields(names) {
59
+ return names.filter((n) => {
60
+ const plain = n.toLowerCase().replace(/[^a-z0-9]/g, "");
61
+ return plain === "pin" || SECRET_NAMES.some((w) => plain.includes(w));
62
+ });
63
+ }
64
+ /**
65
+ * A server → client message that is one of the server's requests: its kind and `meta`, or null
66
+ * for anything else (a notification, a response, another method, a batch, a request without a
67
+ * usable id).
68
+ */
69
+ export function serverRequestMeta(message) {
70
+ const m = obj(message);
71
+ const method = typeof m.method === "string" ? m.method : "";
72
+ const kind = Object.hasOwn(SERVER_REQUESTS, method) ? SERVER_REQUESTS[method] : undefined;
73
+ const id = rpcIdOf(m.id);
74
+ if (!kind || id === undefined)
75
+ return null;
76
+ const p = obj(m.params);
77
+ const meta = { phase: "request", method, rpc_id: id };
78
+ if (kind === "tool.sampling") {
79
+ put(meta, "max_tokens", nonNegative(p.maxTokens));
80
+ put(meta, "messages", count(p.messages));
81
+ const include = str(p.includeContext, 32);
82
+ if (include === "none" || include === "thisServer" || include === "allServers")
83
+ meta.include_context = include;
84
+ const hints = obj(p.modelPreferences).hints;
85
+ if (Array.isArray(hints)) {
86
+ const names = hints
87
+ .map((h) => str(obj(h).name, 128))
88
+ .filter((n) => n !== undefined)
89
+ .slice(0, 8);
90
+ if (names.length)
91
+ meta.model_hints = names;
92
+ }
93
+ }
94
+ else if (kind === "tool.elicitation") {
95
+ put(meta, "mode", str(p.mode, 32));
96
+ const fields = keys(obj(p.requestedSchema).properties);
97
+ if (fields.length)
98
+ meta.fields = fields;
99
+ const secret = secretFields(fields);
100
+ if (secret.length)
101
+ meta.secret_fields = secret;
102
+ }
103
+ return { kind, meta };
104
+ }
105
+ /** Flattens a usage object into integer fields (`{a: {b: 1}}` → `{a_b: 1}`), at most 64. */
106
+ function flatUsage(usage) {
107
+ const out = {};
108
+ const walk = (o, prefix, depth) => {
109
+ for (const [k, v] of Object.entries(o)) {
110
+ if (Object.keys(out).length >= 64)
111
+ return;
112
+ const key = (prefix + k).slice(0, 128);
113
+ if (typeof v === "number" && Number.isSafeInteger(v) && v >= 0)
114
+ out[key] = v;
115
+ else if (depth < 3 && typeof v === "object" && v !== null && !Array.isArray(v))
116
+ walk(v, `${key}_`, depth + 1);
117
+ }
118
+ };
119
+ walk(obj(usage), "", 0);
120
+ return Object.keys(out).length ? out : undefined;
121
+ }
122
+ /** The client's reply to a server request of `method`: its `meta` (spec/mcp.md §6). */
123
+ export function serverReplyMeta(method, message) {
124
+ const m = obj(message);
125
+ const meta = { phase: "reply", method };
126
+ put(meta, "rpc_id", rpcIdOf(m.id));
127
+ const code = obj(m.error).code;
128
+ if (typeof code === "number" && Number.isSafeInteger(code))
129
+ meta.rpc_error = code;
130
+ if (!("result" in m))
131
+ return meta;
132
+ const r = obj(m.result);
133
+ if (method === "sampling/createMessage") {
134
+ put(meta, "model", str(r.model, 256));
135
+ put(meta, "stop_reason", str(r.stopReason, 64));
136
+ put(meta, "usage", flatUsage(r.usage));
137
+ }
138
+ else if (method === "elicitation/create") {
139
+ const action = str(r.action, 16);
140
+ if (action === "accept" || action === "decline" || action === "cancel")
141
+ meta.action = action;
142
+ const fields = keys(r.content);
143
+ if (fields.length)
144
+ meta.fields = fields;
145
+ }
146
+ else if (method === "roots/list")
147
+ put(meta, "roots", count(r.roots));
148
+ return meta;
149
+ }
150
+ /** Whether a client → server message is a reply (a JSON-RPC response) rather than a request. */
151
+ export function isRpcResponse(message) {
152
+ const m = obj(message);
153
+ return "id" in m && !("method" in m) && ("result" in m || "error" in m);
154
+ }
@@ -80,6 +80,18 @@ type Result = {
80
80
  export declare function callTool(get: Get, name: unknown, args: unknown): Promise<Result>;
81
81
  /** Answers one JSON-RPC line; undefined for a notification. */
82
82
  export declare function handleLine(get: Get, line: string): Promise<object | undefined>;
83
+ /** Answers one parsed JSON-RPC message; undefined for a notification. */
84
+ export declare function handleMessage(get: Get, parsed: unknown): Promise<object | undefined>;
83
85
  /** Serves stdin → stdout until stdin closes; one request at a time, in order. */
84
86
  export declare function serveMcp(get: Get): Promise<number>;
87
+ /**
88
+ * spec/mcp-server.md §HTTP: the same server over MCP Streamable HTTP, at `/mcp`. Each POST is
89
+ * answered on its own, so requests run concurrently. The caller's bearer key (an admin or tenant
90
+ * key of the Blackbox server) is the key every tool call reads with: the server holds none.
91
+ */
92
+ export declare function serveMcpHttp(getFor: (key: string) => Get, options: {
93
+ host: string;
94
+ port: number;
95
+ onListening?: (url: string) => void;
96
+ }): Promise<number>;
85
97
  export {};
@@ -1,5 +1,7 @@
1
- // The Blackbox MCP server (spec/mcp-server.md): read-only tools over the gateway's API, on stdio.
1
+ // The Blackbox MCP server (spec/mcp-server.md): read-only tools over the gateway's API, on stdio or
2
+ // Streamable HTTP.
2
3
  // Mirrors sdks/python/src/zanii_blackbox/mcp_server.py.
4
+ import { createServer } from "node:http";
3
5
  import { createInterface } from "node:readline";
4
6
  const SESSION_ID = /^ses_[A-Za-z0-9]{16,64}$/;
5
7
  const sid = {
@@ -172,6 +174,7 @@ export async function callTool(get, name, args) {
172
174
  : doc;
173
175
  return text(JSON.stringify(out, null, 2));
174
176
  }
177
+ const PARSE_ERROR = { jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } };
175
178
  /** Answers one JSON-RPC line; undefined for a notification. */
176
179
  export async function handleLine(get, line) {
177
180
  let msg;
@@ -179,8 +182,13 @@ export async function handleLine(get, line) {
179
182
  msg = JSON.parse(line);
180
183
  }
181
184
  catch {
182
- return { jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } };
185
+ return PARSE_ERROR;
183
186
  }
187
+ return handleMessage(get, msg);
188
+ }
189
+ /** Answers one parsed JSON-RPC message; undefined for a notification. */
190
+ export async function handleMessage(get, parsed) {
191
+ const msg = parsed;
184
192
  if (typeof msg !== "object" || msg === null || !("id" in msg))
185
193
  return undefined;
186
194
  const reply = (result) => ({ jsonrpc: "2.0", id: msg.id, result });
@@ -214,3 +222,77 @@ export async function serveMcp(get) {
214
222
  }
215
223
  return 0;
216
224
  }
225
+ /**
226
+ * spec/mcp-server.md §HTTP: the same server over MCP Streamable HTTP, at `/mcp`. Each POST is
227
+ * answered on its own, so requests run concurrently. The caller's bearer key (an admin or tenant
228
+ * key of the Blackbox server) is the key every tool call reads with: the server holds none.
229
+ */
230
+ export function serveMcpHttp(getFor, options) {
231
+ const server = createServer((req, res) => void answerHttp(getFor, req, res));
232
+ return new Promise((resolve, reject) => {
233
+ server.once("error", reject);
234
+ server.listen(options.port, options.host, () => {
235
+ const { port } = server.address();
236
+ const host = options.host.includes(":") ? `[${options.host}]` : options.host;
237
+ options.onListening?.(`http://${host}:${port}/mcp`);
238
+ });
239
+ const stop = () => {
240
+ server.close(() => resolve(0));
241
+ server.closeAllConnections();
242
+ };
243
+ process.once("SIGINT", stop);
244
+ process.once("SIGTERM", stop);
245
+ });
246
+ }
247
+ const MAX_BODY = 1024 * 1024;
248
+ const BEARER = /^Bearer ([\x21-\x7e]{1,512})$/;
249
+ async function answerHttp(getFor, req, res) {
250
+ req.on("error", () => { });
251
+ res.on("error", () => { });
252
+ const send = (status, body, headers = {}) => void res
253
+ .writeHead(status, body ? { "content-type": "application/json", ...headers } : headers)
254
+ .end(body ? JSON.stringify(body) : undefined);
255
+ const fail = (status, message, headers = {}) => send(status, { jsonrpc: "2.0", id: null, error: { code: -32001, message } }, headers);
256
+ if ((req.url ?? "").split("?")[0] !== "/mcp")
257
+ return fail(404, "not found: the endpoint is /mcp");
258
+ // MCP Streamable HTTP: a browser page must not reach a local server (DNS rebinding)
259
+ const origin = req.headers.origin;
260
+ if (origin !== undefined && !sameHost(origin, req.headers.host))
261
+ return fail(403, "bad origin");
262
+ const auth = BEARER.exec(req.headers.authorization ?? "");
263
+ if (!auth?.[1])
264
+ return fail(401, "a Blackbox API key is required (Authorization: Bearer)", {
265
+ "www-authenticate": 'Bearer realm="blackbox"',
266
+ });
267
+ if (req.method !== "POST")
268
+ return fail(405, "only POST is served", { allow: "POST" });
269
+ const parts = [];
270
+ let size = 0;
271
+ for await (const chunk of req) {
272
+ size += chunk.length;
273
+ if (size > MAX_BODY)
274
+ return fail(413, "request body too large");
275
+ parts.push(chunk);
276
+ }
277
+ let parsed;
278
+ try {
279
+ parsed = JSON.parse(Buffer.concat(parts).toString("utf8"));
280
+ }
281
+ catch {
282
+ return send(400, PARSE_ERROR);
283
+ }
284
+ const get = getFor(auth[1]);
285
+ const many = Array.isArray(parsed);
286
+ const replies = (await Promise.all((many ? parsed : [parsed]).map((m) => handleMessage(get, m)))).filter((r) => r !== undefined);
287
+ if (replies.length === 0)
288
+ return send(202, null); // notifications only
289
+ send(200, many ? replies : replies[0]);
290
+ }
291
+ function sameHost(origin, host) {
292
+ try {
293
+ return new URL(origin).host === host;
294
+ }
295
+ catch {
296
+ return false;
297
+ }
298
+ }
@@ -118,8 +118,9 @@ export async function mcpWrap(options) {
118
118
  ? `blackbox mcp-wrap: a ${d} message was not recorded, so not relayed: ${failure}\n`
119
119
  : `blackbox mcp-wrap: a ${d} message was refused by the gateway's policy\n`);
120
120
  const reply = refusal(d, line, failure);
121
+ // spec/mcp.md §6: a server's own request refused by policy is answered to the server
121
122
  if (reply)
122
- await write(stdout, reply);
123
+ await write(reply.toServer ? child.stdin : stdout, reply.bytes);
123
124
  });
124
125
  };
125
126
  const done = (d, source) => new Promise((resolve) => {
@@ -145,7 +146,8 @@ export async function mcpWrap(options) {
145
146
  agent.destroy();
146
147
  return code;
147
148
  }
148
- /** What the agent gets instead of a message that couldn't be recorded (spec/mcp.md §5). */
149
+ /** What the agent gets instead of a message that couldn't be recorded (spec/mcp.md §5), or, for
150
+ * a server's own request the gateway's policy refused, what the server gets (§6). */
149
151
  function refusal(d, line, reason) {
150
152
  let message;
151
153
  try {
@@ -158,12 +160,14 @@ function refusal(d, line, reason) {
158
160
  return null;
159
161
  const isRequest = "method" in message;
160
162
  const isResponse = "result" in message || "error" in message;
161
- if ((d === "client" && !isRequest) || (d === "server" && !isResponse))
163
+ const toServer = d === "server" && isRequest && typeof reason !== "string";
164
+ if (!toServer && ((d === "client" && !isRequest) || (d === "server" && !isResponse)))
162
165
  return null;
163
166
  const error = typeof reason === "string"
164
167
  ? { code: -32001, message: `blackbox: not recorded, so not relayed (${reason})` }
165
168
  : reason.rpcError;
166
- return Buffer.from(`${JSON.stringify({ jsonrpc: "2.0", id: message.id, error })}\n`);
169
+ const bytes = Buffer.from(`${JSON.stringify({ jsonrpc: "2.0", id: message.id, error })}\n`);
170
+ return { bytes, toServer };
167
171
  }
168
172
  function quoteWindows(arg) {
169
173
  return /^[\w\-./:=@\\]+$/.test(arg) ? arg : `"${arg.replace(/"/g, '""')}"`;
@@ -28,6 +28,10 @@ export declare function toOtlp(lines: readonly string[], options?: {
28
28
  }[];
29
29
  }[];
30
30
  };
31
+ type AnyAttr = {
32
+ key: string;
33
+ value: unknown;
34
+ };
31
35
  /** One event for `POST /v1/sessions/:id/sdk-events`, numbered by the server (spec/sdk.md §1). */
32
36
  export interface OtlpEvent {
33
37
  event_id: string;
@@ -45,4 +49,155 @@ export declare function fromOtlp(body: unknown): {
45
49
  } | {
46
50
  error: string;
47
51
  };
52
+ /** spec/otlp.md §5: a double as integer micro-units, half up; null when it isn't a finite number
53
+ * or its micro-units aren't a safe integer. */
54
+ export declare function microOf(v: unknown): number | null;
55
+ /**
56
+ * spec/otlp.md §5: OTLP/JSON metrics as session events, one per data point of a GenAI metric
57
+ * (`gen_ai.*`) or a point with a `gen_ai.*` or `blackbox.*` attribute (the resource's count);
58
+ * others are skipped. Doubles become integer micro-units; the event_id is a hash of the point's
59
+ * identity, so a resent point adds nothing.
60
+ */
61
+ export declare function fromOtlpMetrics(body: unknown): {
62
+ events: OtlpEvent[];
63
+ } | {
64
+ error: string;
65
+ };
66
+ /**
67
+ * spec/otlp.md §5: OTLP/JSON logs as session events, one per GenAI record (an event name
68
+ * `gen_ai.*`, or a `gen_ai.*` or `blackbox.*` attribute, the resource's included); others are
69
+ * skipped. The body is recorded as `body_sha256`, and itself only with `bodies: true`.
70
+ */
71
+ export declare function fromOtlpLogs(body: unknown, options?: {
72
+ bodies?: boolean;
73
+ }): {
74
+ events: OtlpEvent[];
75
+ } | {
76
+ error: string;
77
+ };
78
+ /**
79
+ * spec/otlp.md §6: a session's metrics as OTLP/JSON: token usage and call duration (the GenAI
80
+ * client histograms), the calls witnessed, and the priced cost in micro-USD per model from its cost
81
+ * report (spec/cost.md), cumulative from the session's open to its last event.
82
+ */
83
+ export declare function toOtlpMetrics(lines: readonly string[], cost?: {
84
+ calls: ReadonlyArray<{
85
+ model?: string | null;
86
+ micro_usd: number | null;
87
+ }>;
88
+ } | null): {
89
+ resourceMetrics: {
90
+ resource: {
91
+ attributes: Attr[];
92
+ };
93
+ scopeMetrics: {
94
+ scope: {
95
+ name: string;
96
+ version: string;
97
+ };
98
+ metrics: ({
99
+ name: string;
100
+ description: string;
101
+ unit: string;
102
+ histogram: {
103
+ aggregationTemporality: number;
104
+ dataPoints: {
105
+ startTimeUnixNano: string;
106
+ timeUnixNano: string;
107
+ attributes: AnyAttr[];
108
+ count: string;
109
+ sum: number;
110
+ bucketCounts: string[];
111
+ explicitBounds: number[];
112
+ min: number;
113
+ max: number;
114
+ }[];
115
+ };
116
+ sum?: never;
117
+ } | {
118
+ histogram?: never;
119
+ name: string;
120
+ description: string;
121
+ unit: string;
122
+ sum: {
123
+ aggregationTemporality: number;
124
+ isMonotonic: boolean;
125
+ dataPoints: object[];
126
+ };
127
+ })[];
128
+ }[];
129
+ }[];
130
+ };
131
+ /**
132
+ * spec/otlp.md §6: a session's findings as OTLP/JSON logs, one record each (event name
133
+ * `blackbox.finding`, the code as its body), in the session's trace (the root span's ids).
134
+ */
135
+ export declare function toOtlpLogs(lines: readonly string[]): {
136
+ resourceLogs: {
137
+ resource: {
138
+ attributes: Attr[];
139
+ };
140
+ scopeLogs: {
141
+ scope: {
142
+ name: string;
143
+ version: string;
144
+ };
145
+ logRecords: {
146
+ timeUnixNano: string;
147
+ severityNumber?: number;
148
+ severityText?: string;
149
+ body: {
150
+ stringValue: string;
151
+ };
152
+ attributes: AnyAttr[];
153
+ traceId: string;
154
+ spanId: string;
155
+ eventName: string;
156
+ }[];
157
+ }[];
158
+ }[];
159
+ };
160
+ /** One request the gateway forwarded, as it measured it (spec/otlp.md §7). */
161
+ export interface GatewayCall {
162
+ request_seq: number;
163
+ response_seq?: number;
164
+ provider: string;
165
+ model?: string;
166
+ status?: number;
167
+ /** The `llm.incomplete` reason, when the call didn't complete. */
168
+ reason?: string;
169
+ start_unix_nano: string;
170
+ end_unix_nano: string;
171
+ }
172
+ /**
173
+ * spec/otlp.md §7: the gateway's own spans for a session's forwarded requests, in the session's
174
+ * trace: each a SERVER span `forward {provider}`, child of the record's model-call span (§1, §2).
175
+ */
176
+ export declare function gatewayTrace(sessionId: string, calls: readonly GatewayCall[]): {
177
+ resourceSpans: {
178
+ resource: {
179
+ attributes: Attr[];
180
+ };
181
+ scopeSpans: {
182
+ scope: {
183
+ name: string;
184
+ version: string;
185
+ };
186
+ spans: {
187
+ traceId: string;
188
+ spanId: string;
189
+ parentSpanId: string;
190
+ name: string;
191
+ kind: number;
192
+ startTimeUnixNano: string;
193
+ endTimeUnixNano: string;
194
+ attributes: Attr[];
195
+ status?: {
196
+ code: number;
197
+ message: string;
198
+ };
199
+ }[];
200
+ }[];
201
+ }[];
202
+ };
48
203
  export {};