-
js/src/instrumentation/core/ - tracing-channel helpers, stream patching, shared types
-
js/src/instrumentation/plugins/ - provider-specific channel subscriptions and event-to-span conversion
-
js/src/wrappers/ - manual instrumentation entrypoints that should mirror the same logical contracts
-
js/src/auto-instrumentations/ - loader and bundler instrumentation config
-
js/tests/auto-instrumentations/ - functional coverage for transformed code
-
e2e/scenarios/ - subprocess-level contract coverage against the mock Braintrust server
-
Non-invasive: instrumentation must not change user-visible behavior. Errors must still propagate, streams and promise subclasses must keep their original semantics, and any patch must be behavior-preserving and idempotent.
-
Inputs are untrusted: treat args, results, events, headers, and metadata as hostile. Prototype pollution is a concrete risk here. Avoid unsafe property access patterns, prototype-sensitive operations, and unnecessary mutation of third-party objects.
-
Support both auto-instrumentation and manual instrumentation. Auto-instrumentation does not cover every environment, loader, or framework.
-
For orchestrion auto-instrumentation, prefer targeting public API functions. Instrumenting internal helpers is more likely to break across library versions.
-
Auto and manual paths should share logic. Prefer both paths emitting the same tracing-channel events, with provider plugins converting those events into spans/logs/errors. Manual wrappers should not directly emit observability data.
-
Reuse shared repo utilities before introducing local helpers. Check js/util/index.ts, neighboring instrumentation files, and existing plugins/wrappers for utilities like isObject, merge helpers, and sanitizers before adding ad hoc replacements.
-
If a public instrumentation surface changes, check whether the export surface also needs updates in js/src/instrumentation/index.ts or js/src/exports.ts.
-
Preserve async context propagation. Changes around tracing channels, stream patching, or loader hooks must keep the current span context across awaits and stream consumption.
-
Maintain isomorphic behavior. Node and browser/bundled paths must use compatible channel implementations and avoid channel-registry mismatches.
-
Setup, teardown, and patching must be idempotent. Enabling twice, disabling twice, or applying a patch twice should remain safe.
-
Promise/stream behavior must be preserved. Patches need to keep subclass/helper semantics intact.
-
Contain instrumentation failures. Extraction/logging bugs should be logged or ignored as appropriate, but must not break the user call path.
-
Use the SDK debugLogger for SDK instrumentation diagnostics. Do not call console.* directly from instrumentation code; direct console use should stay inside the debug logger implementation or another explicitly justified exception.
-
Pass Error objects directly as span.log({ error }) values. The SDK serializes errors correctly, so do not add local helpers that manually turn errors into message/stack strings unless an external API requires a non-Error representation.
-
Log only the useful surface. Prefer narrow, stable payloads over dumping full request/response objects; exclude redundant or overly large data when possible.
-
We want to limit our instrumentation to operations that are relevant for AI generations and operations (LLMs, embeddings, media generation, ...). Things like creating entities on platforms (CRUD for Workflows of Agent entities) is irrelevant to us.
-
When building instrumentation, we should always have a vendored type/interface for what we are wrapping. The type or interface should not be larger than what is relevant to the instrumentation. The type or interface should be used for typing tracing channels and also should be used to assert the type on whatever is passed into wrappers as soon as the wrapper has verified that the passed in value is plausibly what should be wrapped.
Before implementing or changing instrumentation it is advisable to add or adjust the e2e tests for the desired change, make it fail, then implement the new instrumentation until the test passes.