Skip to content

test(cloudflare): Add e2e test app for @cloudflare/think - #24660

Open
RulaKhaled wants to merge 2 commits into
developfrom
feat/cloudflare-think-e2e
Open

RulaKhaled wants to merge 2 commits into
developfrom
feat/cloudflare-think-e2e

Conversation

@RulaKhaled

@RulaKhaled RulaKhaled commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

Adds a cloudflare-think e2e app that records what a Think agent produces with the SDK as it stands today, before any Think-specific code exists. Same shape as node-eve and cloudflare-mastra.

The setup is the whole point: the worker imports no Sentry API at all.

// vite.config.mts
export default defineConfig({
  plugins: [cloudflare(), sentryCloudflareVitePlugin()],
});
// src/index.ts
export class ThinkAgent extends Think<Env> {
  getModel() { return openrouter('openai/gpt-4o-mini'); }
  getSystemPrompt() { /* ... */ }
  getTools() { /* get_weather, fail_now */ }
}

sentryCloudflareVitePlugin() detects extends Think and wraps the export at build time, and ai publishes the telemetry vercelAIIntegration already consumes. Options come from src/instrument.server.ts by convention.

What works today

Five checks, the same set the other AI apps cover:

  1. The gen_ai hierarchy. gen_ai.invoke_agent, one gen_ai.generate_content per step, and gen_ai.execute_tool, correctly nested and all with origin auto.vercelai.channel. Token usage is right per step and rolls up onto the agent span; prompts, tool arguments and results, tool definitions and finish reasons are all present.
  2. Tool errors. A throw becomes an issue with mechanism auto.vercelai.channel and marks only its own span errored, leaving the model calls ok.
  3. Manual spans. A Sentry.startSpan raised inside a tool nests under that tool's span, so Think runs tools inside the async context the SDK opened.
  4. The provider request. The outgoing model call joins the turn's trace rather than running beside it.
  5. Other instrumented libraries. dataloader spans land in the same trace, so the orchestrion module transform reaches a bundled Think worker with no --import bootstrap, unlike the Node apps.

What does not

The provider HTTP call nests under gen_ai.generate_content on ai v7 but is a sibling of it on v4 to v6. That difference is ours: only v7's native channel binds the model-call span into async context. Asserted per lane so the gap is recorded rather than hidden.

The agent span is named from this.constructor.name, which the Vite plugin's class rename currently mangles into invoke_agent __SENTRY_ORIGINAL_ThinkAgent__. Not asserted here. A follow-up fixes it and adds the assertion.

There is no gen_ai.agent.name, and none of the cloudflare.agents.turn.* context Think supplies (agent id, Durable Object conversation id, turn trigger, continuation) reaches a span. Think sends all of it, as runtimeContext on v7 and experimental_telemetry.metadata on v6, and neither subscriber reads either field.

Notes

sentryTest.optional: true keeps this in the optional matrix like node-eve, since it makes real OpenRouter calls. The v6 lane rides along as an optionalVariants entry rather than a second app. E2E_OPENROUTER_API_KEY is already wired in build.yml from the eve PR, so CI needs no change.

ref JS-3568

🤖 Generated with Claude Code

Records what a Think agent produces with the current SDK, before any
Think-specific code exists. The worker imports no Sentry API at all:
`sentryCloudflareVitePlugin()` detects `class ThinkAgent extends Think` and
wraps the export at build time, and `ai` publishes the telemetry
`vercelAIIntegration` already consumes.

Covers the same five things the other AI apps do: the gen_ai hierarchy, a tool
throw becoming an issue with an errored span, a manual span nesting under the
tool span, the provider request staying inside the turn, and `dataloader`
proving the orchestrion transform reaches a bundled worker.

Runs on `ai` v7 by default with a v6 lane as an optional variant, because
Think's peer range spans both and the two produce different trace shapes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Sep 23, 2026

Copy link
Copy Markdown

JS-3568

@github-actions

github-actions Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.24 kB - -
@sentry/browser - with treeshaking flags 27.5 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.4 kB - -
@sentry/browser (incl. Tracing) 51.15 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 51.17 kB - -
@sentry/browser (incl. Tracing, Profiling) 54.18 kB - -
@sentry/browser (incl. Tracing, Replay) 90.76 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.86 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 95.46 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 108.41 kB - -
@sentry/browser (incl. Feedback) 46.76 kB - -
@sentry/browser (incl. sendFeedback) 34.3 kB - -
@sentry/browser (incl. FeedbackAsync) 39.41 kB - -
@sentry/browser (incl. Metrics) 30.25 kB - -
@sentry/browser (incl. Logs) 30.51 kB - -
@sentry/browser (incl. Metrics & Logs) 31.18 kB - -
@sentry/react 31 kB - -
@sentry/react (incl. Tracing) 53.45 kB - -
@sentry/vue 36.74 kB - -
@sentry/vue (incl. Tracing) 53.7 kB - -
@sentry/svelte 29.26 kB - -
CDN Bundle 30.93 kB - -
CDN Bundle (incl. Tracing) 51.69 kB - -
CDN Bundle (incl. Logs, Metrics) 33.2 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.66 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.92 kB - -
CDN Bundle (incl. Tracing, Replay) 89.28 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.25 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 95.45 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 97.42 kB - -
CDN Bundle - uncompressed 91.4 kB - -
CDN Bundle (incl. Tracing) - uncompressed 153.77 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.97 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 159.73 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.54 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 273.5 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 279.44 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 287.2 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 293.13 kB - -
@sentry/nextjs (client) 55.77 kB - -
@sentry/sveltekit (client) 51.59 kB - -
@sentry/core/server 39.95 kB - -
@sentry/core/browser 13.63 kB - -
@sentry/node 133.88 kB +0.01% +6 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.4 kB - -
@sentry/node - without tracing 90.43 kB +0.02% +16 B 🔺
@sentry/node - without channel injection 112.41 kB +0.01% +11 B 🔺
@sentry/aws-serverless 98.69 kB +0.01% +7 B 🔺
@sentry/cloudflare (withSentry) - minified 206.49 kB - -
@sentry/cloudflare (withSentry) 513.73 kB - -

View base workflow run

@RulaKhaled
RulaKhaled marked this pull request as ready for review September 24, 2026 08:51
Comment thread dev-packages/e2e-tests/test-applications/cloudflare-think/tests/think.test.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9796d9c. Configure here.

Comment thread dev-packages/e2e-tests/test-applications/cloudflare-think/playwright.config.ts Outdated
`getPlaywrightConfig` already defaults to `workers: 1` and a 30s timeout. The app
overrode both, so five tests each driving a live OpenRouter turn ran in parallel
under a cap meant for mocked models. That came from copying
`cloudflare-vercelai-v7`, which mocks its model; the two apps that call a real
provider, `cloudflare-mastra` and `node-flue`, keep the serial default and raise
the timeout to 90s instead.

Also corrects the comment above the first test, which still claimed the worker
imports no Sentry API. That stopped being true when the manual-span test was
added, and the worker now imports `startSpan` for exactly that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment on lines +59 to +60
execute: async (_input: { reason: string }): Promise<string> => {
throw new Error('Think tool failed on purpose');

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 runTurn method can return a result where continuation is undefined due to a race condition. This value is used without a check, leading to an incorrect API response.
Severity: MEDIUM

Suggested Fix

Add a check to ensure result and result.continuation are not undefined before returning the response. If result.continuation is missing, consider throwing an error or returning a more explicit error response to the client instead of an empty object. For example: if (!result?.continuation) { throw new Error('Failed to get continuation from agent turn.'); } return Response.json({ continuation: result.continuation });.

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: dev-packages/e2e-tests/test-applications/cloudflare-think/src/index.ts#L59-L60

Potential issue: In
`dev-packages/e2e-tests/test-applications/cloudflare-think/src/index.ts`, the
`onRequest` method calls `runTurn` and then directly accesses `result.continuation` to
build the JSON response. Due to a known race condition in the `@cloudflare/think`
library, concurrent agent turns can cause `runTurn` to resolve with a result where the
`continuation` property is `undefined`. When this happens, `Response.json({
continuation: undefined })` serializes to an empty object `{}`, breaking the API
contract and causing silent failures for clients that expect the `continuation` field.
This race condition is reproducible in the e2e test environment.

This branch has not been deployed

No deployments
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.

1 participant