Skip to content

Commit 610c814

Browse files
authored
Merge branch 'develop' into rolaabuhasna/js-3489/tanstackstart-react-cloudflare
2 parents a6e6ed2 + 7dadc71 commit 610c814

773 files changed

Lines changed: 27213 additions & 15386 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/add-ai-integration/SKILL.md‎

Lines changed: 41 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,116 +6,61 @@ argument-hint: <provider-name>
66

77
# Adding a New AI Integration
88

9-
## Decision Tree
9+
## Conventions First
1010

11-
```
12-
Does the AI SDK have native OpenTelemetry support?
13-
|- YES -> Does it emit OTel spans automatically?
14-
| |- YES (like Vercel AI) -> Pattern 1: OTel Span Processors
15-
| +- NO -> Pattern 2: OTel Instrumentation (wrap client)
16-
+- NO -> Does the SDK provide hooks/callbacks?
17-
|- YES (like LangChain) -> Pattern 3: Callback/Hook Based
18-
+- NO -> Pattern 4: Client Wrapping
19-
```
20-
21-
## Runtime-Specific Placement
22-
23-
If an AI SDK only works in one runtime, code lives exclusively in that runtime's package. Do NOT add it to `packages/core/`.
24-
25-
- **Node.js-only** -> `packages/node/src/integrations/tracing/{provider}/`
26-
- **Cloudflare-only** -> `packages/cloudflare/src/integrations/tracing/{provider}.ts`
27-
- **Browser-only** -> `packages/browser/src/integrations/tracing/{provider}/`
28-
- **Multi-runtime** -> shared core in `packages/core/src/tracing/{provider}/` with runtime-specific wrappers
29-
30-
## Span Hierarchy
31-
32-
- `gen_ai.invoke_agent` — parent/pipeline spans (chains, agents, orchestration)
33-
- `gen_ai.chat`, `gen_ai.generate_text`, etc. — child spans (actual LLM calls)
34-
35-
## Shared Utilities (`packages/core/src/tracing/ai/`)
36-
37-
- `gen-ai-attributes.ts` — OTel Semantic Convention attribute constants. **Always use these, never hardcode.**
38-
- `utils.ts` — `setTokenUsageAttributes()`, `getTruncatedJsonString()`, `truncateGenAiMessages()`, `buildMethodPath()`
39-
- Only use attributes from [Sentry Gen AI Conventions](https://getsentry.github.io/sentry-conventions/attributes/gen_ai/).
40-
41-
## Streaming
42-
43-
- **Non-streaming:** `startSpan()`, set attributes from response
44-
- **Streaming:** `startSpanManual()`, accumulate state via async generator or event listeners, set `GEN_AI_RESPONSE_STREAMING_ATTRIBUTE: true`, call `span.end()` in finally block
45-
- Detect via `params.stream === true`
46-
- References: `openai/streaming.ts` (async generator), `anthropic-ai/streaming.ts` (event listeners)
11+
Span ops and attributes are specified outside this repo. Never invent or hardcode either:
4712

48-
## Token Accumulation
13+
- [gen_ai attributes](https://getsentry.github.io/sentry-conventions/attributes/gen_ai/) and [gen_ai ops](https://getsentry.github.io/sentry-conventions/ops/#gen_ai) — normative; import from `@sentry/conventions/attributes` and `@sentry/conventions/op`
14+
- [RFC 0153](https://github.com/getsentry/rfcs/blob/main/text/0153-decoupling-sentrys-generative-ai-conventions-from-open-telemetry.md) — why Sentry's gen-AI conventions diverge from the [OTel gen-ai semconv](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
4915

50-
- **Child spans:** Set tokens directly from API response via `setTokenUsageAttributes()`
51-
- **Parent spans (`invoke_agent`):** Accumulate from children using event processor (see `vercel-ai/`)
16+
Derive the op with `getGenAiSpanOp()` from `ai/core/utils.ts` rather than picking one by hand. `ai/core/gen-ai-attributes.ts` is for gap-fillers only — keys with no `@sentry/conventions` equivalent — so check it last, not first.
5217

53-
## Pattern 1: OTel Span Processors
18+
## Which Pattern
5419

55-
**Use when:** SDK emits OTel spans automatically (Vercel AI)
56-
57-
1. **Core:** Create `add{Provider}Processors()` in `packages/core/src/tracing/{provider}/index.ts` — registers `spanStart` listener + event processor
58-
2. **Node.js:** Add `callWhenPatched()` optimization in `packages/node/src/integrations/tracing/{provider}/index.ts` — defers registration until package is imported
59-
3. **Edge:** Direct registration in `packages/cloudflare/src/integrations/tracing/{provider}.ts` — no OTel, call processors immediately
60-
61-
Reference: `packages/node/src/integrations/tracing/vercelai/`
62-
63-
## Pattern 2: OTel Instrumentation (Client Wrapping)
64-
65-
**Use when:** SDK has no native OTel support (OpenAI, Anthropic, Google GenAI)
66-
67-
1. **Core:** Create `instrument{Provider}Client()` in `packages/core/src/tracing/{provider}/index.ts` — Proxy to wrap client methods, create spans manually
68-
2. **Node.js `instrumentation.ts`:** Patch module exports, wrap client constructor. Check `_INTERNAL_shouldSkipAiProviderWrapping()` for LangChain compatibility.
69-
3. **Node.js `index.ts`:** Export integration function using `generateInstrumentOnce()` helper
70-
71-
Reference: `packages/node/src/integrations/tracing/openai/`
72-
73-
## Pattern 3: Callback/Hook Based
74-
75-
**Use when:** SDK provides lifecycle hooks (LangChain, LangGraph)
76-
77-
1. **Core:** Create `create{Provider}CallbackHandler()` — implement SDK's callback interface, create spans in callbacks
78-
2. **Node.js `instrumentation.ts`:** Auto-inject callbacks by patching runnable methods. Disable underlying AI provider wrapping.
20+
```
21+
Does the SDK publish its own `diagnostics_channel` telemetry?
22+
|- YES (ai >= 7) -> Pattern 1: Native tracing channel
23+
+- NO -> Does the SDK expose callback/exporter hooks?
24+
|- YES (LangChain, Mastra) -> Pattern 3: Callback/Exporter
25+
+- NO (OpenAI, Anthropic, Google GenAI, ai < 7) -> Pattern 2: Orchestrion-injected channels
26+
```
7927

80-
Reference: `packages/node/src/integrations/tracing/langchain/`
28+
| Pattern | Use when | Reference |
29+
| -------------------------- | ------------------------------------------ | --------------------------------------------------------------- |
30+
| 1 — Native tracing channel | the SDK publishes to `diagnostics_channel` | `integrations/vercel-ai/vercel-ai-dc-subscriber.ts` |
31+
| 2 — Orchestrion channels | the SDK has no telemetry of its own | `integrations/openai.ts` + `orchestrion/config/openai.ts` |
32+
| 3 — Callback/exporter | the SDK exposes hooks or an exporter | `ai/langchain/`, `ai/mastra/` (exporter-shaped agent framework) |
8133

82-
## Auto-Instrumentation (Node.js)
34+
What the reference files won't tell you:
8335

84-
**Mandatory** for Node.js AI integrations. OTel only patches when the package is imported (zero cost if unused).
36+
- A provider can need two patterns at once: `vercelAIIntegration` subscribes to native `ai:telemetry` for `ai` >= 7 _and_ runs orchestrion injection for v4-v6.
37+
- Pattern 1 subscribers are safe to register unconditionally — subscribing is a no-op on SDK versions that never publish.
8538

86-
### Steps
39+
## Where The Code Goes
8740

88-
1. **Add to `getAutoPerformanceIntegrations()`** in `packages/node/src/integrations/tracing/index.ts` — LangChain MUST come first
89-
2. **Add to `getOpenTelemetryInstrumentationToPreload()`** for OTel-based integrations
90-
3. **Export from `packages/node/src/index.ts`**: integration function + options type
91-
4. **Add E2E tests:**
92-
- Node.js: `dev-packages/node-integration-tests/suites/tracing/{provider}/`
93-
- Cloudflare: `dev-packages/cloudflare-integration-tests/suites/tracing/{provider}/`
94-
- Browser: `dev-packages/browser-integration-tests/suites/tracing/ai-providers/{provider}/`
41+
- **Instrumentation** -> `packages/server-utils/src/ai/{provider}/`
42+
- **Integration** -> `packages/server-utils/src/integrations/{provider}.ts`
43+
- Runtime packages (`node`, `cloudflare`, `bun`, ...) re-export from `@sentry/server-utils` — they never define their own
44+
- Exception: Workers AI is client-wrapped in `packages/cloudflare/src/instrumentations/worker/instrumentEnv.ts`
9545

96-
## Key Rules
46+
## Gotchas
9747

98-
1. Respect `dataCollection.genAI` for recording input and output messages
99-
2. Set `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN = 'auto.ai.{provider}'` (alphanumerics, `_`, `.` only)
100-
3. Truncate large data with helper functions from `utils.ts`
101-
4. `gen_ai.invoke_agent` for parent ops, `gen_ai.chat` for child ops
48+
1. **Detect streaming from the result shape** — an async-iterable or the SDK's stream object — not from `params.stream`. Only the manual `instrument{Provider}Client()` API keys off `params.stream === true`.
49+
2. **Never set streamed response attributes by hand.** Accumulate into a `StreamResponseState` and call `endStreamSpan()` (`ai/openai/streaming.ts` for an async generator, `ai/anthropic-ai/streaming.ts` for a listener-based stream).
50+
3. **Never truncate message payloads.** Truncation was removed in v11 (#23045) and nothing downstream caps them; size limiting is server-side.
51+
4. **Never roll child token usage up onto parent spans.** Tree totals are computed product-side from the full span tree.
52+
5. **Never read `dataCollection.genAI` directly.** Gate input/output recording on `resolveAIRecordingOptions()`.
53+
6. **LangChain must be registered first** in `getTracingIntegrations()`, so it can disable the provider integrations before they instrument.
54+
7. Set `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN = 'auto.ai.{provider}'` (alphanumerics, `_`, `.` only).
10255

10356
## Checklist
10457

105-
- [ ] Runtime-specific code placed only in that runtime's package
106-
- [ ] Added to `getAutoPerformanceIntegrations()` in correct order (Node.js)
107-
- [ ] Added to `getOpenTelemetryInstrumentationToPreload()` (Node.js with OTel)
108-
- [ ] Exported from appropriate package index
109-
- [ ] E2E tests added and verifying auto-instrumentation
110-
- [ ] Only used attributes from [Sentry Gen AI Conventions](https://getsentry.github.io/sentry-conventions/attributes/gen_ai/)
111-
- [ ] JSDoc says "enabled by default" or "not enabled by default"
112-
- [ ] Documented how to disable (if auto-enabled)
113-
- [ ] Verified OTel only patches when package imported (Node.js)
114-
115-
## Reference Implementations
116-
117-
- **Pattern 1 (Span Processors):** `packages/node/src/integrations/tracing/vercelai/`
118-
- **Pattern 2 (Client Wrapping):** `packages/node/src/integrations/tracing/openai/`
119-
- **Pattern 3 (Callback/Hooks):** `packages/node/src/integrations/tracing/langchain/`
58+
- [ ] Instrumentation in `src/ai/`, integration in `src/integrations/`, registered in `getTracingIntegrations()` (LangChain first)
59+
- [ ] Exported from `packages/server-utils/src/index.ts`, re-exported from the supported runtime packages
60+
- [ ] E2E tests in `dev-packages/node-integration-tests/suites/tracing/{provider}/` (and `cloudflare-integration-tests/` if supported)
61+
- [ ] Ops and attributes from `@sentry/conventions`, op derived via `getGenAiSpanOp()`
62+
- [ ] Recording gated on `resolveAIRecordingOptions()`; no truncation, no token rollup
63+
- [ ] JSDoc names the channels subscribed to, the supported SDK versions, and — for Pattern 2 — that it requires the Sentry runtime hook or bundler plugin
64+
- [ ] Patching happens only once the target package is imported (zero cost if unused)
12065

12166
**When in doubt, follow the pattern of the most similar existing integration.**

‎.cursor/BUGBOT.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ Keep reviews high-signal. Prefer actionable, high-confidence findings over specu
6868
- `consoleSandbox(() => { console.warn(...) })` for intentional user-facing warnings (e.g. init-time misconfiguration messages). The `consoleSandbox` wrapper prevents the SDK's own console instrumentation from intercepting the call. Bare `console.*` calls outside very early init paths (e.g. before the logger is available) should be flagged.
6969
- Flag `url.full`, `url.query`, `http.target` or `request.query_string` being set from a URL that isn't filtered. Wrap the value in `filterCollectedUrl()` (or `filterCollectedUrlQuery()` for a bare query string), passing the `client` if one is in scope, so `dataCollection.urlQueryParams` applies. Values that can't contain a query (a bare pathname, a queue URL) are fine. The `sdk/no-unfiltered-url-attributes` lint rule catches direct attribute writes, so look for what it can't: URLs passed through a helper or variable first, deprecated aliases set next to a filtered attribute, and URLs on breadcrumbs or events instead of spans.
7070
- Flag span names built from a raw URL. Names follow `METHOD scheme://host/path` and must never contain a query string, so they need `stripUrlQueryAndFragment()`, not `filterCollectedUrl()`.
71+
- Flag a SQL statement that reaches telemetry unsanitized. `db.query.text`, `db.query.summary`, a DB span name, and a breadcrumb carrying a statement all have to come from `sanitizeSqlQuery()` or `sanitizeSqlQueryWithSummary()` (`@sentry/server-utils`). Inline literals are user data, and OTel allows collecting query text only once they are replaced with `?`. This is deliberately not gated on `dataCollection.databaseQueryData`, which does not cover query text.
72+
- Cover every place the statement lands, not only the span attribute. The two that get forgotten are the breadcrumb beside the span and the span name used when span streaming is off.
73+
- Sanitize each statement of a batch before joining them.
74+
- Pass the dialect. `toSqlDialect()` maps a driver or `db.system.name` value to one, and a missing dialect leaves MySQL and SQL Server values in the statement.
75+
- Leave Redis command text alone. It is not SQL and has its own redaction path.
7176
- Flag usage of the following APIs: `getCurrentScope()`, `getIsolationScope()`, `getClient()` if they are avoidable. Flag it with severity Low and acknowledge from the start that this is more a "is this necessary" check, rather than a rule violation.
7277
- Reason for flagging: Usage of these APIs is problematic for multi-client setups where either there is no "current" client/scope, or the wrong client might be used. Calling these APIs would create a current scope, thereby misleading any future calls to these APIs.
7378
- What to do instead: Use an existing reference to the scope or client. For example, this is possible in most `Integration` hooks.

‎AGENTS.md‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,17 @@ Uses **Git Flow** (see `docs/gitflow.md`).
6464
- **Omit the "Summary" heading** in PR bodies — lead with the summary text itself, no `## Summary` header.
6565
- Include `Fixes #<issue-number>` somewhere in the PR body so the merge auto-closes the linked issue.
6666
- Always open PRs as draft.
67-
- Include reasoning of changes in the PR description, as well as decisions that were taken during implementation. Do not explain the implementation that can be viewed in the code.
67+
- **Keep PR descriptions condensed** — usually a few sentences. Explain the _reasoning_: why the change is needed, and any non-obvious decisions or tradeoffs made along the way. Do **not** walk through the implementation or describe what changed file-by-file — the diff already shows that. Only go longer when the decisions genuinely warrant it.
6868

6969
## Architecture
7070

71-
- `packages/types/` is **deprecated — never modify it**. Types live in
72-
`packages/core/`.
73-
- An AI provider integration spans three places: core instrumentation in
74-
`packages/core/src/tracing/{provider}/`, the Node integration in
75-
`packages/node/src/integrations/tracing/{provider}/`, and the edge
76-
runtime in `packages/cloudflare/src/integrations/tracing/{provider}.ts`.
71+
- Types live in `packages/core/`. The `@sentry/types` package is gone.
72+
- An AI provider integration spans two places, both in
73+
`packages/server-utils/`: the gen-AI instrumentation logic in
74+
`src/ai/{provider}/`, and the integration that wires it up in
75+
`src/integrations/{provider}.ts`, registered in `getTracingIntegrations()`.
76+
Runtime packages (`node`, `cloudflare`, ...) re-export from
77+
`@sentry/server-utils` rather than defining their own.
7778

7879
## Linting & Formatting
7980

@@ -90,7 +91,7 @@ Uses **Git Flow** (see `docs/gitflow.md`).
9091
- Only use libraries already in the codebase
9192
- Never expose secrets or keys
9293
- When modifying files, cover all occurrences (including `src/` and `test/`)
93-
- Comments explain **why**, never **what** — never add a comment that restates what the code does or describes the change being made; only comment when the reasoning isn't obvious from the code itself
94+
- **Write few comments; default to none.** Comments explain **why**, never **what** — never add a comment that restates what the code does or narrates the change being made. Only add one when the reasoning isn't clear from the code itself, or to flag a tradeoff or something that would otherwise look surprising to a reader. When in doubt, leave it out.
9495
- Do not use `expect(someSpy.mock.calls[0]?.[0])` or similar constructs to check what a spy was called with.
9596
Instead use `expect(someSpy).toHaveBeenCalledWith(...)` or derivatives for a more readable and less brittle test assertion.
9697

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Work in this release was contributed by @psh4607, @thijsw, @trinitiwowka, @nehap
1414
- `DenoMongoose` => `Mongoose`
1515
- `DenoMysql` => `Mysql`
1616
- `DenoPostgres` => `Postgres`
17+
- feat(node): Add first-party Mastra integration ([#23823](https://github.com/getsentry/sentry-javascript/pull/23823)). Enabled by default; disable with `defaultIntegrations: integrations => integrations.filter(i => i.name !== 'Mastra')`.
1718
- **feat(browser): Add `bfcacheMetricsIntegration` to track back/forward cache health**
1819

1920
The new opt-in `bfcacheMetricsIntegration` emits metrics about browser back/forward cache (bfcache) navigations, so you can

0 commit comments

Comments
 (0)