test(cloudflare): Add e2e test app for @cloudflare/think - #24660
RulaKhaled wants to merge 2 commits into
Conversation
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>
size-limit report 📦
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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.
`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>
| execute: async (_input: { reason: string }): Promise<string> => { | ||
| throw new Error('Think tool failed on purpose'); |
There was a problem hiding this comment.
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.

Adds a
cloudflare-thinke2e app that records what a Think agent produces with the SDK as it stands today, before any Think-specific code exists. Same shape asnode-eveandcloudflare-mastra.The setup is the whole point: the worker imports no Sentry API at all.
sentryCloudflareVitePlugin()detectsextends Thinkand wraps the export at build time, andaipublishes the telemetryvercelAIIntegrationalready consumes. Options come fromsrc/instrument.server.tsby convention.What works today
Five checks, the same set the other AI apps cover:
gen_ai.invoke_agent, onegen_ai.generate_contentper step, andgen_ai.execute_tool, correctly nested and all with originauto.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.auto.vercelai.channeland marks only its own span errored, leaving the model callsok.Sentry.startSpanraised inside a tool nests under that tool's span, so Think runs tools inside the async context the SDK opened.dataloaderspans land in the same trace, so the orchestrion module transform reaches a bundled Think worker with no--importbootstrap, unlike the Node apps.What does not
The provider HTTP call nests under
gen_ai.generate_contentonaiv7 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 intoinvoke_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 thecloudflare.agents.turn.*context Think supplies (agent id, Durable Object conversation id, turn trigger, continuation) reaches a span. Think sends all of it, asruntimeContexton v7 andexperimental_telemetry.metadataon v6, and neither subscriber reads either field.Notes
sentryTest.optional: truekeeps this in the optional matrix likenode-eve, since it makes real OpenRouter calls. The v6 lane rides along as anoptionalVariantsentry rather than a second app.E2E_OPENROUTER_API_KEYis already wired inbuild.ymlfrom the eve PR, so CI needs no change.ref JS-3568
🤖 Generated with Claude Code