Skip to content

feat(cloudflare): Instrument Agents automatically - #22727

Merged
JPeer264 merged 2 commits into
jp/cloudflare-instrument-reset-conv-idfrom
jp/cloudflare-instrument-agent-automatically
Jul 28, 2026
Merged

feat(cloudflare): Instrument Agents automatically#22727
JPeer264 merged 2 commits into
jp/cloudflare-instrument-reset-conv-idfrom
jp/cloudflare-instrument-agent-automatically

Conversation

@JPeer264

Copy link
Copy Markdown
Member

Since instrumentAgentWithSentry is here, it also needs to be supported within the Vite plugin and its autoInstrument flag. The issue here is that we need to parse through the imports in order to understand where the Agent import is coming from. (The majority of the changes are tests again)

It could also be that external libraries are extending from Agent, such as McpAgent, and these need to be wrapped too. It is super hacky but it works.

For the tests I wished to add them in the cloudflare-integration-tests, but unfortunately agents needs Node v20+ and locally we run with v18, so it wouldn't work without either bumping Nodejs or making the tests their own package so they are independent.

@JPeer264 JPeer264 self-assigned this Jul 28, 2026
@JPeer264 JPeer264 changed the title feat(cloudflare): Add instrumentAgentWithSentry for Cloudflare Agents feat(cloudflare): Instrument Agents automatically Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 29.85 kB +0.04% +9 B 🔺
@sentry/browser - with treeshaking flags 28.05 kB +0.03% +8 B 🔺
@sentry/browser (incl. Tracing) 47.42 kB +0.03% +11 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 47.43 kB +0.03% +10 B 🔺
@sentry/browser (incl. Tracing, Profiling) 52.16 kB +0.03% +11 B 🔺
@sentry/browser (incl. Tracing, Replay) 86.74 kB +0.01% +6 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 76.16 kB +0.02% +9 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 91.48 kB +0.01% +8 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 104.11 kB +0.01% +9 B 🔺
@sentry/browser (incl. Feedback) 47.16 kB +0.02% +8 B 🔺
@sentry/browser (incl. sendFeedback) 34.69 kB +0.03% +9 B 🔺
@sentry/browser (incl. FeedbackAsync) 39.78 kB +0.03% +8 B 🔺
@sentry/browser (incl. Metrics) 30.92 kB +0.03% +7 B 🔺
@sentry/browser (incl. Logs) 31.14 kB +0.02% +6 B 🔺
@sentry/browser (incl. Metrics & Logs) 31.83 kB +0.02% +6 B 🔺
@sentry/react 31.64 kB +0.03% +7 B 🔺
@sentry/react (incl. Tracing) 49.64 kB +0.02% +5 B 🔺
@sentry/vue 34.77 kB +0.03% +8 B 🔺
@sentry/vue (incl. Tracing) 49.37 kB +0.02% +9 B 🔺
@sentry/svelte 29.88 kB +0.04% +9 B 🔺
CDN Bundle 31.89 kB +0.02% +5 B 🔺
CDN Bundle (incl. Tracing) 47.76 kB +0.01% +2 B 🔺
CDN Bundle (incl. Logs, Metrics) 33.44 kB +0.02% +4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 49.14 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 72.8 kB +0.01% +7 B 🔺
CDN Bundle (incl. Tracing, Replay) 85.4 kB +0.01% +6 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.68 kB +0.01% +4 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 91.17 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.47 kB +0.01% +8 B 🔺
CDN Bundle - uncompressed 95.12 kB +0.04% +38 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 143.26 kB +0.03% +41 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.83 kB +0.04% +38 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 147.25 kB +0.03% +41 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 224.6 kB +0.02% +41 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 262.52 kB +0.02% +41 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 266.49 kB +0.02% +41 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 276.23 kB +0.02% +41 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 280.18 kB +0.02% +41 B 🔺
@sentry/nextjs (client) 52.23 kB +0.01% +5 B 🔺
@sentry/sveltekit (client) 47.84 kB +0.03% +10 B 🔺
@sentry/core/server 79.73 kB -0.06% -47 B 🔽
@sentry/core/browser 51.61 kB +0.02% +7 B 🔺
@sentry/node 122.35 kB +1.07% +1.29 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 166 B - -
@sentry/node - without tracing 85.75 kB +1.51% +1.27 kB 🔺
@sentry/aws-serverless 93.78 kB +1.16% +1.07 kB 🔺
@sentry/cloudflare (withSentry) - minified 197.53 kB +0.01% +11 B 🔺
@sentry/cloudflare (withSentry) 485.64 kB -0.03% -124 B 🔽

View base workflow run

@JPeer264
JPeer264 marked this pull request as ready for review July 28, 2026 11:05
@JPeer264
JPeer264 requested a review from a team as a code owner July 28, 2026 11:05
@JPeer264
JPeer264 requested review from andreiborza, isaacs, mydea and timfish and removed request for a team July 28, 2026 11:05
@andreiborza

Copy link
Copy Markdown
Member

For the tests I wished to add them in the cloudflare-integration-tests, but unfortunately agents needs Node v20+ and locally we run with v18, so it wouldn't work without either bumping Nodejs or making the tests their own package so they are independent.

What do you mean we run with v18 locally? Why can't we bump now that we're on v11?

@JPeer264

Copy link
Copy Markdown
Member Author

What do you mean we run with v18 locally? Why can't we bump now that we're on v11?

Because I need to backport that PR. We can change that anytime now ofc

@JPeer264
JPeer264 force-pushed the jp/cloudflare-instrument-agent-automatically branch from cc88548 to 62f5ca2 Compare July 28, 2026 13:36
Comment thread packages/cloudflare/test/vite/autoInstrument.test.ts
@JPeer264
JPeer264 force-pushed the jp/cloudflare-instrument-agent-automatically branch from 62f5ca2 to 8d6a511 Compare July 28, 2026 14:05
Comment on lines +94 to +95
// its base-class chain tells the two apart. Detection walks the module graph (base classes
// usually live in their own file), so it is limited to the configured DO classes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: Does that constraint really hold true? I wouldn't be surprised if someone has several classes in one file. Or am I misunderstanding?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By definition all the classes are in one file, it is just the question if they are defined in the main entrypoint or in other files.

const code = stripComments(rawCode);
const shape = emptyShape();

// `class X extends Y {` / `class X<T> extends Y<T> {`, optionally exported and/or abstract.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about cases where generics extend, e.g. class X<T extends S> extends Y<T extends Z>?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I'm sure there are more edge cases which we didn't think of just yet.
Added in facc163

Comment on lines +213 to +214
const CLASS_RE = /\b(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)([^{]*)\{/g;
for (const match of code.matchAll(CLASS_RE)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The CLASS_RE regex in shapeFromSource() fails to detect anonymous classes like export default class extends Agent {}, causing agent detection to fail for those modules.
Severity: MEDIUM

Suggested Fix

Modify the CLASS_RE regex to make the class name capture group optional. This will allow it to correctly match and parse anonymous default exported classes.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/cloudflare/src/vite/moduleShape.ts#L213-L214

Potential issue: The regex `CLASS_RE` used in `shapeFromSource()` to parse module shapes
requires a class name to be present after the `class` keyword. This means it will not
match an anonymous default export, such as `export default class extends Agent {}`. When
this pattern is used in a module, `defaultExportIsClass` is not set on the module's
shape. Consequently, when `isAgentBinding()` later checks the module, it incorrectly
determines that the default export is not an agent, leading to a failure in agent
detection. The code seems to anticipate this pattern, as `shapeFromAst()` handles it,
but the regex-based fallback does not.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this seems relevant too, speaking of edge-cases @JPeer264

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't export default a DurableObject.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least what I'm aware of*

@JPeer264
JPeer264 merged commit 550b2d8 into develop Jul 28, 2026
81 checks passed
@JPeer264
JPeer264 deleted the jp/cloudflare-instrument-agent-automatically branch July 28, 2026 17:15
JPeer264 added a commit that referenced this pull request Jul 28, 2026
JPeer264 added a commit that referenced this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants