@bitfab/sdk 0.23.2 → 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-PP5K6RIU.js → chunk-S3PN26RH.js} +322 -206
- package/dist/chunk-S3PN26RH.js.map +1 -0
- package/dist/index.cjs +393 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -8
- package/dist/index.d.ts +24 -8
- package/dist/index.js +2 -2
- package/dist/node.cjs +393 -205
- 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-EQI6ZJC3.js.map +0 -1
- package/dist/chunk-PP5K6RIU.js.map +0 -1
- package/dist/replay-V6RPJYXZ.js.map +0 -1
package/dist/node.cjs
CHANGED
|
@@ -91,6 +91,54 @@ var init_errors = __esm({
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
// src/warnOnce.ts
|
|
95
|
+
function warnOnce(key, message) {
|
|
96
|
+
if (warned.has(key)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
warned.add(key);
|
|
100
|
+
try {
|
|
101
|
+
console.warn(`[bitfab] ${message}`);
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
var warned;
|
|
106
|
+
var init_warnOnce = __esm({
|
|
107
|
+
"src/warnOnce.ts"() {
|
|
108
|
+
"use strict";
|
|
109
|
+
warned = /* @__PURE__ */ new Set();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/randomUuid.ts
|
|
114
|
+
function randomUuid() {
|
|
115
|
+
const globalCrypto = globalThis.crypto;
|
|
116
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
117
|
+
try {
|
|
118
|
+
return globalCrypto.randomUUID();
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
warnOnce(
|
|
123
|
+
"crypto-unavailable",
|
|
124
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
125
|
+
);
|
|
126
|
+
return fallbackUuidV4();
|
|
127
|
+
}
|
|
128
|
+
function fallbackUuidV4() {
|
|
129
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
130
|
+
const rand = Math.random() * 16 | 0;
|
|
131
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
132
|
+
return value.toString(16);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
var init_randomUuid = __esm({
|
|
136
|
+
"src/randomUuid.ts"() {
|
|
137
|
+
"use strict";
|
|
138
|
+
init_warnOnce();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
94
142
|
// src/serialize.ts
|
|
95
143
|
function describeValue(value) {
|
|
96
144
|
try {
|
|
@@ -103,6 +151,10 @@ function describeValue(value) {
|
|
|
103
151
|
return typeof value;
|
|
104
152
|
}
|
|
105
153
|
function unserializableStub(value, reason) {
|
|
154
|
+
warnOnce(
|
|
155
|
+
`serialize:${reason.replace(/\d+/g, "N")}`,
|
|
156
|
+
`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.`
|
|
157
|
+
);
|
|
106
158
|
let summary;
|
|
107
159
|
try {
|
|
108
160
|
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
@@ -142,7 +194,19 @@ function deserializeValue(serialized) {
|
|
|
142
194
|
});
|
|
143
195
|
}
|
|
144
196
|
function toJsonSafe(value) {
|
|
145
|
-
|
|
197
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
198
|
+
try {
|
|
199
|
+
const size = JSON.stringify(safe)?.length ?? 0;
|
|
200
|
+
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
201
|
+
warnOnce(
|
|
202
|
+
"toJsonSafe:too_large",
|
|
203
|
+
`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.`
|
|
204
|
+
);
|
|
205
|
+
return `<unserializable: too_large_${size}_bytes>`;
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
return safe;
|
|
146
210
|
}
|
|
147
211
|
function toJsonSafeInner(value, depth, seen) {
|
|
148
212
|
if (value === null || value === void 0) {
|
|
@@ -195,12 +259,14 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
195
259
|
seen.delete(value);
|
|
196
260
|
return result;
|
|
197
261
|
}
|
|
198
|
-
var import_superjson, MAX_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
262
|
+
var import_superjson, MAX_SERIALIZED_BYTES, MAX_FRAMEWORK_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
199
263
|
var init_serialize = __esm({
|
|
200
264
|
"src/serialize.ts"() {
|
|
201
265
|
"use strict";
|
|
202
266
|
import_superjson = __toESM(require("superjson"), 1);
|
|
267
|
+
init_warnOnce();
|
|
203
268
|
MAX_SERIALIZED_BYTES = 512e3;
|
|
269
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
204
270
|
MAX_SAFE_DEPTH = 6;
|
|
205
271
|
}
|
|
206
272
|
});
|
|
@@ -286,7 +352,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
286
352
|
let originalOutput;
|
|
287
353
|
let result;
|
|
288
354
|
let error = null;
|
|
289
|
-
const replayedTraceId =
|
|
355
|
+
const replayedTraceId = randomUuid();
|
|
290
356
|
const pendingPersistence = [];
|
|
291
357
|
try {
|
|
292
358
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
@@ -470,6 +536,7 @@ var init_replay = __esm({
|
|
|
470
536
|
"src/replay.ts"() {
|
|
471
537
|
"use strict";
|
|
472
538
|
init_errors();
|
|
539
|
+
init_randomUuid();
|
|
473
540
|
init_replayContext();
|
|
474
541
|
init_serialize();
|
|
475
542
|
}
|
|
@@ -506,13 +573,24 @@ registerAsyncLocalStorageClass(
|
|
|
506
573
|
);
|
|
507
574
|
|
|
508
575
|
// src/version.generated.ts
|
|
509
|
-
var __version__ = "0.
|
|
576
|
+
var __version__ = "0.24.0";
|
|
510
577
|
|
|
511
578
|
// src/constants.ts
|
|
512
579
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
513
580
|
|
|
514
581
|
// src/http.ts
|
|
515
582
|
init_errors();
|
|
583
|
+
|
|
584
|
+
// src/unrefTimer.ts
|
|
585
|
+
function unrefTimer(timer) {
|
|
586
|
+
const handle = timer;
|
|
587
|
+
if (typeof handle.unref === "function") {
|
|
588
|
+
handle.unref();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/http.ts
|
|
593
|
+
init_warnOnce();
|
|
516
594
|
function serializePayloadBody(payload) {
|
|
517
595
|
try {
|
|
518
596
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -557,11 +635,20 @@ function serializePayloadBody(payload) {
|
|
|
557
635
|
result = `<unserializable: ${className}>`;
|
|
558
636
|
}
|
|
559
637
|
} else {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
638
|
+
try {
|
|
639
|
+
const out = {};
|
|
640
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
641
|
+
out[k] = sanitize(v, seen);
|
|
642
|
+
}
|
|
643
|
+
result = out;
|
|
644
|
+
} catch {
|
|
645
|
+
warnOnce(
|
|
646
|
+
"payload:field-getter-threw",
|
|
647
|
+
"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."
|
|
648
|
+
);
|
|
649
|
+
dropped.push(className);
|
|
650
|
+
result = `<unserializable: ${className}>`;
|
|
563
651
|
}
|
|
564
|
-
result = out;
|
|
565
652
|
}
|
|
566
653
|
seen.delete(obj);
|
|
567
654
|
return result;
|
|
@@ -606,10 +693,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
606
693
|
if (pendingTracePromises.size === 0) {
|
|
607
694
|
return;
|
|
608
695
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
696
|
+
let timer;
|
|
697
|
+
try {
|
|
698
|
+
await Promise.race([
|
|
699
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
700
|
+
new Promise((resolve) => {
|
|
701
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
702
|
+
unrefTimer(timer);
|
|
703
|
+
})
|
|
704
|
+
]);
|
|
705
|
+
} finally {
|
|
706
|
+
if (timer) {
|
|
707
|
+
clearTimeout(timer);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
613
710
|
}
|
|
614
711
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
615
712
|
let isFlushing = false;
|
|
@@ -919,6 +1016,7 @@ var HttpClient = class {
|
|
|
919
1016
|
};
|
|
920
1017
|
|
|
921
1018
|
// src/claudeAgentSdk.ts
|
|
1019
|
+
init_randomUuid();
|
|
922
1020
|
init_serialize();
|
|
923
1021
|
function nowIso() {
|
|
924
1022
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1006,7 +1104,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1006
1104
|
if (this.activeContext) {
|
|
1007
1105
|
this.traceId = this.activeContext.traceId;
|
|
1008
1106
|
} else {
|
|
1009
|
-
this.traceId =
|
|
1107
|
+
this.traceId = randomUuid();
|
|
1010
1108
|
}
|
|
1011
1109
|
this.traceStartedAt = nowIso();
|
|
1012
1110
|
return this.traceId;
|
|
@@ -1031,7 +1129,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1031
1129
|
if (this.activeContext !== null) {
|
|
1032
1130
|
return;
|
|
1033
1131
|
}
|
|
1034
|
-
const spanId =
|
|
1132
|
+
const spanId = randomUuid();
|
|
1035
1133
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1036
1134
|
this.rootSpanId = spanId;
|
|
1037
1135
|
}
|
|
@@ -1145,7 +1243,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1145
1243
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1146
1244
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1147
1245
|
try {
|
|
1148
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1246
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1149
1247
|
const toolName = inputData.tool_name ?? "tool";
|
|
1150
1248
|
const toolInput = inputData.tool_input ?? {};
|
|
1151
1249
|
const agentId = inputData.agent_id;
|
|
@@ -1175,10 +1273,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1175
1273
|
}
|
|
1176
1274
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1177
1275
|
try {
|
|
1178
|
-
const agentId = inputData.agent_id ??
|
|
1276
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1179
1277
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1180
1278
|
const parentId = this.getParentId();
|
|
1181
|
-
const spanId =
|
|
1279
|
+
const spanId = randomUuid();
|
|
1182
1280
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1183
1281
|
this.startSpan(
|
|
1184
1282
|
spanId,
|
|
@@ -1319,7 +1417,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1319
1417
|
this.flushLlmTurn();
|
|
1320
1418
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1321
1419
|
this.pendingMessages = [];
|
|
1322
|
-
this.currentLlmSpanId =
|
|
1420
|
+
this.currentLlmSpanId = randomUuid();
|
|
1323
1421
|
this.currentLlmMessageId = messageId ?? null;
|
|
1324
1422
|
this.currentLlmContent = [];
|
|
1325
1423
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1447,6 +1545,16 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1447
1545
|
// src/client.ts
|
|
1448
1546
|
init_asyncStorage();
|
|
1449
1547
|
|
|
1548
|
+
// src/optionalPeer.ts
|
|
1549
|
+
function importOptionalPeer(specifierParts) {
|
|
1550
|
+
const specifier = specifierParts.join("/");
|
|
1551
|
+
return import(
|
|
1552
|
+
/* webpackIgnore: true */
|
|
1553
|
+
/* @vite-ignore */
|
|
1554
|
+
specifier
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1450
1558
|
// src/baml.ts
|
|
1451
1559
|
var cachedBaml = null;
|
|
1452
1560
|
async function loadBaml() {
|
|
@@ -1454,7 +1562,7 @@ async function loadBaml() {
|
|
|
1454
1562
|
return cachedBaml;
|
|
1455
1563
|
}
|
|
1456
1564
|
try {
|
|
1457
|
-
cachedBaml = await
|
|
1565
|
+
cachedBaml = await importOptionalPeer(["@boundaryml", "baml"]);
|
|
1458
1566
|
return cachedBaml;
|
|
1459
1567
|
} catch {
|
|
1460
1568
|
throw new Error(
|
|
@@ -1753,6 +1861,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1753
1861
|
}
|
|
1754
1862
|
|
|
1755
1863
|
// src/langgraph.ts
|
|
1864
|
+
init_randomUuid();
|
|
1756
1865
|
init_serialize();
|
|
1757
1866
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1758
1867
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -2001,7 +2110,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2001
2110
|
} else {
|
|
2002
2111
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
2003
2112
|
invocation = {
|
|
2004
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2113
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2005
2114
|
activeContext,
|
|
2006
2115
|
rootRunId: runId
|
|
2007
2116
|
};
|
|
@@ -2296,8 +2405,25 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2296
2405
|
this.withSpanFn = config.withSpan;
|
|
2297
2406
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2298
2407
|
}
|
|
2408
|
+
/**
|
|
2409
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2410
|
+
* replayable root `agent` span.
|
|
2411
|
+
*
|
|
2412
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2413
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2414
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2415
|
+
* the result is handed back immediately and the final output is recorded
|
|
2416
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2417
|
+
*
|
|
2418
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2419
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2420
|
+
* this root.
|
|
2421
|
+
*/
|
|
2299
2422
|
async wrapRun(agent, input, options) {
|
|
2300
|
-
const { run } = await
|
|
2423
|
+
const { run } = await importOptionalPeer([
|
|
2424
|
+
"@openai",
|
|
2425
|
+
"agents"
|
|
2426
|
+
]);
|
|
2301
2427
|
if (this.getActiveSpanContext?.() != null) {
|
|
2302
2428
|
return run(
|
|
2303
2429
|
agent,
|
|
@@ -2331,6 +2457,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2331
2457
|
};
|
|
2332
2458
|
|
|
2333
2459
|
// src/client.ts
|
|
2460
|
+
init_randomUuid();
|
|
2334
2461
|
init_replayContext();
|
|
2335
2462
|
|
|
2336
2463
|
// src/replayEnvironment.ts
|
|
@@ -2750,6 +2877,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2750
2877
|
};
|
|
2751
2878
|
|
|
2752
2879
|
// src/client.ts
|
|
2880
|
+
init_warnOnce();
|
|
2753
2881
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2754
2882
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2755
2883
|
var asyncLocalStorage = null;
|
|
@@ -2848,7 +2976,10 @@ async function loadCollectorClass() {
|
|
|
2848
2976
|
return cachedCollectorClass;
|
|
2849
2977
|
}
|
|
2850
2978
|
try {
|
|
2851
|
-
const baml = await
|
|
2979
|
+
const baml = await importOptionalPeer([
|
|
2980
|
+
"@boundaryml",
|
|
2981
|
+
"baml"
|
|
2982
|
+
]);
|
|
2852
2983
|
cachedCollectorClass = baml.Collector;
|
|
2853
2984
|
return cachedCollectorClass;
|
|
2854
2985
|
} catch {
|
|
@@ -3105,7 +3236,20 @@ var Bitfab = class {
|
|
|
3105
3236
|
functionVersion.providers,
|
|
3106
3237
|
this.envVars
|
|
3107
3238
|
);
|
|
3108
|
-
|
|
3239
|
+
let resultStr;
|
|
3240
|
+
if (typeof executionResult.result === "string") {
|
|
3241
|
+
resultStr = executionResult.result;
|
|
3242
|
+
} else {
|
|
3243
|
+
try {
|
|
3244
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3245
|
+
} catch {
|
|
3246
|
+
warnOnce(
|
|
3247
|
+
"call-result-serialize",
|
|
3248
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3249
|
+
);
|
|
3250
|
+
resultStr = String(executionResult.result);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3109
3253
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3110
3254
|
result: resultStr,
|
|
3111
3255
|
source: "typescript-sdk",
|
|
@@ -3354,11 +3498,26 @@ var Bitfab = class {
|
|
|
3354
3498
|
return await bamlClient[methodName](...args);
|
|
3355
3499
|
}
|
|
3356
3500
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
trackedClient
|
|
3361
|
-
|
|
3501
|
+
let trackedClient;
|
|
3502
|
+
let trackedMethod;
|
|
3503
|
+
try {
|
|
3504
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3505
|
+
const method2 = trackedClient[methodName];
|
|
3506
|
+
if (typeof method2 !== "function") {
|
|
3507
|
+
throw new BitfabError(
|
|
3508
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3509
|
+
);
|
|
3510
|
+
}
|
|
3511
|
+
trackedMethod = method2;
|
|
3512
|
+
} catch {
|
|
3513
|
+
warnOnce(
|
|
3514
|
+
`wrapBAML-setup:${methodName}`,
|
|
3515
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3516
|
+
);
|
|
3517
|
+
wrappedFn.collector = null;
|
|
3518
|
+
return await bamlClient[methodName](...args);
|
|
3519
|
+
}
|
|
3520
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3362
3521
|
wrappedFn.collector = collector;
|
|
3363
3522
|
try {
|
|
3364
3523
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3434,200 +3593,229 @@ var Bitfab = class {
|
|
|
3434
3593
|
() => wrappedFn.apply(this, args)
|
|
3435
3594
|
);
|
|
3436
3595
|
}
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
const
|
|
3451
|
-
|
|
3596
|
+
let newStack;
|
|
3597
|
+
let executeWithContext;
|
|
3598
|
+
let registeredTraceId;
|
|
3599
|
+
try {
|
|
3600
|
+
const currentStack = getSpanStack();
|
|
3601
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3602
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3603
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3604
|
+
const spanId = randomUuid();
|
|
3605
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3606
|
+
const isRootSpan = parentSpanId === null;
|
|
3607
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3608
|
+
newStack = [...currentStack, newContext];
|
|
3609
|
+
const inputs = args;
|
|
3610
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3611
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3612
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3613
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3614
|
+
activeTraceStates.set(traceId, {
|
|
3615
|
+
traceId,
|
|
3616
|
+
startedAt,
|
|
3617
|
+
contexts: [],
|
|
3618
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3619
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3620
|
+
},
|
|
3621
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3622
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3623
|
+
},
|
|
3624
|
+
dbSnapshotRef
|
|
3625
|
+
});
|
|
3626
|
+
pendingSpanPromises.set(traceId, []);
|
|
3627
|
+
registeredTraceId = traceId;
|
|
3628
|
+
}
|
|
3629
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3630
|
+
const baseSpanParams = {
|
|
3631
|
+
traceFunctionKey,
|
|
3632
|
+
functionName,
|
|
3633
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3452
3634
|
traceId,
|
|
3635
|
+
spanId,
|
|
3636
|
+
parentSpanId,
|
|
3637
|
+
inputs,
|
|
3453
3638
|
startedAt,
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
startedAt,
|
|
3475
|
-
spanType: options.type ?? "custom"
|
|
3476
|
-
};
|
|
3477
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3478
|
-
const replayCtx = getReplayContext();
|
|
3479
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3480
|
-
let resolvePersistence;
|
|
3481
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3482
|
-
persistenceCollector.push(
|
|
3483
|
-
new Promise((resolve) => {
|
|
3484
|
-
resolvePersistence = resolve;
|
|
3485
|
-
})
|
|
3486
|
-
);
|
|
3487
|
-
}
|
|
3488
|
-
try {
|
|
3489
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3490
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3491
|
-
...baseSpanParams,
|
|
3492
|
-
...params,
|
|
3493
|
-
contexts: newContext.contexts,
|
|
3494
|
-
prompt: newContext.prompt,
|
|
3495
|
-
endedAt,
|
|
3496
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3497
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3498
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3499
|
-
}
|
|
3500
|
-
});
|
|
3501
|
-
if (isRootSpan) {
|
|
3502
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3503
|
-
pending.push(spanPromise);
|
|
3504
|
-
if (persistenceCollector) {
|
|
3505
|
-
await Promise.allSettled(pending);
|
|
3506
|
-
} else {
|
|
3507
|
-
await Promise.race([
|
|
3508
|
-
Promise.allSettled(pending),
|
|
3509
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3510
|
-
]);
|
|
3511
|
-
}
|
|
3512
|
-
pendingSpanPromises.delete(traceId);
|
|
3513
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3514
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3515
|
-
traceFunctionKey,
|
|
3516
|
-
traceId,
|
|
3517
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3639
|
+
spanType: options.type ?? "custom"
|
|
3640
|
+
};
|
|
3641
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3642
|
+
const replayCtx = getReplayContext();
|
|
3643
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3644
|
+
let resolvePersistence;
|
|
3645
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3646
|
+
persistenceCollector.push(
|
|
3647
|
+
new Promise((resolve) => {
|
|
3648
|
+
resolvePersistence = resolve;
|
|
3649
|
+
})
|
|
3650
|
+
);
|
|
3651
|
+
}
|
|
3652
|
+
try {
|
|
3653
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3654
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3655
|
+
...baseSpanParams,
|
|
3656
|
+
...params,
|
|
3657
|
+
contexts: newContext.contexts,
|
|
3658
|
+
prompt: newContext.prompt,
|
|
3518
3659
|
endedAt,
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
testRunId: traceState?.testRunId,
|
|
3523
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3524
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3525
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3526
|
-
// whether customer code obtained the branch URL during this
|
|
3527
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3528
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3529
|
-
...replayCtx?.dbBranchLease && {
|
|
3530
|
-
dbSnapshotUsage: {
|
|
3531
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3532
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3533
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3534
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3535
|
-
}
|
|
3660
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3661
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3662
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3536
3663
|
}
|
|
3537
3664
|
});
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
await completionPromise;
|
|
3541
|
-
}
|
|
3542
|
-
} else {
|
|
3543
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3544
|
-
if (pending) {
|
|
3665
|
+
if (isRootSpan) {
|
|
3666
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3545
3667
|
pending.push(spanPromise);
|
|
3668
|
+
if (persistenceCollector) {
|
|
3669
|
+
await Promise.allSettled(pending);
|
|
3670
|
+
} else {
|
|
3671
|
+
let raceTimer;
|
|
3672
|
+
try {
|
|
3673
|
+
await Promise.race([
|
|
3674
|
+
Promise.allSettled(pending),
|
|
3675
|
+
new Promise((resolve) => {
|
|
3676
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3677
|
+
unrefTimer(raceTimer);
|
|
3678
|
+
})
|
|
3679
|
+
]);
|
|
3680
|
+
} finally {
|
|
3681
|
+
if (raceTimer) {
|
|
3682
|
+
clearTimeout(raceTimer);
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
pendingSpanPromises.delete(traceId);
|
|
3687
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3688
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3689
|
+
traceFunctionKey,
|
|
3690
|
+
traceId,
|
|
3691
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3692
|
+
endedAt,
|
|
3693
|
+
sessionId: traceState?.sessionId,
|
|
3694
|
+
metadata: traceState?.metadata,
|
|
3695
|
+
contexts: traceState?.contexts ?? [],
|
|
3696
|
+
testRunId: traceState?.testRunId,
|
|
3697
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3698
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3699
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3700
|
+
// whether customer code obtained the branch URL during this
|
|
3701
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3702
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3703
|
+
...replayCtx?.dbBranchLease && {
|
|
3704
|
+
dbSnapshotUsage: {
|
|
3705
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3706
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3707
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3708
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
});
|
|
3712
|
+
activeTraceStates.delete(traceId);
|
|
3713
|
+
if (persistenceCollector) {
|
|
3714
|
+
await completionPromise;
|
|
3715
|
+
}
|
|
3546
3716
|
} else {
|
|
3547
|
-
pendingSpanPromises.
|
|
3717
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3718
|
+
if (pending) {
|
|
3719
|
+
pending.push(spanPromise);
|
|
3720
|
+
} else {
|
|
3721
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3722
|
+
}
|
|
3548
3723
|
}
|
|
3724
|
+
} catch {
|
|
3725
|
+
} finally {
|
|
3726
|
+
resolvePersistence?.();
|
|
3549
3727
|
}
|
|
3550
|
-
}
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
if (fnReturnsPromise) {
|
|
3575
|
-
return Promise.resolve(output);
|
|
3728
|
+
};
|
|
3729
|
+
const replayCtxForMock = getReplayContext();
|
|
3730
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3731
|
+
const counters = replayCtxForMock.callCounters;
|
|
3732
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3733
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3734
|
+
counters.set(counterKey, callIndex + 1);
|
|
3735
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3736
|
+
if (shouldMock) {
|
|
3737
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3738
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3739
|
+
if (mockSpan) {
|
|
3740
|
+
let output = mockSpan.output;
|
|
3741
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3742
|
+
output = deserializeValue({
|
|
3743
|
+
json: mockSpan.output,
|
|
3744
|
+
meta: mockSpan.outputMeta
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3747
|
+
void sendSpan({ result: output });
|
|
3748
|
+
if (fnReturnsPromise) {
|
|
3749
|
+
return Promise.resolve(output);
|
|
3750
|
+
}
|
|
3751
|
+
return output;
|
|
3576
3752
|
}
|
|
3577
|
-
return output;
|
|
3578
3753
|
}
|
|
3579
3754
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3755
|
+
const recordSpan = (result) => {
|
|
3756
|
+
if (options.finalize) {
|
|
3757
|
+
const replayCtx = getReplayContext();
|
|
3758
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3759
|
+
let resolvePersistence;
|
|
3760
|
+
if (persistenceCollector) {
|
|
3761
|
+
persistenceCollector.push(
|
|
3762
|
+
new Promise((resolve) => {
|
|
3763
|
+
resolvePersistence = resolve;
|
|
3764
|
+
})
|
|
3765
|
+
);
|
|
3766
|
+
}
|
|
3767
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3768
|
+
(output) => sendSpan(
|
|
3769
|
+
{ result: output },
|
|
3770
|
+
{ skipPersistenceRegistration: true }
|
|
3771
|
+
)
|
|
3772
|
+
).catch(
|
|
3773
|
+
(error) => sendSpan(
|
|
3774
|
+
{
|
|
3775
|
+
result: void 0,
|
|
3776
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3777
|
+
},
|
|
3778
|
+
{ skipPersistenceRegistration: true }
|
|
3779
|
+
)
|
|
3780
|
+
).finally(() => resolvePersistence?.());
|
|
3781
|
+
} else {
|
|
3782
|
+
void sendSpan({ result });
|
|
3592
3783
|
}
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
)
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3784
|
+
};
|
|
3785
|
+
executeWithContext = () => {
|
|
3786
|
+
const result = fn(...args);
|
|
3787
|
+
if (result instanceof Promise) {
|
|
3788
|
+
return result.then((resolvedResult) => {
|
|
3789
|
+
recordSpan(resolvedResult);
|
|
3790
|
+
return resolvedResult;
|
|
3791
|
+
}).catch((error) => {
|
|
3792
|
+
void sendSpan({
|
|
3601
3793
|
result: void 0,
|
|
3602
|
-
error: error instanceof Error ?
|
|
3603
|
-
}
|
|
3604
|
-
|
|
3605
|
-
)
|
|
3606
|
-
).finally(() => resolvePersistence?.());
|
|
3607
|
-
} else {
|
|
3608
|
-
void sendSpan({ result });
|
|
3609
|
-
}
|
|
3610
|
-
};
|
|
3611
|
-
const executeWithContext = () => {
|
|
3612
|
-
const result = fn(...args);
|
|
3613
|
-
if (result instanceof Promise) {
|
|
3614
|
-
return result.then((resolvedResult) => {
|
|
3615
|
-
recordSpan(resolvedResult);
|
|
3616
|
-
return resolvedResult;
|
|
3617
|
-
}).catch((error) => {
|
|
3618
|
-
void sendSpan({
|
|
3619
|
-
result: void 0,
|
|
3620
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3794
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3795
|
+
});
|
|
3796
|
+
throw error;
|
|
3621
3797
|
});
|
|
3622
|
-
|
|
3623
|
-
|
|
3798
|
+
}
|
|
3799
|
+
if (isAsyncGenerator(result)) {
|
|
3800
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3801
|
+
}
|
|
3802
|
+
recordSpan(result);
|
|
3803
|
+
return result;
|
|
3804
|
+
};
|
|
3805
|
+
} catch (setupError) {
|
|
3806
|
+
if (registeredTraceId) {
|
|
3807
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3808
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3624
3809
|
}
|
|
3625
|
-
if (
|
|
3626
|
-
|
|
3810
|
+
if (getReplayContext()) {
|
|
3811
|
+
throw setupError;
|
|
3627
3812
|
}
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3813
|
+
warnOnce(
|
|
3814
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3815
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3816
|
+
);
|
|
3817
|
+
return fn(...args);
|
|
3818
|
+
}
|
|
3631
3819
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3632
3820
|
};
|
|
3633
3821
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|