Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 54 additions & 22 deletions docs/platforms/javascript/guides/cloudflare/features/agents-sdk.mdx
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
---
title: Cloudflare Agents SDK
description: "Link Cloudflare Agents SDK chats to Sentry Conversations with a chat session ID."
description: "Instrument Cloudflare Agents SDK classes with Sentry and link chats to Conversations."
---

When you build agents with the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/), set a conversation ID so multi-turn AI activity groups correctly in <Link to="/product/agents/conversations/">Conversations</Link>.
<AvailableSince version="10.69.0" />

Use **your chat session ID** (for example a UUID or `conv_...` value your app creates when the user starts a chat). Do not use a user ID, room name, or the Durable Object instance name unless that name is already one chat session.
When you build agents with the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/), wrap your agent classes with `instrumentAgentWithSentry`. Agents are Durable Objects under the hood, so the wrapper applies everything <PlatformLink to="/features/durableobject/">Durable Object instrumentation</PlatformLink> does (request transactions, alarms, WebSocket handlers, RPC trace propagation), plus agent-specific telemetry:

Workers AI does **not** set this for you. Set the ID on the isolation scope that runs your AI work — for chat agents, that is typically at the start of `onChatMessage`, before any model or tool calls.
- **Callable RPC spans**: a span for each `@callable()` method invoked over WebSocket, carrying the agent class and instance name as attributes.
- **Automatic conversation IDs**: the SDK sets the conversation ID on every chat turn (`onChatMessage`) and every callable RPC call, so `gen_ai` spans group in <Link to="/product/agents/conversations/">Conversations</Link> without any `setConversationId` call.

## Prerequisites
```typescript
import * as Sentry from "@sentry/cloudflare";
import { Agent, callable } from "agents";

class MyAgentBase extends Agent<Env> {
@callable()
async greet(name: string): Promise<string> {
return `Hello, ${name}!`;
}
}

// Export your named class as defined in your wrangler config
export const MyAgent = Sentry.instrumentAgentWithSentry(
(env: Env) => ({
dsn: "___PUBLIC_DSN___",
tracesSampleRate: 1.0,
enableRpcTracePropagation: true,
}),
MyAgentBase
);
```

- <PlatformLink to="/tracing/">Tracing enabled</PlatformLink>
- Durable Objects instrumented with `instrumentDurableObjectWithSentry` (see <PlatformLink to="/features/durableobject/">Durable Objects</PlatformLink>)
`instrumentAgentWithSentry` works with `Agent` from `agents`, `AIChatAgent` from `@cloudflare/ai-chat`, and `McpAgent` from `agents/mcp`. When you build with the <PlatformLink to="/features/vite-plugin/">Sentry Cloudflare Vite plugin</PlatformLink>'s `autoInstrumentation`, the plugin detects and wraps Agent classes automatically.

## Link conversations
## Conversation IDs

Set the conversation ID before any AI calls in the chat handler:
The automatic conversation ID defaults to the **agent instance name**, which is correct when one agent instance is one chat session (for example `useAgent({ name: chatSessionId })`). When the chat is cleared (`clearHistory()` from `useAgentChat`, or anything that emits the [`message:clear` observability event](https://developers.cloudflare.com/agents/runtime/operations/observability/#channels)), the SDK rotates to a fresh conversation ID, so a reset chat groups as a new conversation.

If your instances are per-user or a shared singleton like `"default"`, the default would group unrelated chats into one conversation. Override it with your own chat session ID at the start of `onChatMessage`, before any model or tool calls:

```typescript
import * as Sentry from "@sentry/cloudflare";
Expand All @@ -30,6 +52,25 @@ class MyChatAgentBase extends AIChatAgent<Env> {
// … run your model and tools …
}
}
```

Use a real chat session ID, such as a UUID or `conv_...` value your app creates when the user starts a chat. Do not use a user ID or room name: those group unrelated chats into one conversation. To clear the ID, call `Sentry.setConversationId(null)`.

## SDK Versions Before 10.69.0

On older SDK versions, wrap agent classes with `instrumentDurableObjectWithSentry` instead and set the conversation ID manually at the start of the handler (`onRequest` for `Agent`, `onChatMessage` for `AIChatAgent`), before any AI calls:

```typescript
import * as Sentry from "@sentry/cloudflare";
import { AIChatAgent } from "@cloudflare/ai-chat";

class MyChatAgentBase extends AIChatAgent<Env> {
async onChatMessage() {
Sentry.setConversationId("conv_abc123");

// … run your model and tools …
}
}

export const MyChatAgent = Sentry.instrumentDurableObjectWithSentry(
(env: Env) => ({
Expand All @@ -40,18 +81,6 @@ export const MyChatAgent = Sentry.instrumentDurableObjectWithSentry(
);
```

Define the conversation ID however your app tracks chats (client param, storage, etc.). Re-set it at the start of each handler that performs AI work so it applies on that isolation scope.

If you already name each agent instance as one chat session (for example `useAgent({ name: chatSessionId })`), you can pass that value:

```typescript
Sentry.setConversationId(this.name);
```

Only do this when the instance name is **one conversation**, not per-user or a shared singleton like `"default"`.

To clear the ID: `Sentry.setConversationId(null)`.

## Users

Populate the Conversations **User** column with `Sentry.setUser` on every request or handler that performs AI calls, before those calls run. See <PlatformLink to="/agent-tracing/#tracking-conversations">Tracking Conversations</PlatformLink>.
Expand All @@ -60,5 +89,8 @@ Populate the Conversations **User** column with `Sentry.setUser` on every reques

- <PlatformLink to="/features/workers-ai/">Workers AI</PlatformLink>
- <PlatformLink to="/features/durableobject/">Durable Objects</PlatformLink>
- <PlatformLink to="/agent-tracing/#tracking-conversations">Tracking Conversations</PlatformLink>
- <PlatformLink to="/features/vite-plugin/">Vite Plugin</PlatformLink>
- <PlatformLink to="/agent-tracing/#tracking-conversations">
Tracking Conversations
</PlatformLink>
- <Link to="/product/agents/conversations/">Conversations</Link> product docs
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,13 @@ export const MyDurableObject = Sentry.instrumentDurableObjectWithSentry(

`instrumentDurableObjectWithSentry` works with any class that extends `DurableObject`, not only those that extend it directly. This includes classes built on top of Durable Objects by other libraries.

For example, the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/) builds its `Agent` and `AIChatAgent` classes on top of Durable Objects, so you can instrument them the same way:
For classes built on the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/) (`Agent`, `AIChatAgent`, `McpAgent`), use the dedicated `instrumentAgentWithSentry` wrapper instead (SDK version 10.69.0 or higher). It applies the same Durable Object instrumentation and adds spans for `@callable()` RPC methods as well as automatic conversation IDs. See <PlatformLink to="/features/agents-sdk/">Cloudflare Agents SDK</PlatformLink>.

```typescript
import * as Sentry from "@sentry/cloudflare";
import { Agent } from "agents";
## Durable Object Storage Spans

class MyAgentBase extends Agent<Env> {
// impl
}
`instrumentDurableObjectWithSentry` automatically instruments Durable Object storage operations (`get`, `put`, `delete`, `list`). Each storage operation creates a span.

// Export your named class as defined in your wrangler config
export const MyAgent = Sentry.instrumentDurableObjectWithSentry(
(env: Env) => ({
dsn: "___PUBLIC_DSN___",
tracesSampleRate: 1.0,
}),
MyAgentBase
);
```
The SDK also filters out framework-internal storage operations (version 10.69.0 or higher): keys prefixed with `cf_`, `cf:`, `__ps_`, or `/` (used internally by the Agents SDK and PartyServer) don't create spans, so your traces show only your own storage access.

## RPC Trace Propagation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ description: "Learn how to use the Sentry Cloudflare Vite plugin to instrument b
<AvailableSince version="10.68.0" />

<Alert>
The Sentry Cloudflare Vite plugin has **experimental** stability. Configuration
options and behavior may change or be removed in any release.
The Sentry Cloudflare Vite plugin has **experimental** stability.
Configuration options and behavior may change or be removed in any release.
</Alert>

The Sentry Cloudflare Vite plugin (`sentryCloudflareVitePlugin`) instruments your Worker at build time. It can:

1. **Instrument bundled dependencies** automatically instruments supported packages in your bundle (such as database clients like `mysql`) at build time, giving you more traces out of the box.
2. **Auto-instrument your Worker entry**optionally wraps your default export with `Sentry.withSentry()` and Durable Objects with `instrumentDurableObjectWithSentry()` at build time, so you don't need to modify your code.
1. **Instrument bundled dependencies**: automatically instruments supported packages in your bundle (such as database clients like `mysql`) at build time, giving you more traces out of the box.
2. **Auto-instrument your Worker entry**: optionally wraps your default export with `Sentry.withSentry()`, and Durable Object, Workflow, and Agents SDK classes with the matching `instrument*WithSentry` helper at build time, so you don't need to modify your code.

**We recommend building your Cloudflare Worker with Vite and the `sentryCloudflareVitePlugin` plugin.** It's the most complete way to get tracing for bundled dependencies in the Workers runtime. If you already deploy with `wrangler` directly, see [Migrating From Wrangler](#migrating-from-wrangler).

Expand Down Expand Up @@ -65,7 +65,7 @@ export default Sentry.withSentry(

### Auto-instrumentation (Experimental)

Alternatively, the plugin can wrap your Worker for you at build time, so you don't need `withSentry` in your code. Enable `autoInstrumentation` and the plugin reads your `wrangler.(jsonc|toml)` to find the entry point, Durable Objects, and workflows:
Alternatively, the plugin can wrap your Worker for you at build time, so you don't need `withSentry` in your code. Enable `autoInstrumentation` and the plugin reads your wrangler config (probing `wrangler.json`, `wrangler.jsonc`, and `wrangler.toml` at the Vite root, or the file set with [`wranglerConfigPath`](#options)) to find the entry point, Durable Objects, workflows, and Agents SDK classes. The plugin wraps Agents SDK classes (`Agent`, `AIChatAgent`, `McpAgent`) with `instrumentAgentWithSentry` (SDK version 10.69.0 or higher), which also gives them automatic conversation IDs (see <PlatformLink to="/features/agents-sdk/">Cloudflare Agents SDK</PlatformLink>).

```typescript {filename:vite.config.ts}
import { cloudflare } from "@cloudflare/vite-plugin";
Expand Down Expand Up @@ -100,6 +100,26 @@ If no `instrument.server.*` file exists, the SDK reads all configuration (DSN, r

## Options

<SdkOption name="wranglerConfigPath" type="string" availableSince="10.69.0">

Path to your wrangler config file. By default the plugin probes `wrangler.json`, `wrangler.jsonc`, and `wrangler.toml` at the Vite root. Set this when your config lives at a custom path, for example to mirror the `configPath` option of the Cloudflare Vite plugin:

```typescript {filename:vite.config.ts}
export default defineConfig({
plugins: [
cloudflare({ configPath: "./wrangler.agent.jsonc" }),
sentryCloudflareVitePlugin({
wranglerConfigPath: "./wrangler.agent.jsonc",
_experimental: {
autoInstrumentation: true,
},
}),
],
});
```

</SdkOption>

<SdkOption name="_experimental" type="object">

Experimental options that may change or be removed without notice.
Expand All @@ -108,7 +128,7 @@ Experimental options that may change or be removed without notice.

<SdkOption name="_experimental.autoInstrumentation" type="boolean" defaultValue="false">

Automatically wraps your Worker at build time so you don't have to edit your entry. The plugin reads your wrangler config, wraps the default export with `Sentry.withSentry()` (sourcing options from a co-located `instrument.server.*` file, falling back to `env`), and wraps any configured Durable Object and Workflow classes with the matching `instrument*WithSentry` helper. Both `vite build` and `vite dev` are instrumented.
Automatically wraps your Worker at build time so you don't have to edit your entry. The plugin reads your wrangler config, wraps the default export with `Sentry.withSentry()` (sourcing options from a co-located `instrument.server.*` file, falling back to `env`), and wraps configured classes with the matching helper: Durable Objects with `instrumentDurableObjectWithSentry`, Workflows with `instrumentWorkflowWithSentry`, and Agents SDK classes with `instrumentAgentWithSentry` (SDK version 10.69.0 or higher). Both `vite build` and `vite dev` are instrumented.

</SdkOption>

Expand All @@ -125,4 +145,4 @@ If you deploy with `wrangler` directly, moving to Vite is straightforward:
1. Set up the [Cloudflare Vite plugin](https://developers.cloudflare.com/workers/vite-plugin/get-started/) and add a `vite.config.ts` with the `cloudflare()` and `sentryCloudflareVitePlugin()` plugins as shown above.
2. Run `vite build` before `wrangler deploy`, and use `vite dev` in place of `wrangler dev` for local development.

Your existing `wrangler.jsonc` becomes the input config the plugin generates the deployed output during the build. For the full list of fields that change or become redundant, see Cloudflare's [Migrating from Wrangler](https://developers.cloudflare.com/workers/vite-plugin/reference/migrating-from-wrangler-dev/) guide.
Your existing `wrangler.jsonc` becomes the input config, and the plugin generates the deployed output during the build. For the full list of fields that change or become redundant, see Cloudflare's [Migrating from Wrangler](https://developers.cloudflare.com/workers/vite-plugin/reference/migrating-from-wrangler-dev/) guide.
Loading