@bitfab/sdk 0.26.0 → 0.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GCAJN5I7.js → chunk-DW7VTEO2.js} +88 -14
- package/dist/chunk-DW7VTEO2.js.map +1 -0
- package/dist/{chunk-26MUT4IP.js → chunk-IDGR2OIX.js} +54 -33
- package/dist/chunk-IDGR2OIX.js.map +1 -0
- package/dist/index.cjs +140 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/node.cjs +140 -47
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +2 -2
- package/dist/{replay-KYGI6LJY.js → replay-CORDD7TR.js} +2 -2
- package/package.json +2 -2
- package/dist/chunk-26MUT4IP.js.map +0 -1
- package/dist/chunk-GCAJN5I7.js.map +0 -1
- /package/dist/{replay-KYGI6LJY.js.map → replay-CORDD7TR.js.map} +0 -0
|
@@ -7,6 +7,9 @@ var BitfabError = class extends Error {
|
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
// src/serialize.ts
|
|
11
|
+
import superjson from "superjson";
|
|
12
|
+
|
|
10
13
|
// src/warnOnce.ts
|
|
11
14
|
var warned = /* @__PURE__ */ new Set();
|
|
12
15
|
function warnOnce(key, message) {
|
|
@@ -20,31 +23,7 @@ function warnOnce(key, message) {
|
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
// src/randomUuid.ts
|
|
24
|
-
function randomUuid() {
|
|
25
|
-
const globalCrypto = globalThis.crypto;
|
|
26
|
-
if (typeof globalCrypto?.randomUUID === "function") {
|
|
27
|
-
try {
|
|
28
|
-
return globalCrypto.randomUUID();
|
|
29
|
-
} catch {
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
warnOnce(
|
|
33
|
-
"crypto-unavailable",
|
|
34
|
-
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
35
|
-
);
|
|
36
|
-
return fallbackUuidV4();
|
|
37
|
-
}
|
|
38
|
-
function fallbackUuidV4() {
|
|
39
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
40
|
-
const rand = Math.random() * 16 | 0;
|
|
41
|
-
const value = char === "x" ? rand : rand & 3 | 8;
|
|
42
|
-
return value.toString(16);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
26
|
// src/serialize.ts
|
|
47
|
-
import superjson from "superjson";
|
|
48
27
|
var MAX_SERIALIZED_BYTES = 512e3;
|
|
49
28
|
var MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
50
29
|
function describeValue(value) {
|
|
@@ -102,7 +81,11 @@ function deserializeValue(serialized) {
|
|
|
102
81
|
}
|
|
103
82
|
var MAX_SAFE_DEPTH = 6;
|
|
104
83
|
function toJsonSafe(value) {
|
|
105
|
-
|
|
84
|
+
return toJsonSafeReport(value).safe;
|
|
85
|
+
}
|
|
86
|
+
function toJsonSafeReport(value) {
|
|
87
|
+
const dropped = [];
|
|
88
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet(), dropped);
|
|
106
89
|
try {
|
|
107
90
|
const size = JSON.stringify(safe)?.length ?? 0;
|
|
108
91
|
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
@@ -110,13 +93,16 @@ function toJsonSafe(value) {
|
|
|
110
93
|
"toJsonSafe:too_large",
|
|
111
94
|
`a framework payload exceeded ${MAX_FRAMEWORK_SERIALIZED_BYTES} bytes and was replaced with a placeholder so the span still ships. The captured state for this span is incomplete.`
|
|
112
95
|
);
|
|
113
|
-
return
|
|
96
|
+
return {
|
|
97
|
+
safe: `<unserializable: too_large_${size}_bytes>`,
|
|
98
|
+
dropped: [...dropped, `too_large_${size}_bytes`]
|
|
99
|
+
};
|
|
114
100
|
}
|
|
115
101
|
} catch {
|
|
116
102
|
}
|
|
117
|
-
return safe;
|
|
103
|
+
return { safe, dropped };
|
|
118
104
|
}
|
|
119
|
-
function toJsonSafeInner(value, depth, seen) {
|
|
105
|
+
function toJsonSafeInner(value, depth, seen, dropped) {
|
|
120
106
|
if (value === null || value === void 0) {
|
|
121
107
|
return value;
|
|
122
108
|
}
|
|
@@ -125,30 +111,40 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
125
111
|
}
|
|
126
112
|
const className = value?.constructor?.name ?? typeof value;
|
|
127
113
|
if (depth > MAX_SAFE_DEPTH) {
|
|
114
|
+
dropped.push(className);
|
|
128
115
|
return `<${className}>`;
|
|
129
116
|
}
|
|
130
117
|
if (typeof value !== "object") {
|
|
118
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
119
|
+
dropped.push(className);
|
|
120
|
+
}
|
|
131
121
|
try {
|
|
132
122
|
return String(value);
|
|
133
123
|
} catch {
|
|
124
|
+
dropped.push(className);
|
|
134
125
|
return `<${className}>`;
|
|
135
126
|
}
|
|
136
127
|
}
|
|
137
128
|
if (seen.has(value)) {
|
|
129
|
+
dropped.push(className);
|
|
138
130
|
return `<cycle ${className}>`;
|
|
139
131
|
}
|
|
140
132
|
seen.add(value);
|
|
141
133
|
let result;
|
|
142
134
|
if (Array.isArray(value)) {
|
|
143
|
-
result = value.map(
|
|
135
|
+
result = value.map(
|
|
136
|
+
(item) => toJsonSafeInner(item, depth + 1, seen, dropped)
|
|
137
|
+
);
|
|
144
138
|
} else if (typeof value.toJSON === "function") {
|
|
145
139
|
try {
|
|
146
140
|
result = toJsonSafeInner(
|
|
147
141
|
value.toJSON(),
|
|
148
142
|
depth + 1,
|
|
149
|
-
seen
|
|
143
|
+
seen,
|
|
144
|
+
dropped
|
|
150
145
|
);
|
|
151
146
|
} catch {
|
|
147
|
+
dropped.push(className);
|
|
152
148
|
result = `<${className}>`;
|
|
153
149
|
}
|
|
154
150
|
} else {
|
|
@@ -156,11 +152,12 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
156
152
|
const obj = {};
|
|
157
153
|
for (const [k, v] of Object.entries(value)) {
|
|
158
154
|
if (!k.startsWith("_")) {
|
|
159
|
-
obj[k] = toJsonSafeInner(v, depth + 1, seen);
|
|
155
|
+
obj[k] = toJsonSafeInner(v, depth + 1, seen, dropped);
|
|
160
156
|
}
|
|
161
157
|
}
|
|
162
158
|
result = obj;
|
|
163
159
|
} catch {
|
|
160
|
+
dropped.push(className);
|
|
164
161
|
result = `<${className}>`;
|
|
165
162
|
}
|
|
166
163
|
}
|
|
@@ -168,6 +165,29 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
168
165
|
return result;
|
|
169
166
|
}
|
|
170
167
|
|
|
168
|
+
// src/randomUuid.ts
|
|
169
|
+
function randomUuid() {
|
|
170
|
+
const globalCrypto = globalThis.crypto;
|
|
171
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
172
|
+
try {
|
|
173
|
+
return globalCrypto.randomUUID();
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
warnOnce(
|
|
178
|
+
"crypto-unavailable",
|
|
179
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
180
|
+
);
|
|
181
|
+
return fallbackUuidV4();
|
|
182
|
+
}
|
|
183
|
+
function fallbackUuidV4() {
|
|
184
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
185
|
+
const rand = Math.random() * 16 | 0;
|
|
186
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
187
|
+
return value.toString(16);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
171
191
|
// src/asyncStorage.ts
|
|
172
192
|
var AsyncLocalStorageClass = null;
|
|
173
193
|
var initDone = false;
|
|
@@ -226,10 +246,11 @@ function runWithReplayContext(ctx, fn) {
|
|
|
226
246
|
export {
|
|
227
247
|
BitfabError,
|
|
228
248
|
warnOnce,
|
|
229
|
-
randomUuid,
|
|
230
249
|
serializeValue,
|
|
231
250
|
deserializeValue,
|
|
232
251
|
toJsonSafe,
|
|
252
|
+
toJsonSafeReport,
|
|
253
|
+
randomUuid,
|
|
233
254
|
registerAsyncLocalStorageClass,
|
|
234
255
|
assertAsyncStorageRegistered,
|
|
235
256
|
asyncStorageReady,
|
|
@@ -239,4 +260,4 @@ export {
|
|
|
239
260
|
getReplayContext,
|
|
240
261
|
runWithReplayContext
|
|
241
262
|
};
|
|
242
|
-
//# sourceMappingURL=chunk-
|
|
263
|
+
//# sourceMappingURL=chunk-IDGR2OIX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/serialize.ts","../src/warnOnce.ts","../src/randomUuid.ts","../src/asyncStorage.ts","../src/replayContext.ts"],"sourcesContent":["/**\n * Shared error type for Bitfab SDK runtime errors. Lives in its own\n * module to avoid import cycles between `http.ts` and modules that need\n * to throw structured errors (e.g. `dbSnapshot.ts` validation).\n */\n\nexport class BitfabError extends Error {\n constructor(\n message: string,\n public readonly url?: string,\n ) {\n super(message)\n this.name = \"BitfabError\"\n }\n}\n","/**\n * Serialization utilities for Bitfab SDK.\n *\n * This module provides serialization with type metadata preservation,\n * using superjson for handling special JavaScript types like Date, Map,\n * Set, BigInt, undefined, etc.\n */\n\nimport superjson from \"superjson\"\nimport { warnOnce } from \"./warnOnce.js\"\n\n/**\n * Serialized value with JSON data and optional superjson meta for type preservation.\n *\n * The json field contains the JSON-serializable data.\n * The meta field (if present) contains superjson type information for deserializing\n * special types like Date, Map, Set, BigInt, etc.\n */\nexport interface SerializedValue {\n json: unknown\n meta?: unknown\n}\n\n// Cap on serialized payload size. superjson can succeed on values like SDK\n// client instances (OpenAI, etc.) and produce hundreds of KB to MB of useless\n// internal state. Anything beyond this is replaced with a stub so the span\n// still ships and the trace isn't dropped server-side.\nconst MAX_SERIALIZED_BYTES = 512_000\n\n// A higher cap for the framework-capture path (toJsonSafe). Agent/graph states\n// (LangGraph, Claude Agent SDK) are legitimately larger than a single\n// function's args/return, so this ceiling preserves real detail while still\n// bounding the payload so the wire-side JSON.stringify can't blow up or get the\n// span rejected server-side.\nconst MAX_FRAMEWORK_SERIALIZED_BYTES = 2_000_000\n\nfunction describeValue(value: unknown): string {\n try {\n const ctorName = (value as { constructor?: { name?: string } })?.constructor\n ?.name\n if (ctorName && ctorName !== \"Object\") {\n return ctorName\n }\n } catch {\n // Property access on `value` can throw (Proxy, poisoned getter).\n }\n return typeof value\n}\n\nfunction unserializableStub(value: unknown, reason: string): SerializedValue {\n // Normalize the byte count out of the key so a too_large warning dedups\n // across differently-sized payloads instead of warning once per size.\n warnOnce(\n `serialize:${reason.replace(/\\d+/g, \"N\")}`,\n `a value could not be fully serialized for a span (${reason}); it was replaced with a placeholder. The span still ships, but its captured input/output is incomplete.`,\n )\n let summary: string\n try {\n summary = `<unserializable: ${describeValue(value)} (${reason})>`\n } catch {\n summary = `<unserializable (${reason})>`\n }\n return { json: summary }\n}\n\n/**\n * Serialize a value using superjson for trace storage.\n *\n * Handles arbitrary JavaScript values including:\n * - Date, RegExp, Error\n * - Map, Set\n * - BigInt\n * - undefined (in objects/arrays)\n * - Circular references\n *\n * Guarantees:\n * - Never throws. Pathological inputs (SDK clients, proxies, poisoned\n * getters, circular graphs that defeat superjson) return a stub string.\n * - Never returns a payload larger than MAX_SERIALIZED_BYTES; oversized\n * inputs are replaced with a stub. Without this the wire-side\n * `JSON.stringify` in http.ts can produce a request that times out or\n * gets rejected, leaving a trace with zero spans.\n *\n * @param value - Any JavaScript value to serialize\n * @returns SerializedValue with 'json' field containing the data.\n * If type metadata is needed for reconstruction, includes 'meta' field.\n *\n * @example\n * ```typescript\n * const result = serializeValue(new Date('2024-01-15T10:30:00Z'))\n * // result.json contains the ISO string\n * // result.meta contains type info for Date reconstruction\n * ```\n */\nexport function serializeValue(value: unknown): SerializedValue {\n try {\n const { json, meta } = superjson.serialize(value)\n\n let size: number\n try {\n size = JSON.stringify(json).length\n } catch {\n return unserializableStub(value, \"stringify_failed_after_superjson\")\n }\n if (size > MAX_SERIALIZED_BYTES) {\n return unserializableStub(value, `too_large_${size}_bytes`)\n }\n\n return meta ? { json, meta } : { json }\n } catch {\n try {\n return { json: JSON.parse(JSON.stringify(value)) }\n } catch {\n return unserializableStub(value, \"json_stringify_failed\")\n }\n }\n}\n\n/**\n * Deserialize a value that was serialized with serializeValue.\n *\n * @param serialized - A SerializedValue object with 'json' and optional 'meta'\n * @returns The reconstructed JavaScript value\n *\n * @example\n * ```typescript\n * const serialized = serializeValue(new Date('2024-01-15'))\n * const date = deserializeValue(serialized)\n * // date is a Date object\n * ```\n */\nexport function deserializeValue(serialized: SerializedValue): unknown {\n if (serialized.meta === undefined) {\n // No metadata, return as-is\n return serialized.json\n }\n\n // Use superjson to deserialize with type reconstruction\n // Cast json to the expected superjson type\n type SuperJSONResult = Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize({\n json: serialized.json as SuperJSONResult[\"json\"],\n meta: serialized.meta as SuperJSONResult[\"meta\"],\n })\n}\n\nconst MAX_SAFE_DEPTH = 6\n\n/**\n * Convert any value to JSON-safe primitives, never throwing.\n *\n * Produces plain objects/arrays/scalars, recursing through `toJSON()` and\n * own-enumerable properties so no raw non-serializable value (a class, a\n * BigInt-bearing object) survives into a span payload. Cycles collapse to a\n * `<cycle ...>` marker; depth is capped.\n *\n * This is the single shared \"safe serialize\" used by the framework\n * integrations that capture raw objects (LangGraph, Claude Agent SDK). Keeping\n * the recurse-the-dump logic here, in one place, is what stops a new\n * integration from reintroducing the \"dump without recursing\" bug — see\n * `serializationInvariant.test.ts`.\n */\nexport function toJsonSafe(value: unknown): unknown {\n return toJsonSafeReport(value).safe\n}\n\n/**\n * Like {@link toJsonSafe}, but also reports what could not be faithfully\n * captured.\n *\n * Returns `{ safe, dropped }` where `dropped` lists the type name behind every\n * placeholder the walker had to emit: a cycle, a max-depth cut, an oversized\n * payload, or a value that could only be stringified (a function/symbol) or\n * stubbed after a throw. A non-empty `dropped` means the captured input/output\n * is lossy. Framework handlers carry it to the send boundary so a degraded\n * capture is marked non-replayable (`serialization_degraded`) instead of being\n * shipped as if it round-trips — mirrors the Python SDK's `to_json_safe_report`\n * + `finalize_span_payload`.\n */\nexport function toJsonSafeReport(value: unknown): {\n safe: unknown\n dropped: string[]\n} {\n const dropped: string[] = []\n const safe = toJsonSafeInner(value, 0, new WeakSet(), dropped)\n // Cap output size for parity with serializeValue. A multi-MB framework\n // payload (a large LangGraph state, a long message history) would otherwise\n // be JSON.stringify'd synchronously on the user's thread in http.ts and may\n // be rejected server-side, leaving a trace with zero spans. Stub it instead\n // so the span still ships. toJsonSafeInner produces only plain\n // objects/arrays/scalars/strings, so JSON.stringify here cannot throw; the\n // try is belt-and-suspenders.\n try {\n const size = JSON.stringify(safe)?.length ?? 0\n if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {\n warnOnce(\n \"toJsonSafe:too_large\",\n `a framework payload exceeded ${MAX_FRAMEWORK_SERIALIZED_BYTES} bytes and was replaced with a placeholder so the span still ships. The captured state for this span is incomplete.`,\n )\n // Keep any drops already accumulated by the walk (cycles, functions,\n // depth cuts); a payload can be both lossy AND oversized, and the\n // non-replayable marking needs the real types, not just the size stub.\n return {\n safe: `<unserializable: too_large_${size}_bytes>`,\n dropped: [...dropped, `too_large_${size}_bytes`],\n }\n }\n } catch {\n // Keep the recursed value; the http-layer sanitizer is the final backstop.\n }\n return { safe, dropped }\n}\n\nfunction toJsonSafeInner(\n value: unknown,\n depth: number,\n seen: WeakSet<object>,\n dropped: string[],\n): unknown {\n if (value === null || value === undefined) {\n return value\n }\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value\n }\n\n const className =\n (value as { constructor?: { name?: string } })?.constructor?.name ??\n typeof value\n if (depth > MAX_SAFE_DEPTH) {\n dropped.push(className)\n return `<${className}>`\n }\n\n // Non-object composites (bigint, function, symbol) stringify directly. A\n // bigint stringifies faithfully; a function/symbol becomes a lossy summary,\n // so those are reported as dropped.\n if (typeof value !== \"object\") {\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n dropped.push(className)\n }\n try {\n return String(value)\n } catch {\n dropped.push(className)\n return `<${className}>`\n }\n }\n\n if (seen.has(value as object)) {\n dropped.push(className)\n return `<cycle ${className}>`\n }\n seen.add(value as object)\n\n let result: unknown\n if (Array.isArray(value)) {\n result = value.map((item) =>\n toJsonSafeInner(item, depth + 1, seen, dropped),\n )\n } else if (typeof (value as Record<string, unknown>).toJSON === \"function\") {\n // Recurse toJSON() output: it can still hold non-serializable values (e.g.\n // a LangChain tool whose schema is a class) that would otherwise survive\n // into the span payload and crash the wire-side JSON.stringify.\n try {\n result = toJsonSafeInner(\n (value as { toJSON(): unknown }).toJSON(),\n depth + 1,\n seen,\n dropped,\n )\n } catch {\n dropped.push(className)\n result = `<${className}>`\n }\n } else {\n try {\n const obj: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n if (!k.startsWith(\"_\")) {\n obj[k] = toJsonSafeInner(v, depth + 1, seen, dropped)\n }\n }\n result = obj\n } catch {\n dropped.push(className)\n result = `<${className}>`\n }\n }\n\n // Backtrack: keep only ancestors on the current path in `seen`, so a shared\n // (DAG) reference under sibling keys is serialized again rather than stubbed\n // as a false cycle. Real cycles (an ancestor referencing itself) are still\n // caught above.\n seen.delete(value as object)\n return result\n}\n","/**\n * Emit a `console.warn` at most once per distinct `key` for the life of the\n * process.\n *\n * The SDK must NEVER crash a host app, so every failure on the user's path\n * degrades silently (a span is dropped, a call runs untraced, a payload is\n * stubbed). Silent is safe but undebuggable: a user who suddenly has no traces,\n * or sees `<unserializable>` in a span, has no signal as to why. A one-time\n * warning per distinct issue restores that signal without spamming the console\n * from a hot path.\n *\n * Keys should identify the specific degradation (e.g. include the traced\n * function key) so each distinct issue warns once, not just the first one seen.\n */\nconst warned = new Set<string>()\n\nexport function warnOnce(key: string, message: string): void {\n if (warned.has(key)) {\n return\n }\n warned.add(key)\n try {\n console.warn(`[bitfab] ${message}`)\n } catch {\n // Logging must never crash the host app (e.g. a closed/replaced console).\n }\n}\n\n/** Test-only: clear the dedup set so a warning can fire again. */\nexport function _resetWarnOnce(): void {\n warned.clear()\n}\n","/**\n * Generate a UUID v4 without assuming a global `crypto`.\n *\n * `crypto.randomUUID()` is the fast path and exists in every mainstream modern\n * runtime (Node >= 19, all browsers, Deno, Vercel edge, Cloudflare Workers).\n * But the SDK must NEVER crash a host app, and a few runtimes it can legitimately\n * run in lack a global `crypto` or `crypto.randomUUID` (Node < 19 without a\n * polyfill, older React Native, some sandboxes). There, an unguarded\n * `crypto.randomUUID()` throws on the user's call path. This helper falls back\n * to a `Math.random()`-based v4 string instead.\n *\n * The fallback is NOT cryptographically secure. That is fine: trace and span\n * IDs only need to be unique enough to correlate the spans of one trace; they\n * are never security-sensitive.\n */\nimport { warnOnce } from \"./warnOnce.js\"\n\nexport function randomUuid(): string {\n const globalCrypto = (\n globalThis as { crypto?: { randomUUID?: () => string } }\n ).crypto\n if (typeof globalCrypto?.randomUUID === \"function\") {\n try {\n return globalCrypto.randomUUID()\n } catch {\n // Fall through to the manual generator below.\n }\n }\n warnOnce(\n \"crypto-unavailable\",\n \"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive).\",\n )\n return fallbackUuidV4()\n}\n\n/**\n * RFC 4122 version 4 layout filled from `Math.random()`. Used only when a usable\n * global `crypto.randomUUID` is unavailable.\n */\nfunction fallbackUuidV4(): string {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (char) => {\n const rand = (Math.random() * 16) | 0\n const value = char === \"x\" ? rand : (rand & 0x3) | 0x8\n return value.toString(16)\n })\n}\n","/**\n * Shared AsyncLocalStorage loader.\n *\n * Provides two ways to initialize AsyncLocalStorage:\n *\n * 1. **Synchronous registration** (preferred for Node.js):\n * `asyncStorageNode.ts` calls `registerAsyncLocalStorageClass()` at module\n * evaluation time, so the class is available immediately — no async gap.\n * The `node.ts` entry point imports it before anything else.\n *\n * 2. **Async dynamic import** (fallback for the default entry point):\n * Loads `node:async_hooks` via a bundler-safe dynamic import. This is used\n * by the default `index.ts` entry point so the SDK works in browsers\n * (where the import silently fails) and in Node.js when imported via the\n * default entry point.\n *\n * ## Why the dynamic import looks like this\n *\n * We need to handle three environments:\n *\n * 1. **Pure Node.js** — `import(\"node:async_hooks\")` works natively.\n * 2. **Webpack/Turbopack (Next.js server)** — The bundler processes\n * `import()` calls at build time. The `webpackIgnore` magic comment tells\n * webpack (and turbopack) to emit a native `import()` call instead of\n * trying to resolve it, so Node.js handles it at runtime.\n * 3. **Browsers / Edge** — The `process.versions?.node` guard prevents\n * execution entirely. If it somehow runs, `.catch(() => {})` swallows\n * the failure.\n */\n\nexport interface AsyncLocalStorageLike<T> {\n getStore(): T | undefined\n run<R>(store: T, fn: () => R): R\n}\n\nlet AsyncLocalStorageClass: (new () => AsyncLocalStorageLike<unknown>) | null =\n null\nlet initDone = false\n\n/**\n * Register the AsyncLocalStorage class synchronously.\n *\n * Called by `asyncStorageNode.ts` at module evaluation time so the class\n * is available before any span is created — no async gap, no race condition.\n *\n * Safe to call multiple times; subsequent calls are no-ops.\n */\nexport function registerAsyncLocalStorageClass(\n cls: new () => AsyncLocalStorageLike<unknown>,\n): void {\n if (!AsyncLocalStorageClass) {\n AsyncLocalStorageClass = cls\n }\n initDone = true\n}\n\n/**\n * Assert that AsyncLocalStorage was registered successfully.\n *\n * Called by `node.ts` after importing `asyncStorageNode.ts` to catch\n * import-order bugs at startup rather than silently degrading to the\n * browser fallback (flat spans with no nesting).\n *\n * This should ONLY be called from the Node.js entry point where we\n * know `node:async_hooks` must be available.\n */\nexport function assertAsyncStorageRegistered(): void {\n if (!AsyncLocalStorageClass) {\n console.warn(\n \"Bitfab: AsyncLocalStorage not available — nested span context will not propagate.\",\n )\n }\n}\n\nexport const asyncStorageReady: Promise<void> = (\n typeof process !== \"undefined\" && process.versions?.node\n ? // The join trick hides \"node:async_hooks\" from static analysis so\n // bundlers that ban Node.js built-ins don't fail at build time.\n // webpackIgnore tells webpack/turbopack to emit a native import()\n // so Node.js can resolve the module at runtime.\n import(\n /* webpackIgnore: true */\n [\"node\", \"async_hooks\"].join(\":\")\n )\n .then(\n (mod: {\n AsyncLocalStorage: new () => AsyncLocalStorageLike<unknown>\n }) => {\n registerAsyncLocalStorageClass(mod.AsyncLocalStorage)\n },\n )\n .catch(() => {})\n : Promise.resolve()\n).then(() => {\n initDone = true\n})\n\nexport function isAsyncStorageInitDone(): boolean {\n return initDone\n}\n\nexport function createAsyncLocalStorage<T>(): AsyncLocalStorageLike<T> | null {\n return AsyncLocalStorageClass\n ? (new AsyncLocalStorageClass() as AsyncLocalStorageLike<T>)\n : null\n}\n","/**\n * Replay context propagation via AsyncLocalStorage.\n *\n * When set, the withSpan wrapper injects testRunId into the span payload\n * so that new spans created during replay are linked to the test run.\n * Optionally carries a mock tree so child spans can return historical\n * outputs instead of executing.\n */\n\nimport {\n type AsyncLocalStorageLike,\n asyncStorageReady,\n createAsyncLocalStorage,\n} from \"./asyncStorage.js\"\n\n/** A single span entry in the mock tree with its historical output. */\nexport interface MockSpan {\n sourceSpanId: string\n output: unknown\n outputMeta?: unknown\n}\n\n/**\n * Per-item DB branch resolved by the Bitfab service from the source\n * trace's `dbSnapshotRef`. Carried on the replay context so that\n * customer code reads `databaseUrl` through `ReplayEnvironment`, and so\n * the process-isolated replay runner can materialize it into a `.env`\n * overlay file before customer code initializes its DB client.\n *\n * `neonBranchId` is the literal Neon branch id; passing it to\n * `releaseDbBranchLease` deletes that branch.\n */\nexport interface DbBranchLease {\n neonBranchId: string\n /** Env var name the customer's app reads, e.g. \"DATABASE_URL\". */\n envKey: string\n databaseUrl: string\n expiresAt: string\n /**\n * The instant the branch was pinned to (the source trace's wall clock).\n * Echoed back in `db_snapshot_usage` on the replayed trace's completion.\n */\n snapshotTimestamp?: string\n providerConsoleUrl?: string\n readOnly?: boolean\n}\n\n/**\n * Pre-built lookup table of historical span outputs.\n * Keys are `${traceFunctionKey}:${spanName}:${callIndex}` so that repeated\n * calls with the same (key, name) are matched by call order, but spans\n * sharing only the traceFunctionKey (different name) do not collide.\n */\nexport interface MockTree {\n spans: Map<string, MockSpan>\n}\n\nexport interface ReplayContext {\n testRunId: string\n traceId?: string\n inputSourceSpanId?: string\n /**\n * External trace ID from `external_traces.id`. Used for span-chain\n * lookup against the source platform's trace tree (Braintrust, etc.).\n * NOT the same as the Bitfab `traceId` — see `sourceBitfabTraceId`.\n */\n inputSourceTraceId?: string\n /**\n * The Bitfab `traces.id` of the historical trace that produced this\n * replay item's input. This is what customer-facing surfaces (e.g.\n * `ReplayEnvironment.traceId`) should expose, since it's the ID the\n * customer sees in the Bitfab dashboard.\n */\n sourceBitfabTraceId?: string\n mockTree?: MockTree\n callCounters?: Map<string, number>\n mockStrategy?: \"none\" | \"all\" | \"marked\"\n dbBranchLease?: DbBranchLease\n /**\n * Set to true by `ReplayEnvironment` the first time customer code\n * actually obtains `databaseUrl` for this item (via the getter or\n * `snapshot()`). Reported on the trace completion inside\n * `db_snapshot_usage` so the server can distinguish \"branch was\n * provisioned and exposed\" from \"branch URL was actually consumed\".\n * Any future consumption path that hands the URL to customer code by\n * other means (e.g. a process-isolated runner writing an env overlay)\n * must also set this.\n */\n dbSnapshotAccessed?: boolean\n /**\n * Collector for the replay item's trace-persistence work. When present,\n * the root span's send path pushes a promise that resolves only after\n * every span upload AND the trace completion have been sent (registered\n * synchronously at send time, so the replay runner can await it after\n * the wrapped fn resolves). This is what lets replay guarantee traces\n * are persisted server-side before `completeReplay` builds the\n * trace-ID mapping. Absent outside replay, where sends stay\n * fire-and-forget.\n */\n pendingPersistence?: Promise<unknown>[]\n}\n\nlet replayContextStorage: AsyncLocalStorageLike<ReplayContext | null> | null =\n null\n\nexport const replayContextReady: Promise<void> = asyncStorageReady.then(() => {\n replayContextStorage = createAsyncLocalStorage<ReplayContext | null>()\n})\n\n/** Get the current replay context, if any. */\nexport function getReplayContext(): ReplayContext | null {\n return replayContextStorage?.getStore() ?? null\n}\n\n/** Run a function within a replay context. */\nexport function runWithReplayContext<T>(ctx: ReplayContext, fn: () => T): T {\n if (replayContextStorage) {\n return replayContextStorage.run(ctx, fn)\n }\n return fn()\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -64,35 +64,6 @@ var init_warnOnce = __esm({
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
// src/randomUuid.ts
|
|
68
|
-
function randomUuid() {
|
|
69
|
-
const globalCrypto = globalThis.crypto;
|
|
70
|
-
if (typeof globalCrypto?.randomUUID === "function") {
|
|
71
|
-
try {
|
|
72
|
-
return globalCrypto.randomUUID();
|
|
73
|
-
} catch {
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
warnOnce(
|
|
77
|
-
"crypto-unavailable",
|
|
78
|
-
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
79
|
-
);
|
|
80
|
-
return fallbackUuidV4();
|
|
81
|
-
}
|
|
82
|
-
function fallbackUuidV4() {
|
|
83
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
84
|
-
const rand = Math.random() * 16 | 0;
|
|
85
|
-
const value = char === "x" ? rand : rand & 3 | 8;
|
|
86
|
-
return value.toString(16);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
var init_randomUuid = __esm({
|
|
90
|
-
"src/randomUuid.ts"() {
|
|
91
|
-
"use strict";
|
|
92
|
-
init_warnOnce();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
67
|
// src/serialize.ts
|
|
97
68
|
function describeValue(value) {
|
|
98
69
|
try {
|
|
@@ -148,7 +119,11 @@ function deserializeValue(serialized) {
|
|
|
148
119
|
});
|
|
149
120
|
}
|
|
150
121
|
function toJsonSafe(value) {
|
|
151
|
-
|
|
122
|
+
return toJsonSafeReport(value).safe;
|
|
123
|
+
}
|
|
124
|
+
function toJsonSafeReport(value) {
|
|
125
|
+
const dropped = [];
|
|
126
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet(), dropped);
|
|
152
127
|
try {
|
|
153
128
|
const size = JSON.stringify(safe)?.length ?? 0;
|
|
154
129
|
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
@@ -156,13 +131,16 @@ function toJsonSafe(value) {
|
|
|
156
131
|
"toJsonSafe:too_large",
|
|
157
132
|
`a framework payload exceeded ${MAX_FRAMEWORK_SERIALIZED_BYTES} bytes and was replaced with a placeholder so the span still ships. The captured state for this span is incomplete.`
|
|
158
133
|
);
|
|
159
|
-
return
|
|
134
|
+
return {
|
|
135
|
+
safe: `<unserializable: too_large_${size}_bytes>`,
|
|
136
|
+
dropped: [...dropped, `too_large_${size}_bytes`]
|
|
137
|
+
};
|
|
160
138
|
}
|
|
161
139
|
} catch {
|
|
162
140
|
}
|
|
163
|
-
return safe;
|
|
141
|
+
return { safe, dropped };
|
|
164
142
|
}
|
|
165
|
-
function toJsonSafeInner(value, depth, seen) {
|
|
143
|
+
function toJsonSafeInner(value, depth, seen, dropped) {
|
|
166
144
|
if (value === null || value === void 0) {
|
|
167
145
|
return value;
|
|
168
146
|
}
|
|
@@ -171,30 +149,40 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
171
149
|
}
|
|
172
150
|
const className = value?.constructor?.name ?? typeof value;
|
|
173
151
|
if (depth > MAX_SAFE_DEPTH) {
|
|
152
|
+
dropped.push(className);
|
|
174
153
|
return `<${className}>`;
|
|
175
154
|
}
|
|
176
155
|
if (typeof value !== "object") {
|
|
156
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
157
|
+
dropped.push(className);
|
|
158
|
+
}
|
|
177
159
|
try {
|
|
178
160
|
return String(value);
|
|
179
161
|
} catch {
|
|
162
|
+
dropped.push(className);
|
|
180
163
|
return `<${className}>`;
|
|
181
164
|
}
|
|
182
165
|
}
|
|
183
166
|
if (seen.has(value)) {
|
|
167
|
+
dropped.push(className);
|
|
184
168
|
return `<cycle ${className}>`;
|
|
185
169
|
}
|
|
186
170
|
seen.add(value);
|
|
187
171
|
let result;
|
|
188
172
|
if (Array.isArray(value)) {
|
|
189
|
-
result = value.map(
|
|
173
|
+
result = value.map(
|
|
174
|
+
(item) => toJsonSafeInner(item, depth + 1, seen, dropped)
|
|
175
|
+
);
|
|
190
176
|
} else if (typeof value.toJSON === "function") {
|
|
191
177
|
try {
|
|
192
178
|
result = toJsonSafeInner(
|
|
193
179
|
value.toJSON(),
|
|
194
180
|
depth + 1,
|
|
195
|
-
seen
|
|
181
|
+
seen,
|
|
182
|
+
dropped
|
|
196
183
|
);
|
|
197
184
|
} catch {
|
|
185
|
+
dropped.push(className);
|
|
198
186
|
result = `<${className}>`;
|
|
199
187
|
}
|
|
200
188
|
} else {
|
|
@@ -202,11 +190,12 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
202
190
|
const obj = {};
|
|
203
191
|
for (const [k, v] of Object.entries(value)) {
|
|
204
192
|
if (!k.startsWith("_")) {
|
|
205
|
-
obj[k] = toJsonSafeInner(v, depth + 1, seen);
|
|
193
|
+
obj[k] = toJsonSafeInner(v, depth + 1, seen, dropped);
|
|
206
194
|
}
|
|
207
195
|
}
|
|
208
196
|
result = obj;
|
|
209
197
|
} catch {
|
|
198
|
+
dropped.push(className);
|
|
210
199
|
result = `<${className}>`;
|
|
211
200
|
}
|
|
212
201
|
}
|
|
@@ -225,6 +214,35 @@ var init_serialize = __esm({
|
|
|
225
214
|
}
|
|
226
215
|
});
|
|
227
216
|
|
|
217
|
+
// src/randomUuid.ts
|
|
218
|
+
function randomUuid() {
|
|
219
|
+
const globalCrypto = globalThis.crypto;
|
|
220
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
221
|
+
try {
|
|
222
|
+
return globalCrypto.randomUUID();
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
warnOnce(
|
|
227
|
+
"crypto-unavailable",
|
|
228
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
229
|
+
);
|
|
230
|
+
return fallbackUuidV4();
|
|
231
|
+
}
|
|
232
|
+
function fallbackUuidV4() {
|
|
233
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
234
|
+
const rand = Math.random() * 16 | 0;
|
|
235
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
236
|
+
return value.toString(16);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
var init_randomUuid = __esm({
|
|
240
|
+
"src/randomUuid.ts"() {
|
|
241
|
+
"use strict";
|
|
242
|
+
init_warnOnce();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
228
246
|
// src/asyncStorage.ts
|
|
229
247
|
function registerAsyncLocalStorageClass(cls) {
|
|
230
248
|
if (!AsyncLocalStorageClass) {
|
|
@@ -587,7 +605,7 @@ __export(index_exports, {
|
|
|
587
605
|
module.exports = __toCommonJS(index_exports);
|
|
588
606
|
|
|
589
607
|
// src/version.generated.ts
|
|
590
|
-
var __version__ = "0.26.
|
|
608
|
+
var __version__ = "0.26.2";
|
|
591
609
|
|
|
592
610
|
// src/constants.ts
|
|
593
611
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
@@ -1029,6 +1047,62 @@ var HttpClient = class {
|
|
|
1029
1047
|
}
|
|
1030
1048
|
};
|
|
1031
1049
|
|
|
1050
|
+
// src/processorPayload.ts
|
|
1051
|
+
init_serialize();
|
|
1052
|
+
init_warnOnce();
|
|
1053
|
+
var SERIALIZATION_DEGRADED_STEP = "serialization_degraded";
|
|
1054
|
+
function degradedError(dropped) {
|
|
1055
|
+
const names = [...new Set(dropped)].sort().join(", ");
|
|
1056
|
+
return {
|
|
1057
|
+
source: "sdk",
|
|
1058
|
+
step: SERIALIZATION_DEGRADED_STEP,
|
|
1059
|
+
error: `non-replayable: could not faithfully capture ${names}`
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
var ENVELOPE_FIELDS = [
|
|
1063
|
+
"type",
|
|
1064
|
+
"source",
|
|
1065
|
+
"traceFunctionKey",
|
|
1066
|
+
"sourceTraceId",
|
|
1067
|
+
"completed"
|
|
1068
|
+
];
|
|
1069
|
+
function rebuildEnvelope(payload, bodyKey, placeholder) {
|
|
1070
|
+
const rebuilt = {};
|
|
1071
|
+
for (const k of ENVELOPE_FIELDS) {
|
|
1072
|
+
if (k in payload) {
|
|
1073
|
+
rebuilt[k] = payload[k];
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
rebuilt[bodyKey] = { serialized: placeholder };
|
|
1077
|
+
return rebuilt;
|
|
1078
|
+
}
|
|
1079
|
+
function finalizeSpanPayload(payload, extraDropped) {
|
|
1080
|
+
const { safe, dropped } = toJsonSafeReport(payload);
|
|
1081
|
+
const allDropped = [...extraDropped ?? [], ...dropped];
|
|
1082
|
+
const collapsed = safe === null || typeof safe !== "object" || Array.isArray(safe);
|
|
1083
|
+
const result = collapsed ? rebuildEnvelope(payload, "rawSpan", safe) : safe;
|
|
1084
|
+
if (allDropped.length > 0) {
|
|
1085
|
+
const existing = result.errors;
|
|
1086
|
+
const errors = Array.isArray(existing) ? existing : [];
|
|
1087
|
+
errors.push(degradedError(allDropped));
|
|
1088
|
+
result.errors = errors;
|
|
1089
|
+
}
|
|
1090
|
+
return result;
|
|
1091
|
+
}
|
|
1092
|
+
function finalizeTracePayload(payload) {
|
|
1093
|
+
const { safe, dropped } = toJsonSafeReport(payload);
|
|
1094
|
+
const collapsed = safe === null || typeof safe !== "object" || Array.isArray(safe);
|
|
1095
|
+
const result = collapsed ? rebuildEnvelope(payload, "externalTrace", safe) : safe;
|
|
1096
|
+
if (dropped.length > 0 || collapsed) {
|
|
1097
|
+
const names = dropped.length > 0 ? [...new Set(dropped)].sort().join(", ") : "trace";
|
|
1098
|
+
warnOnce(
|
|
1099
|
+
`finalizeTrace:${names.replace(/\d+/g, "N")}`,
|
|
1100
|
+
`a trace held non-serializable value(s) (${names}); they were captured as placeholders, so the trace may not be replayable.`
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
return result;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1032
1106
|
// src/claudeAgentSdk.ts
|
|
1033
1107
|
init_randomUuid();
|
|
1034
1108
|
init_serialize();
|
|
@@ -1158,6 +1232,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1158
1232
|
// ── span helpers ─────────────────────────────────────────────
|
|
1159
1233
|
startSpan(spanId, name, spanType, inputData, parentId) {
|
|
1160
1234
|
const traceId = this.ensureTrace();
|
|
1235
|
+
const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
|
|
1161
1236
|
const spanInfo = {
|
|
1162
1237
|
spanId,
|
|
1163
1238
|
traceId,
|
|
@@ -1165,9 +1240,12 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1165
1240
|
startedAt: nowIso(),
|
|
1166
1241
|
name,
|
|
1167
1242
|
type: spanType,
|
|
1168
|
-
input:
|
|
1243
|
+
input: safeInput,
|
|
1169
1244
|
contexts: []
|
|
1170
1245
|
};
|
|
1246
|
+
if (inputDropped.length > 0) {
|
|
1247
|
+
spanInfo.dropped = [...inputDropped];
|
|
1248
|
+
}
|
|
1171
1249
|
this.runToSpan.set(spanId, spanInfo);
|
|
1172
1250
|
return spanInfo;
|
|
1173
1251
|
}
|
|
@@ -1178,7 +1256,11 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1178
1256
|
}
|
|
1179
1257
|
this.runToSpan.delete(spanId);
|
|
1180
1258
|
spanInfo.endedAt = nowIso();
|
|
1181
|
-
|
|
1259
|
+
const { safe: safeOutput, dropped: outputDropped } = toJsonSafeReport(output);
|
|
1260
|
+
spanInfo.output = safeOutput;
|
|
1261
|
+
if (outputDropped.length > 0) {
|
|
1262
|
+
spanInfo.dropped = [...spanInfo.dropped ?? [], ...outputDropped];
|
|
1263
|
+
}
|
|
1182
1264
|
if (error !== void 0) {
|
|
1183
1265
|
spanInfo.error = error;
|
|
1184
1266
|
}
|
|
@@ -1221,8 +1303,9 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1221
1303
|
sourceTraceId: spanInfo.traceId,
|
|
1222
1304
|
rawSpan
|
|
1223
1305
|
};
|
|
1306
|
+
const finalized = finalizeSpanPayload(payload, spanInfo.dropped);
|
|
1224
1307
|
try {
|
|
1225
|
-
this.httpClient.sendExternalSpan(
|
|
1308
|
+
this.httpClient.sendExternalSpan(finalized);
|
|
1226
1309
|
} catch {
|
|
1227
1310
|
}
|
|
1228
1311
|
}
|
|
@@ -1249,8 +1332,9 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1249
1332
|
externalTrace,
|
|
1250
1333
|
completed
|
|
1251
1334
|
};
|
|
1335
|
+
const finalized = finalizeTracePayload(traceData);
|
|
1252
1336
|
try {
|
|
1253
|
-
this.httpClient.sendExternalTrace(
|
|
1337
|
+
this.httpClient.sendExternalTrace(finalized);
|
|
1254
1338
|
} catch {
|
|
1255
1339
|
}
|
|
1256
1340
|
}
|
|
@@ -1888,7 +1972,6 @@ var LANGGRAPH_METADATA_KEYS = [
|
|
|
1888
1972
|
function nowIso2() {
|
|
1889
1973
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1890
1974
|
}
|
|
1891
|
-
var safeSerialize2 = toJsonSafe;
|
|
1892
1975
|
function convertMessage(message) {
|
|
1893
1976
|
if (typeof message !== "object" || message === null) {
|
|
1894
1977
|
return { role: "unknown", content: String(message) };
|
|
@@ -2133,6 +2216,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2133
2216
|
}
|
|
2134
2217
|
const lgMetadata = extractLangGraphMetadata(metadata);
|
|
2135
2218
|
const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
|
|
2219
|
+
const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
|
|
2136
2220
|
const spanInfo = {
|
|
2137
2221
|
spanId: runId,
|
|
2138
2222
|
traceId: invocation.traceId,
|
|
@@ -2141,9 +2225,12 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2141
2225
|
startedAt: nowIso2(),
|
|
2142
2226
|
name,
|
|
2143
2227
|
type: spanType,
|
|
2144
|
-
input:
|
|
2228
|
+
input: safeInput,
|
|
2145
2229
|
contexts
|
|
2146
2230
|
};
|
|
2231
|
+
if (inputDropped.length > 0) {
|
|
2232
|
+
spanInfo.dropped = [...inputDropped];
|
|
2233
|
+
}
|
|
2147
2234
|
if (willHide) {
|
|
2148
2235
|
spanInfo.hidden = true;
|
|
2149
2236
|
}
|
|
@@ -2157,7 +2244,11 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2157
2244
|
}
|
|
2158
2245
|
this.runToSpan.delete(runId);
|
|
2159
2246
|
spanInfo.endedAt = nowIso2();
|
|
2160
|
-
|
|
2247
|
+
const { safe: safeOutput, dropped: outputDropped } = toJsonSafeReport(output);
|
|
2248
|
+
spanInfo.output = safeOutput;
|
|
2249
|
+
if (outputDropped.length > 0) {
|
|
2250
|
+
spanInfo.dropped = [...spanInfo.dropped ?? [], ...outputDropped];
|
|
2251
|
+
}
|
|
2161
2252
|
if (error !== void 0) {
|
|
2162
2253
|
spanInfo.error = error;
|
|
2163
2254
|
}
|
|
@@ -2208,8 +2299,9 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2208
2299
|
sourceTraceId: spanInfo.traceId,
|
|
2209
2300
|
rawSpan
|
|
2210
2301
|
};
|
|
2302
|
+
const finalized = finalizeSpanPayload(payload, spanInfo.dropped);
|
|
2211
2303
|
try {
|
|
2212
|
-
this.httpClient.sendExternalSpan(
|
|
2304
|
+
this.httpClient.sendExternalSpan(finalized);
|
|
2213
2305
|
} catch {
|
|
2214
2306
|
}
|
|
2215
2307
|
}
|
|
@@ -2227,8 +2319,9 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2227
2319
|
},
|
|
2228
2320
|
completed
|
|
2229
2321
|
};
|
|
2322
|
+
const finalized = finalizeTracePayload(traceData);
|
|
2230
2323
|
try {
|
|
2231
|
-
this.httpClient.sendExternalTrace(
|
|
2324
|
+
this.httpClient.sendExternalTrace(finalized);
|
|
2232
2325
|
} catch {
|
|
2233
2326
|
}
|
|
2234
2327
|
}
|
|
@@ -2743,7 +2836,7 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2743
2836
|
if (errors.length > 0) {
|
|
2744
2837
|
payload.errors = errors;
|
|
2745
2838
|
}
|
|
2746
|
-
return payload;
|
|
2839
|
+
return finalizeSpanPayload(payload);
|
|
2747
2840
|
}
|
|
2748
2841
|
/**
|
|
2749
2842
|
* Send span to Bitfab API (fire-and-forget).
|