You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 4950cdb
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: .agents/skills/create-changeset/SKILL.md
+12-1Lines changed: 12 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,18 @@ description: Create or update Changesets for this repository with a Conventional
6
6
# Create a Changeset
7
7
8
8
Inspect the diff to identify every publishable package affected and choose the appropriate semantic version bump for each package.
9
-
Use `pnpm changeset` when creating the changeset unless the existing task requires editing a specific changeset file.
9
+
Create the changeset by writing a new Markdown file under `.changeset/` directly, with a short kebab-case filename such as `.changeset/fix-queued-login-race.md`.
10
+
11
+
```markdown
12
+
---
13
+
"braintrust": patch
14
+
"@braintrust/otel": minor
15
+
---
16
+
17
+
fix: Prevent conflicting queued logins from replacing active credentials
18
+
```
19
+
20
+
List each affected package by its `package.json` name with `major`, `minor`, or `patch`.
Copy file name to clipboardExpand all lines: .agents/skills/instrumentation/SKILL.md
+28-8Lines changed: 28 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,23 +1,23 @@
1
1
---
2
2
name: instrumentation
3
-
description: Add or update Braintrust SDK instrumentation. Use when working on instrumentation of any kind - like wrappers, auto-instrumentation configs, tracing channels, provider plugins, vendored SDK typings, or instrumentation-specific tests.
3
+
description: Add or update Braintrust SDK instrumentation. Use when working on instrumentation of any kind - like wrappers, auto-instrumentation configs, invocation hooks, provider plugins, vendored SDK typings, or instrumentation-specific tests.
4
4
---
5
5
6
6
# Instrumentation Rules
7
7
8
8
Read first based on the task:
9
9
10
-
-`js/src/instrumentation/README.md` for plugin and tracing-channel architecture
11
-
- Closest file in `js/src/instrumentation/core/` when changing shared channel semantics
10
+
-`js/src/instrumentation/README.md` for wrapping and tracing architecture
11
+
- Closest file in `js/src/instrumentation/core/` when changing shared invocation semantics
12
12
- Closest file in `js/src/instrumentation/plugins/` when changing provider-specific extraction or span mapping
13
13
- Closest file in `js/src/wrappers/` when manual wrappers and auto-instrumentation need to stay aligned
14
14
- Closest test in `js/tests/auto-instrumentations/` when changing hook, loader, bundler, or transform behavior
15
15
- Closest e2e scenario in `e2e/scenarios/*instrumentation*` or `e2e/scenarios/*node-hook*` when the user-visible trace contract changes
-`js/src/instrumentation/plugins/` - explicit interceptor registration and provider-specific tracing functions
21
21
-`js/src/wrappers/` - manual instrumentation entrypoints that should mirror the same logical contracts
22
22
-`js/src/auto-instrumentations/` - loader and bundler instrumentation config
23
23
-`js/tests/auto-instrumentations/` - functional coverage for transformed code
@@ -27,10 +27,22 @@ Map the change before editing:
27
27
- 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.
28
28
- Support both auto-instrumentation and manual instrumentation. Auto-instrumentation does not cover every environment, loader, or framework.
29
29
- For orchestrion auto-instrumentation, prefer targeting public API functions. Instrumenting internal helpers is more likely to break across library versions.
30
-
- Auto and manual paths should share logic through the same typed channel. For new and migrated instrumentation, prefer `invoke` in manual wrappers and `intercept` in provider plugins so the target can be scoped with `AsyncLocalStorage.run()` and its arguments, receiver, or output can be patched. Keep tracing-style hooks only as the compatibility path for instrumentation that has not migrated yet. Manual wrappers should not directly emit observability data.
30
+
- Braintrust's vendored Orchestrion implementation is purely internal to the SDK.
31
+
Downstream packages must not depend on its APIs, config schema, generated helpers, or compatibility with upstream Orchestrion.
32
+
Do not expose these internals through SDK entrypoints or public configuration options.
33
+
Update the transformer and its SDK callers together; remove unused upstream features and compatibility scaffolding.
34
+
Preserve instrumented application behavior, coordination between SDK copies, and upstream license notices.
35
+
- Keep wrapping and tracing separate, including internal APIs.
36
+
Define typed invocation hooks with `defineInterceptor`; `intercept` and `invoke` only compose and execute wrappers.
37
+
Definitions describe call arguments, return values, and opaque additional data, without span provenance or tracing methods.
38
+
The wrapping runtime must work without SDK initialization and have no dependency on spans, logging, or tracing lifecycle events.
39
+
Put span creation, context propagation, and finalization in separate tracing functions.
40
+
Plugins explicitly register those functions through `intercept`; shared tracing helpers accept callables and tracing configuration, never hooks or registration responsibilities.
41
+
Do not add combined helpers such as `traceInvocation` or `interceptAndTrace`, or retain a tracing-event compatibility path.
42
+
Manual wrappers and generated wrappers use the same hook through `invoke` and do not directly emit observability data.
31
43
- 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.
32
44
- 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`.
33
-
- Preserve async context propagation. Changes around tracing channels, stream patching, or loader hooks must keep the current span context across awaits and stream consumption.
45
+
- Preserve async context propagation. Changes around invocation hooks, stream patching, or loader hooks must keep the current span context across awaits and stream consumption.
34
46
- Maintain isomorphic behavior. Node and browser/bundled paths must use compatible channel implementations and avoid channel-registry mismatches.
35
47
- Setup, teardown, and patching must be idempotent. Enabling twice, disabling twice, or applying a patch twice should remain safe.
36
48
- Promise/stream behavior must be preserved. Patches need to keep subclass/helper semantics intact.
@@ -41,10 +53,18 @@ Map the change before editing:
41
53
- Log only the useful surface. Prefer narrow, stable payloads over dumping full request/response objects; exclude redundant or overly large data when possible.
42
54
- Automatic capture of inline multimodal bytes as Braintrust attachments must be opt-in.
43
55
Guard attachment creation and binary observation with `BRAINTRUST_AUTO_CAPTURE_ATTACHMENTS`, and enable them only when its value is `1` or `true`.
56
+
Audio/video recordings may contain personal data and are separately controlled with their own explicit flags (`BRAINTRUST_CAPTURE_USER_AUDIO_ATTACHMENTS`, `BRAINTRUST_CAPTURE_AGENT_AUDIO_ATTACHMENTS`): enabling the shared flag for other media never records audio/video without these being enabled.
44
57
Preserve useful text, metadata, metrics, and remote references when attachment capture is disabled, and omit inline bytes from logged payloads.
58
+
- Segmentable streaming media must use the shared `configureInstrumentation({ streamingAttachmentBufferBytes })` setting instead of provider-specific chunk-size or time-based controls.
59
+
It defaults to 4 MiB per logical media stream and counts binary attachment payload bytes after decoding and format conversion, including any required padding or silence.
60
+
Flush near the threshold at valid media boundaries and flush remaining valid content when the recording ends.
61
+
Preserve ordering, format metadata, and timestamps needed to interpret the attachments.
62
+
Keep whole-file responses intact, even if they exceed the threshold; never arbitrarily split encoded images, audio, or video.
63
+
This is a flush threshold, not a total memory limit or attachment-capture opt-in.
64
+
Preserve application stream behavior and existing capture consent; keep any capture safety ceiling internal and distinct from the threshold.
45
65
- Do not modify package READMEs during instrumentation work unless the user specifically requests it or the change corrects outdated information.
46
66
- 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.
47
-
- 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.
67
+
- 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 invocation hooks 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.
Copy file name to clipboardExpand all lines: AGENTS.md
+23-3Lines changed: 23 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -46,8 +46,22 @@ Keep public exports minimal. Generally, export only the requested runtime APIs a
46
46
47
47
## Instrumentation
48
48
49
+
Our vendored Orchestrion implementation is purely internal to the Braintrust SDK.
50
+
Downstream packages must not depend on its APIs, config schema, generated helpers, or compatibility with upstream Orchestrion.
51
+
Do not expose these internals through SDK entrypoints or public configuration options.
52
+
Change the transformer and its SDK callers together, and remove unused upstream features instead of retaining compatibility scaffolding.
53
+
Preserve instrumented application behavior, coordination between SDK copies, and upstream license notices.
54
+
49
55
Use the normal Orchestrion config plus plugin/channel path by default. Special-case source patches should be rare exceptions only when the target SDK cannot be instrumented through the standard transformer path, and the reason should be documented next to the patch.
50
56
57
+
API wrapping and span instrumentation are separate concepts.
58
+
Use `defineInterceptor` to define invocation hooks and `intercept`/`invoke` exclusively as generic wrapping machinery.
59
+
Wrapping definitions and runtime code must not depend on spans, tracing lifecycle events, provenance, or SDK initialization.
60
+
Keep span creation, context propagation, and finalization in separate tracing functions.
61
+
Provider plugins explicitly register those functions through interceptors; tracing helpers accept callables and tracing configuration, never hooks or registration responsibilities.
62
+
Do not introduce combined APIs such as `traceInvocation` or `interceptAndTrace`, including internal convenience APIs.
63
+
Manual wrappers and generated wrappers only invoke hooks.
64
+
51
65
Instrumentation patches generally do not need to be removed during teardown. Prefer leaving behavior-preserving patches installed when they are idempotent; do not add unpatching machinery by default.
52
66
53
67
Span names should generally remain stable across calls and versions. Do not include dynamic values such as model names in span names; record those values in metadata instead.
@@ -61,11 +75,17 @@ Generally, do not add tests for build or linting code or their configuration. Va
61
75
**From `js/` directory:**
62
76
63
77
```bash
64
-
pnpm test# Core vitest suite (excludes wrappers)
65
-
pnpm test -- -t "test name"# Filter by test name
66
-
pnpm run test:checks # Hermetic tests (core + vitest wrapper)
78
+
pnpm test# Core vitest suite (excludes wrappers)
79
+
pnpm exec vitest run src/foo.test.ts # Run a single test file
80
+
pnpm exec vitest run src/foo.test.ts -t "x"# Filter by test name within a file
81
+
pnpm run test:checks # Hermetic tests (core + vitest wrapper)
82
+
pnpm run check:typings # Typecheck source and test files
67
83
```
68
84
85
+
Do not pass filters through `pnpm test -- ...`; the `--` makes Vitest ignore them and run the full suite.
86
+
87
+
Vitest does not typecheck. Before opening or updating a pull request, run `pnpm run check:typings` in each package you touched (or from the repo root) so type errors in source or test files are caught before CI.
88
+
69
89
**E2E tests (`e2e/`):**
70
90
71
91
Each scenario runs the SDK in a subprocess against a mock Braintrust server and snapshots the results. No API keys required for replay; recording needs provider keys.
Copy file name to clipboardExpand all lines: PUBLISHING.md
+2-14Lines changed: 2 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ All package publishing happens in GitHub Actions. Do not publish from your local
4
4
5
5
## Short Version
6
6
7
-
- If your PR changes a publishable package, run `pnpm changeset` and commit the generated `.changeset/*.md` file.
7
+
- If your PR changes a publishable package, add a `.changeset/*.md` file to it.
8
8
- Merge the PR into `main`.
9
9
- For a stable release:
10
10
- Run **Prepare Release** on `main`.
@@ -21,19 +21,7 @@ All package publishing happens in GitHub Actions. Do not publish from your local
21
21
22
22
If your PR changes a publishable package, CI expects a changeset unless you explicitly skip it.
23
23
24
-
Create one with:
25
-
26
-
```bash
27
-
pnpm changeset
28
-
```
29
-
30
-
That creates a `.changeset/*.md` file. Commit it with your PR.
31
-
32
-
Use:
33
-
34
-
-`patch` for fixes and non-breaking maintenance changes
35
-
-`minor` for new backwards-compatible features
36
-
-`major` for breaking changes
24
+
To add one, have an agent follow [`.agents/skills/create-changeset/SKILL.md`](.agents/skills/create-changeset/SKILL.md), then commit the generated `.changeset/*.md` file with your PR.
37
25
38
26
If no release is intended, bypass the check with either:
Copy file name to clipboardExpand all lines: e2e/README.md
+13-1Lines changed: 13 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -183,7 +183,19 @@ pnpm --filter=@braintrust/js-e2e-tests run test:e2e
183
183
184
184
If a scenario records but later replay fails because of volatile fields in the request body (e.g. AI-SDK's generated message ids), add or update `<scenario-dir>/cassette-filter.mjs` for that scenario, then re-record.
185
185
186
-
After any successful record run, stale cassette variants are cleaned only inside scenarios that emitted cassette run-context records during that run. With explicit scenario names, cleanup is limited to those scenarios; with a full record run, cleanup applies to every observed cassette-backed scenario. Per-cassette blob sidecar cleanup still happens in the cassette file store when each cassette is saved.
186
+
After a successful record run, unused cassette variants and their blob directories are cleaned only for scenarios whose test files and tests all passed. Cleanup uses the cassette keys actually selected by the harness, including overrides and opt-outs. Scenarios with skipped tests, missing test files, or missing usage records are left untouched. Per-cassette blob sidecar cleanup still happens in the cassette file store when each cassette is saved.
187
+
188
+
### Checking unused cassettes without re-recording
189
+
190
+
```bash
191
+
pnpm run test:e2e:cassettes
192
+
pnpm run test:e2e:cassettes -- cloudflare-think-instrumentation
193
+
pnpm run test:e2e:cassettes -- --clean-cassettes cloudflare-think-instrumentation
194
+
```
195
+
196
+
These commands run a fresh replay and compare the cassette files on disk with actual usage. The default check leaves files untouched and exits nonzero when it finds unused variants. `--clean-cassettes` removes unused JSON files and their `.cassette.blobs` directories after the tests pass.
197
+
198
+
Only whole scenarios can be selected; test filters, file paths, sharding, and snapshot updates are rejected. A scenario with skipped tests, missing test files, or missing usage records is reported as not checked and makes the command exit nonzero. Unselected scenarios and directories with no test files are not checked. Use `BRAINTRUST_E2E_RUN_CONTEXT_DIR` to retain the run records and test report for the trace summary.
0 commit comments