bitfab 0.23.3 → 0.24.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.
- package/dist/{chunk-EQI6ZJC3.js → chunk-26MUT4IP.js} +57 -2
- package/dist/chunk-26MUT4IP.js.map +1 -0
- package/dist/{chunk-CG6LVLBK.js → chunk-S3PN26RH.js} +303 -203
- package/dist/chunk-S3PN26RH.js.map +1 -0
- package/dist/index.cjs +374 -202
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -7
- package/dist/index.d.ts +11 -7
- package/dist/index.js +2 -2
- package/dist/node.cjs +374 -202
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +2 -2
- package/dist/{replay-V6RPJYXZ.js → replay-CQIU2ITL.js} +4 -3
- package/dist/replay-CQIU2ITL.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-CG6LVLBK.js.map +0 -1
- package/dist/chunk-EQI6ZJC3.js.map +0 -1
- package/dist/replay-V6RPJYXZ.js.map +0 -1
package/dist/node.cjs
CHANGED
|
@@ -100,6 +100,54 @@ var init_errors = __esm({
|
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
// src/warnOnce.ts
|
|
104
|
+
function warnOnce(key, message) {
|
|
105
|
+
if (warned.has(key)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
warned.add(key);
|
|
109
|
+
try {
|
|
110
|
+
console.warn(`[bitfab] ${message}`);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
var warned;
|
|
115
|
+
var init_warnOnce = __esm({
|
|
116
|
+
"src/warnOnce.ts"() {
|
|
117
|
+
"use strict";
|
|
118
|
+
warned = /* @__PURE__ */ new Set();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// src/randomUuid.ts
|
|
123
|
+
function randomUuid() {
|
|
124
|
+
const globalCrypto = globalThis.crypto;
|
|
125
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
126
|
+
try {
|
|
127
|
+
return globalCrypto.randomUUID();
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
warnOnce(
|
|
132
|
+
"crypto-unavailable",
|
|
133
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
134
|
+
);
|
|
135
|
+
return fallbackUuidV4();
|
|
136
|
+
}
|
|
137
|
+
function fallbackUuidV4() {
|
|
138
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
139
|
+
const rand = Math.random() * 16 | 0;
|
|
140
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
141
|
+
return value.toString(16);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
var init_randomUuid = __esm({
|
|
145
|
+
"src/randomUuid.ts"() {
|
|
146
|
+
"use strict";
|
|
147
|
+
init_warnOnce();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
103
151
|
// src/serialize.ts
|
|
104
152
|
function describeValue(value) {
|
|
105
153
|
try {
|
|
@@ -112,6 +160,10 @@ function describeValue(value) {
|
|
|
112
160
|
return typeof value;
|
|
113
161
|
}
|
|
114
162
|
function unserializableStub(value, reason) {
|
|
163
|
+
warnOnce(
|
|
164
|
+
`serialize:${reason.replace(/\d+/g, "N")}`,
|
|
165
|
+
`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.`
|
|
166
|
+
);
|
|
115
167
|
let summary;
|
|
116
168
|
try {
|
|
117
169
|
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
@@ -151,7 +203,19 @@ function deserializeValue(serialized) {
|
|
|
151
203
|
});
|
|
152
204
|
}
|
|
153
205
|
function toJsonSafe(value) {
|
|
154
|
-
|
|
206
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
207
|
+
try {
|
|
208
|
+
const size = JSON.stringify(safe)?.length ?? 0;
|
|
209
|
+
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
210
|
+
warnOnce(
|
|
211
|
+
"toJsonSafe:too_large",
|
|
212
|
+
`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.`
|
|
213
|
+
);
|
|
214
|
+
return `<unserializable: too_large_${size}_bytes>`;
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
return safe;
|
|
155
219
|
}
|
|
156
220
|
function toJsonSafeInner(value, depth, seen) {
|
|
157
221
|
if (value === null || value === void 0) {
|
|
@@ -204,12 +268,14 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
204
268
|
seen.delete(value);
|
|
205
269
|
return result;
|
|
206
270
|
}
|
|
207
|
-
var import_superjson, MAX_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
271
|
+
var import_superjson, MAX_SERIALIZED_BYTES, MAX_FRAMEWORK_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
208
272
|
var init_serialize = __esm({
|
|
209
273
|
"src/serialize.ts"() {
|
|
210
274
|
"use strict";
|
|
211
275
|
import_superjson = __toESM(require("superjson"), 1);
|
|
276
|
+
init_warnOnce();
|
|
212
277
|
MAX_SERIALIZED_BYTES = 512e3;
|
|
278
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
213
279
|
MAX_SAFE_DEPTH = 6;
|
|
214
280
|
}
|
|
215
281
|
});
|
|
@@ -295,7 +361,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
295
361
|
let originalOutput;
|
|
296
362
|
let result;
|
|
297
363
|
let error = null;
|
|
298
|
-
const replayedTraceId =
|
|
364
|
+
const replayedTraceId = randomUuid();
|
|
299
365
|
const pendingPersistence = [];
|
|
300
366
|
try {
|
|
301
367
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
@@ -479,6 +545,7 @@ var init_replay = __esm({
|
|
|
479
545
|
"src/replay.ts"() {
|
|
480
546
|
"use strict";
|
|
481
547
|
init_errors();
|
|
548
|
+
init_randomUuid();
|
|
482
549
|
init_replayContext();
|
|
483
550
|
init_serialize();
|
|
484
551
|
}
|
|
@@ -515,13 +582,24 @@ registerAsyncLocalStorageClass(
|
|
|
515
582
|
);
|
|
516
583
|
|
|
517
584
|
// src/version.generated.ts
|
|
518
|
-
var __version__ = "0.
|
|
585
|
+
var __version__ = "0.24.0";
|
|
519
586
|
|
|
520
587
|
// src/constants.ts
|
|
521
588
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
522
589
|
|
|
523
590
|
// src/http.ts
|
|
524
591
|
init_errors();
|
|
592
|
+
|
|
593
|
+
// src/unrefTimer.ts
|
|
594
|
+
function unrefTimer(timer) {
|
|
595
|
+
const handle = timer;
|
|
596
|
+
if (typeof handle.unref === "function") {
|
|
597
|
+
handle.unref();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/http.ts
|
|
602
|
+
init_warnOnce();
|
|
525
603
|
function serializePayloadBody(payload) {
|
|
526
604
|
try {
|
|
527
605
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -566,11 +644,20 @@ function serializePayloadBody(payload) {
|
|
|
566
644
|
result = `<unserializable: ${className}>`;
|
|
567
645
|
}
|
|
568
646
|
} else {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
647
|
+
try {
|
|
648
|
+
const out = {};
|
|
649
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
650
|
+
out[k] = sanitize(v, seen);
|
|
651
|
+
}
|
|
652
|
+
result = out;
|
|
653
|
+
} catch {
|
|
654
|
+
warnOnce(
|
|
655
|
+
"payload:field-getter-threw",
|
|
656
|
+
"a value with a throwing getter/proxy could not be serialized into a span payload; it was replaced with a placeholder. The span still ships with its other fields intact."
|
|
657
|
+
);
|
|
658
|
+
dropped.push(className);
|
|
659
|
+
result = `<unserializable: ${className}>`;
|
|
572
660
|
}
|
|
573
|
-
result = out;
|
|
574
661
|
}
|
|
575
662
|
seen.delete(obj);
|
|
576
663
|
return result;
|
|
@@ -615,10 +702,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
615
702
|
if (pendingTracePromises.size === 0) {
|
|
616
703
|
return;
|
|
617
704
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
705
|
+
let timer;
|
|
706
|
+
try {
|
|
707
|
+
await Promise.race([
|
|
708
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
709
|
+
new Promise((resolve) => {
|
|
710
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
711
|
+
unrefTimer(timer);
|
|
712
|
+
})
|
|
713
|
+
]);
|
|
714
|
+
} finally {
|
|
715
|
+
if (timer) {
|
|
716
|
+
clearTimeout(timer);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
622
719
|
}
|
|
623
720
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
624
721
|
let isFlushing = false;
|
|
@@ -928,6 +1025,7 @@ var HttpClient = class {
|
|
|
928
1025
|
};
|
|
929
1026
|
|
|
930
1027
|
// src/claudeAgentSdk.ts
|
|
1028
|
+
init_randomUuid();
|
|
931
1029
|
init_serialize();
|
|
932
1030
|
function nowIso() {
|
|
933
1031
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1015,7 +1113,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1015
1113
|
if (this.activeContext) {
|
|
1016
1114
|
this.traceId = this.activeContext.traceId;
|
|
1017
1115
|
} else {
|
|
1018
|
-
this.traceId =
|
|
1116
|
+
this.traceId = randomUuid();
|
|
1019
1117
|
}
|
|
1020
1118
|
this.traceStartedAt = nowIso();
|
|
1021
1119
|
return this.traceId;
|
|
@@ -1040,7 +1138,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1040
1138
|
if (this.activeContext !== null) {
|
|
1041
1139
|
return;
|
|
1042
1140
|
}
|
|
1043
|
-
const spanId =
|
|
1141
|
+
const spanId = randomUuid();
|
|
1044
1142
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1045
1143
|
this.rootSpanId = spanId;
|
|
1046
1144
|
}
|
|
@@ -1154,7 +1252,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1154
1252
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1155
1253
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1156
1254
|
try {
|
|
1157
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1255
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1158
1256
|
const toolName = inputData.tool_name ?? "tool";
|
|
1159
1257
|
const toolInput = inputData.tool_input ?? {};
|
|
1160
1258
|
const agentId = inputData.agent_id;
|
|
@@ -1184,10 +1282,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1184
1282
|
}
|
|
1185
1283
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1186
1284
|
try {
|
|
1187
|
-
const agentId = inputData.agent_id ??
|
|
1285
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1188
1286
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1189
1287
|
const parentId = this.getParentId();
|
|
1190
|
-
const spanId =
|
|
1288
|
+
const spanId = randomUuid();
|
|
1191
1289
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1192
1290
|
this.startSpan(
|
|
1193
1291
|
spanId,
|
|
@@ -1328,7 +1426,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1328
1426
|
this.flushLlmTurn();
|
|
1329
1427
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1330
1428
|
this.pendingMessages = [];
|
|
1331
|
-
this.currentLlmSpanId =
|
|
1429
|
+
this.currentLlmSpanId = randomUuid();
|
|
1332
1430
|
this.currentLlmMessageId = messageId ?? null;
|
|
1333
1431
|
this.currentLlmContent = [];
|
|
1334
1432
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1772,6 +1870,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1772
1870
|
}
|
|
1773
1871
|
|
|
1774
1872
|
// src/langgraph.ts
|
|
1873
|
+
init_randomUuid();
|
|
1775
1874
|
init_serialize();
|
|
1776
1875
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1777
1876
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -2020,7 +2119,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2020
2119
|
} else {
|
|
2021
2120
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
2022
2121
|
invocation = {
|
|
2023
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2122
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2024
2123
|
activeContext,
|
|
2025
2124
|
rootRunId: runId
|
|
2026
2125
|
};
|
|
@@ -2315,6 +2414,20 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2315
2414
|
this.withSpanFn = config.withSpan;
|
|
2316
2415
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2317
2416
|
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2419
|
+
* replayable root `agent` span.
|
|
2420
|
+
*
|
|
2421
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2422
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2423
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2424
|
+
* the result is handed back immediately and the final output is recorded
|
|
2425
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2426
|
+
*
|
|
2427
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2428
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2429
|
+
* this root.
|
|
2430
|
+
*/
|
|
2318
2431
|
async wrapRun(agent, input, options) {
|
|
2319
2432
|
const { run } = await importOptionalPeer([
|
|
2320
2433
|
"@openai",
|
|
@@ -2353,6 +2466,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2353
2466
|
};
|
|
2354
2467
|
|
|
2355
2468
|
// src/client.ts
|
|
2469
|
+
init_randomUuid();
|
|
2356
2470
|
init_replayContext();
|
|
2357
2471
|
|
|
2358
2472
|
// src/replayEnvironment.ts
|
|
@@ -2772,6 +2886,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2772
2886
|
};
|
|
2773
2887
|
|
|
2774
2888
|
// src/client.ts
|
|
2889
|
+
init_warnOnce();
|
|
2775
2890
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2776
2891
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2777
2892
|
var asyncLocalStorage = null;
|
|
@@ -3130,7 +3245,20 @@ var Bitfab = class {
|
|
|
3130
3245
|
functionVersion.providers,
|
|
3131
3246
|
this.envVars
|
|
3132
3247
|
);
|
|
3133
|
-
|
|
3248
|
+
let resultStr;
|
|
3249
|
+
if (typeof executionResult.result === "string") {
|
|
3250
|
+
resultStr = executionResult.result;
|
|
3251
|
+
} else {
|
|
3252
|
+
try {
|
|
3253
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3254
|
+
} catch {
|
|
3255
|
+
warnOnce(
|
|
3256
|
+
"call-result-serialize",
|
|
3257
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3258
|
+
);
|
|
3259
|
+
resultStr = String(executionResult.result);
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3134
3262
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3135
3263
|
result: resultStr,
|
|
3136
3264
|
source: "typescript-sdk",
|
|
@@ -3379,11 +3507,26 @@ var Bitfab = class {
|
|
|
3379
3507
|
return await bamlClient[methodName](...args);
|
|
3380
3508
|
}
|
|
3381
3509
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
trackedClient
|
|
3386
|
-
|
|
3510
|
+
let trackedClient;
|
|
3511
|
+
let trackedMethod;
|
|
3512
|
+
try {
|
|
3513
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3514
|
+
const method2 = trackedClient[methodName];
|
|
3515
|
+
if (typeof method2 !== "function") {
|
|
3516
|
+
throw new BitfabError(
|
|
3517
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3518
|
+
);
|
|
3519
|
+
}
|
|
3520
|
+
trackedMethod = method2;
|
|
3521
|
+
} catch {
|
|
3522
|
+
warnOnce(
|
|
3523
|
+
`wrapBAML-setup:${methodName}`,
|
|
3524
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3525
|
+
);
|
|
3526
|
+
wrappedFn.collector = null;
|
|
3527
|
+
return await bamlClient[methodName](...args);
|
|
3528
|
+
}
|
|
3529
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3387
3530
|
wrappedFn.collector = collector;
|
|
3388
3531
|
try {
|
|
3389
3532
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3459,200 +3602,229 @@ var Bitfab = class {
|
|
|
3459
3602
|
() => wrappedFn.apply(this, args)
|
|
3460
3603
|
);
|
|
3461
3604
|
}
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
const
|
|
3476
|
-
|
|
3605
|
+
let newStack;
|
|
3606
|
+
let executeWithContext;
|
|
3607
|
+
let registeredTraceId;
|
|
3608
|
+
try {
|
|
3609
|
+
const currentStack = getSpanStack();
|
|
3610
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3611
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3612
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3613
|
+
const spanId = randomUuid();
|
|
3614
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3615
|
+
const isRootSpan = parentSpanId === null;
|
|
3616
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3617
|
+
newStack = [...currentStack, newContext];
|
|
3618
|
+
const inputs = args;
|
|
3619
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3620
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3621
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3622
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3623
|
+
activeTraceStates.set(traceId, {
|
|
3624
|
+
traceId,
|
|
3625
|
+
startedAt,
|
|
3626
|
+
contexts: [],
|
|
3627
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3628
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3629
|
+
},
|
|
3630
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3631
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3632
|
+
},
|
|
3633
|
+
dbSnapshotRef
|
|
3634
|
+
});
|
|
3635
|
+
pendingSpanPromises.set(traceId, []);
|
|
3636
|
+
registeredTraceId = traceId;
|
|
3637
|
+
}
|
|
3638
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3639
|
+
const baseSpanParams = {
|
|
3640
|
+
traceFunctionKey,
|
|
3641
|
+
functionName,
|
|
3642
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3477
3643
|
traceId,
|
|
3644
|
+
spanId,
|
|
3645
|
+
parentSpanId,
|
|
3646
|
+
inputs,
|
|
3478
3647
|
startedAt,
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
startedAt,
|
|
3500
|
-
spanType: options.type ?? "custom"
|
|
3501
|
-
};
|
|
3502
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3503
|
-
const replayCtx = getReplayContext();
|
|
3504
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3505
|
-
let resolvePersistence;
|
|
3506
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3507
|
-
persistenceCollector.push(
|
|
3508
|
-
new Promise((resolve) => {
|
|
3509
|
-
resolvePersistence = resolve;
|
|
3510
|
-
})
|
|
3511
|
-
);
|
|
3512
|
-
}
|
|
3513
|
-
try {
|
|
3514
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3515
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3516
|
-
...baseSpanParams,
|
|
3517
|
-
...params,
|
|
3518
|
-
contexts: newContext.contexts,
|
|
3519
|
-
prompt: newContext.prompt,
|
|
3520
|
-
endedAt,
|
|
3521
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3522
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3523
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3524
|
-
}
|
|
3525
|
-
});
|
|
3526
|
-
if (isRootSpan) {
|
|
3527
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3528
|
-
pending.push(spanPromise);
|
|
3529
|
-
if (persistenceCollector) {
|
|
3530
|
-
await Promise.allSettled(pending);
|
|
3531
|
-
} else {
|
|
3532
|
-
await Promise.race([
|
|
3533
|
-
Promise.allSettled(pending),
|
|
3534
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3535
|
-
]);
|
|
3536
|
-
}
|
|
3537
|
-
pendingSpanPromises.delete(traceId);
|
|
3538
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3539
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3540
|
-
traceFunctionKey,
|
|
3541
|
-
traceId,
|
|
3542
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3648
|
+
spanType: options.type ?? "custom"
|
|
3649
|
+
};
|
|
3650
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3651
|
+
const replayCtx = getReplayContext();
|
|
3652
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3653
|
+
let resolvePersistence;
|
|
3654
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3655
|
+
persistenceCollector.push(
|
|
3656
|
+
new Promise((resolve) => {
|
|
3657
|
+
resolvePersistence = resolve;
|
|
3658
|
+
})
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3661
|
+
try {
|
|
3662
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3663
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3664
|
+
...baseSpanParams,
|
|
3665
|
+
...params,
|
|
3666
|
+
contexts: newContext.contexts,
|
|
3667
|
+
prompt: newContext.prompt,
|
|
3543
3668
|
endedAt,
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
testRunId: traceState?.testRunId,
|
|
3548
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3549
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3550
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3551
|
-
// whether customer code obtained the branch URL during this
|
|
3552
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3553
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3554
|
-
...replayCtx?.dbBranchLease && {
|
|
3555
|
-
dbSnapshotUsage: {
|
|
3556
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3557
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3558
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3559
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3560
|
-
}
|
|
3669
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3670
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3671
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3561
3672
|
}
|
|
3562
3673
|
});
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
await completionPromise;
|
|
3566
|
-
}
|
|
3567
|
-
} else {
|
|
3568
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3569
|
-
if (pending) {
|
|
3674
|
+
if (isRootSpan) {
|
|
3675
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3570
3676
|
pending.push(spanPromise);
|
|
3677
|
+
if (persistenceCollector) {
|
|
3678
|
+
await Promise.allSettled(pending);
|
|
3679
|
+
} else {
|
|
3680
|
+
let raceTimer;
|
|
3681
|
+
try {
|
|
3682
|
+
await Promise.race([
|
|
3683
|
+
Promise.allSettled(pending),
|
|
3684
|
+
new Promise((resolve) => {
|
|
3685
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3686
|
+
unrefTimer(raceTimer);
|
|
3687
|
+
})
|
|
3688
|
+
]);
|
|
3689
|
+
} finally {
|
|
3690
|
+
if (raceTimer) {
|
|
3691
|
+
clearTimeout(raceTimer);
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
pendingSpanPromises.delete(traceId);
|
|
3696
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3697
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3698
|
+
traceFunctionKey,
|
|
3699
|
+
traceId,
|
|
3700
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3701
|
+
endedAt,
|
|
3702
|
+
sessionId: traceState?.sessionId,
|
|
3703
|
+
metadata: traceState?.metadata,
|
|
3704
|
+
contexts: traceState?.contexts ?? [],
|
|
3705
|
+
testRunId: traceState?.testRunId,
|
|
3706
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3707
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3708
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3709
|
+
// whether customer code obtained the branch URL during this
|
|
3710
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3711
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3712
|
+
...replayCtx?.dbBranchLease && {
|
|
3713
|
+
dbSnapshotUsage: {
|
|
3714
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3715
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3716
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3717
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
});
|
|
3721
|
+
activeTraceStates.delete(traceId);
|
|
3722
|
+
if (persistenceCollector) {
|
|
3723
|
+
await completionPromise;
|
|
3724
|
+
}
|
|
3571
3725
|
} else {
|
|
3572
|
-
pendingSpanPromises.
|
|
3726
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3727
|
+
if (pending) {
|
|
3728
|
+
pending.push(spanPromise);
|
|
3729
|
+
} else {
|
|
3730
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3731
|
+
}
|
|
3573
3732
|
}
|
|
3733
|
+
} catch {
|
|
3734
|
+
} finally {
|
|
3735
|
+
resolvePersistence?.();
|
|
3574
3736
|
}
|
|
3575
|
-
}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
if (fnReturnsPromise) {
|
|
3600
|
-
return Promise.resolve(output);
|
|
3737
|
+
};
|
|
3738
|
+
const replayCtxForMock = getReplayContext();
|
|
3739
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3740
|
+
const counters = replayCtxForMock.callCounters;
|
|
3741
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3742
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3743
|
+
counters.set(counterKey, callIndex + 1);
|
|
3744
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3745
|
+
if (shouldMock) {
|
|
3746
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3747
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3748
|
+
if (mockSpan) {
|
|
3749
|
+
let output = mockSpan.output;
|
|
3750
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3751
|
+
output = deserializeValue({
|
|
3752
|
+
json: mockSpan.output,
|
|
3753
|
+
meta: mockSpan.outputMeta
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
void sendSpan({ result: output });
|
|
3757
|
+
if (fnReturnsPromise) {
|
|
3758
|
+
return Promise.resolve(output);
|
|
3759
|
+
}
|
|
3760
|
+
return output;
|
|
3601
3761
|
}
|
|
3602
|
-
return output;
|
|
3603
3762
|
}
|
|
3604
3763
|
}
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3764
|
+
const recordSpan = (result) => {
|
|
3765
|
+
if (options.finalize) {
|
|
3766
|
+
const replayCtx = getReplayContext();
|
|
3767
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3768
|
+
let resolvePersistence;
|
|
3769
|
+
if (persistenceCollector) {
|
|
3770
|
+
persistenceCollector.push(
|
|
3771
|
+
new Promise((resolve) => {
|
|
3772
|
+
resolvePersistence = resolve;
|
|
3773
|
+
})
|
|
3774
|
+
);
|
|
3775
|
+
}
|
|
3776
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3777
|
+
(output) => sendSpan(
|
|
3778
|
+
{ result: output },
|
|
3779
|
+
{ skipPersistenceRegistration: true }
|
|
3780
|
+
)
|
|
3781
|
+
).catch(
|
|
3782
|
+
(error) => sendSpan(
|
|
3783
|
+
{
|
|
3784
|
+
result: void 0,
|
|
3785
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3786
|
+
},
|
|
3787
|
+
{ skipPersistenceRegistration: true }
|
|
3788
|
+
)
|
|
3789
|
+
).finally(() => resolvePersistence?.());
|
|
3790
|
+
} else {
|
|
3791
|
+
void sendSpan({ result });
|
|
3617
3792
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
)
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3793
|
+
};
|
|
3794
|
+
executeWithContext = () => {
|
|
3795
|
+
const result = fn(...args);
|
|
3796
|
+
if (result instanceof Promise) {
|
|
3797
|
+
return result.then((resolvedResult) => {
|
|
3798
|
+
recordSpan(resolvedResult);
|
|
3799
|
+
return resolvedResult;
|
|
3800
|
+
}).catch((error) => {
|
|
3801
|
+
void sendSpan({
|
|
3626
3802
|
result: void 0,
|
|
3627
|
-
error: error instanceof Error ?
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
)
|
|
3631
|
-
).finally(() => resolvePersistence?.());
|
|
3632
|
-
} else {
|
|
3633
|
-
void sendSpan({ result });
|
|
3634
|
-
}
|
|
3635
|
-
};
|
|
3636
|
-
const executeWithContext = () => {
|
|
3637
|
-
const result = fn(...args);
|
|
3638
|
-
if (result instanceof Promise) {
|
|
3639
|
-
return result.then((resolvedResult) => {
|
|
3640
|
-
recordSpan(resolvedResult);
|
|
3641
|
-
return resolvedResult;
|
|
3642
|
-
}).catch((error) => {
|
|
3643
|
-
void sendSpan({
|
|
3644
|
-
result: void 0,
|
|
3645
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3803
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3804
|
+
});
|
|
3805
|
+
throw error;
|
|
3646
3806
|
});
|
|
3647
|
-
|
|
3648
|
-
|
|
3807
|
+
}
|
|
3808
|
+
if (isAsyncGenerator(result)) {
|
|
3809
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3810
|
+
}
|
|
3811
|
+
recordSpan(result);
|
|
3812
|
+
return result;
|
|
3813
|
+
};
|
|
3814
|
+
} catch (setupError) {
|
|
3815
|
+
if (registeredTraceId) {
|
|
3816
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3817
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3649
3818
|
}
|
|
3650
|
-
if (
|
|
3651
|
-
|
|
3819
|
+
if (getReplayContext()) {
|
|
3820
|
+
throw setupError;
|
|
3652
3821
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3822
|
+
warnOnce(
|
|
3823
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3824
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3825
|
+
);
|
|
3826
|
+
return fn(...args);
|
|
3827
|
+
}
|
|
3656
3828
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3657
3829
|
};
|
|
3658
3830
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|