feat(node): Add eveConversationHook() to link eve sessions as Sentry conversations - #24247
Conversation
size-limit report 📦
|
RulaKhaled
left a comment
There was a problem hiding this comment.
left two comments, otherwise lgtm
|
|
||
| const setConversationIdFromContext: EveHookHandler = (_event, context) => { | ||
| const conversationId = resolveConversationId(context); | ||
| if (conversationId) { |
There was a problem hiding this comment.
does this mean a resolver returning undefined cannot unset the conversation id? setConversationId's documentsnull | undefined
There was a problem hiding this comment.
oops, yeah, should not be that way, I refactored this to just use the value as-is and set it!
| * | ||
| * ```ts | ||
| * // agent/hooks/sentry.ts | ||
| * import * as Sentry from '@sentry/node'; |
There was a problem hiding this comment.
nit: I guess this can also come from non-node packages?
There was a problem hiding this comment.
jup, theoretically, but eve is mostly just documented for node, so I think it's fine to keep this as example/docs here?
There was a problem hiding this comment.
I agree to keep it for node only for now. I don't think Eve would be optimized too much for other runtimes - if we stay with Node for now we have the minimal setup working it's a great start
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.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4bf7875. Configure here.
JPeer264
left a comment
There was a problem hiding this comment.
LGTM. Great points were already raised before.
6110680 to
796048f
Compare
…y conversations
Adds `Sentry.eveConversationHook()`, used as the default export of an eve
`agent/hooks/sentry.ts`:
export default defineHook(Sentry.eveConversationHook());
It tags every turn of an eve session with the durable session id as the Sentry
conversation id, so the session's AI spans — which land in separate traces
(each eve turn is its own durable workflow) — group into one conversation in
the Agents "Conversations" view.
The id is set on the isolation scope rather than on the AI call: eve's session
id never reaches the AI SDK's telemetry diagnostics channel, so the only way to
attach it is via the scope, where the always-on `conversationIdIntegration`
picks it up and stamps `gen_ai.conversation.id` onto the gen_ai spans.
Subscribes to both `turn.started` and `step.started`. Each turn is a fresh
request with its own isolation scope, and a turn that parks and resumes
(approvals, compaction) resumes in another request where `turn.started` won't
re-fire — `step.started` runs before every model call, so together they cover
each request that produces spans. Re-setting the same id is idempotent.
The eve hook context is typed structurally (not imported from `eve`) so
`@sentry/node` keeps no dependency on the framework; the shape is checked at the
`defineHook(...)` call site instead.
The node-eve e2e app uses the hook and asserts `gen_ai.conversation.id` on each
gen_ai span equals the eve session id.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e-export from every runtime SDK The helper only needs `setConversationId` from core, so `@sentry/server-utils` is its natural home — a framework-agnostic shared layer already re-exported by the runtime SDKs — rather than living in `@sentry/node`. - `@sentry/node` now re-exports it from `@sentry/server-utils` (like the other shared server helpers), so every node-based SDK that does `export * from '@sentry/node'` (astro, nitro — eve's own base —, nestjs, hono, effect) surfaces it automatically. - Added to the explicit `@sentry/node` re-export blocks of `@sentry/bun`, `@sentry/aws-serverless` and `@sentry/google-cloud-serverless`. - Added to the `@sentry/server-utils` re-export blocks of `@sentry/deno` and `@sentry/cloudflare`, which build on server-utils rather than node. The unit test moves alongside the implementation into `@sentry/server-utils`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
It is a single-field options bag callers pass as an inline object literal, so exporting the type name added public API surface across every runtime SDK for no benefit. It stays declared (unexported) alongside the function, so the signature is unaffected and callers still pass options structurally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Astro's runtime entry curates its `@sentry/node` re-exports (it can't `export *`), so the helper has to be listed explicitly like the other SDKs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
796048f to
af51063
Compare
Stacked on #24247 (base branch `feat/eve-conversation-hook`). Exposes `getInstrumentedModuleNames()` — the package names Sentry instruments through the orchestrion module transform (its diagnostics-channel injection). ```ts import { getInstrumentedModuleNames } from '@sentry/node'; // eve agent.ts — keep every Sentry-instrumented package external so the // transform can hook it, instead of hardcoding the list: build: { externalDependencies: getInstrumentedModuleNames() } ``` _Why:_ - A framework with no Sentry bundler plugin (eve, via `build.externalDependencies`) otherwise has to hardcode which packages to keep external. An inlined dependency never reaches the transform's `onLoad`, so it's silently never instrumented — this hands the app the authoritative set. - It returns the plain `module.name` set, deliberately **without** the bundler-only additions in the internal `INSTRUMENTED_MODULE_NAMES` (e.g. `@remix-run/node`), which exist to force a helper package to be *bundled* — the opposite of keeping it external. _Export surface:_ lives in `@sentry/server-utils` (home of the orchestrion config). Re-exported from `@sentry/node`, so `export *` consumers — **nitro** (eve's base), astro, nestjs, hono, effect — surface it automatically; and added explicitly to `@sentry/bun`, `@sentry/aws-serverless`, `@sentry/google-cloud-serverless` (from node) and `@sentry/deno`, `@sentry/cloudflare` (from server-utils), i.e. everywhere a server bundle is configured — the same footprint as `eveConversationHook`. _node-eve e2e:_ the orchestrion variant's `build.externalDependencies` now uses `getInstrumentedModuleNames()` instead of a hardcoded `['dataloader']`. The app only uses `dataloader` of that set, so the others are no-ops; `ai` v7 stays correct when externalized (native `ai:telemetry` channel, and registration-only under the transform, so no double instrumentation). The existing `dataloader` assertion continues to prove the transform still runs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## DESCRIBE YOUR PR Adds a standalone **Eve** JavaScript guide and lifts the old Eve Agent Tracing page into it. Based on [getsentry/sentry-javascript#24228](getsentry/sentry-javascript#24228), [#24247](getsentry/sentry-javascript#24247), and [#24254](getsentry/sentry-javascript#24254). - New guide at `/platforms/javascript/guides/eve/` that **extends the Node guide** (`fallbackGuide: javascript.node`) with Error Monitoring / Tracing / Profiling onboarding toggles. Documents the `@sentry/node` SDK-based setup (replacing the old OTLP approach), `Sentry.eveConversationHook()` for linking session turns into Conversations, and `getInstrumentedModuleNames()` for orchestrion `externalDependencies`. - Removes the old OTLP-based `common/agent-tracing/eve.mdx`, redirects the old route to the new guide, and keeps the Eve card in the Node Agent Tracing grid (which now redirects to the guide). - Adds `javascript.eve` to every `notSupported` **and** `supported` list in JS common content wherever `javascript.node` appears. `PlatformSection` doesn't resolve a guide's `fallbackGuide`, so Eve must be enumerated alongside Node in both directions — otherwise browser-only content leaks into the Eve guide and inherited Node sections/pages render empty. - Registers the Eve sidebar/dropdown icon (`javascript-eve`). ## IS YOUR CHANGE URGENT? Help us prioritize incoming PRs by letting us know when the change needs to go live. Select exactly one option. For deadlines, replace `YYYY-MM-DD` with the due date. You can update this information later by editing the PR description. - [ ] Urgent deadline (GA date, etc.): YYYY-MM-DD - [ ] Other deadline: YYYY-MM-DD - [x] No deadline: Not urgent, can wait up to 1 week+ (blocked on the next `@sentry/node` beta release — see above) ## SLA - Teamwork makes the dream work, so please add a reviewer to your PRs. - Please give the docs team up to 1 week to review your PR unless you've supplied a deadline. Thanks in advance for your help! ## PRE-MERGE CHECKLIST _Make sure you've checked the following before merging your changes:_ - [ ] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RaYGdTstWVZKU9X4NjtYV1 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## DESCRIBE YOUR PR Adds a standalone **Eve** JavaScript guide and lifts the old Eve Agent Tracing page into it. Based on [getsentry/sentry-javascript#24228](getsentry/sentry-javascript#24228), [#24247](getsentry/sentry-javascript#24247), and [#24254](getsentry/sentry-javascript#24254). - New guide at `/platforms/javascript/guides/eve/` that **extends the Node guide** (`fallbackGuide: javascript.node`) with Error Monitoring / Tracing / Profiling onboarding toggles. Documents the `@sentry/node` SDK-based setup (replacing the old OTLP approach), `Sentry.eveConversationHook()` for linking session turns into Conversations, and `getInstrumentedModuleNames()` for orchestrion `externalDependencies`. - Removes the old OTLP-based `common/agent-tracing/eve.mdx`, redirects the old route to the new guide, and keeps the Eve card in the Node Agent Tracing grid (which now redirects to the guide). - Adds `javascript.eve` to every `notSupported` **and** `supported` list in JS common content wherever `javascript.node` appears. `PlatformSection` doesn't resolve a guide's `fallbackGuide`, so Eve must be enumerated alongside Node in both directions — otherwise browser-only content leaks into the Eve guide and inherited Node sections/pages render empty. - Registers the Eve sidebar/dropdown icon (`javascript-eve`). ## IS YOUR CHANGE URGENT? Help us prioritize incoming PRs by letting us know when the change needs to go live. Select exactly one option. For deadlines, replace `YYYY-MM-DD` with the due date. You can update this information later by editing the PR description. - [ ] Urgent deadline (GA date, etc.): YYYY-MM-DD - [ ] Other deadline: YYYY-MM-DD - [x] No deadline: Not urgent, can wait up to 1 week+ (blocked on the next `@sentry/node` beta release — see above) ## SLA - Teamwork makes the dream work, so please add a reviewer to your PRs. - Please give the docs team up to 1 week to review your PR unless you've supplied a deadline. Thanks in advance for your help! ## PRE-MERGE CHECKLIST _Make sure you've checked the following before merging your changes:_ - [ ] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RaYGdTstWVZKU9X4NjtYV1 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Stacked on #24228 (base branch
feat/e2e-eve-agent).Adds
eveConversationHook(), used as the default export of an eveagent/hooks/sentry.ts:It tags every turn of an eve session with the durable session id (
ctx.session.id) as the Sentry conversation id, so the session's AI spans — which land in separate traces, since each eve turn is its own durable workflow — group into one conversation in the Agents "Conversations" view.Root cause / why this shape:
recordInputs/recordOutputs/functionId;runtimeContext/metadata go to eve's own OTel integration, which Sentry bypasses). So the id can only be attached via the isolation scope, where the always-onconversationIdIntegrationpicks it up and stampsgen_ai.conversation.idonto the gen_ai spans.turn.startedandstep.started. Each turn is a fresh request with its own isolation scope, and a turn that parks and resumes (approvals, compaction) resumes in another request whereturn.startedwon't re-fire.step.startedruns before every model call, so together they cover every request that produces spans. Re-setting the same id is idempotent.@sentry/server-utils. The helper only needssetConversationIdfrom core, so it belongs in the framework-agnostic shared layer, not@sentry/node.@sentry/nodere-exports it (so everyexport * from '@sentry/node'SDK — astro, nitro (eve's own base), nestjs, hono, effect — surfaces it automatically); it is added explicitly to the node-based@sentry/bun,@sentry/aws-serverless,@sentry/google-cloud-serverless, and to the server-utils-based@sentry/denoand@sentry/cloudflare.eve, so the SDK keeps no dependency on the framework; the shape is checked at thedefineHook(...)call site. An optionalgetConversationIdlets users override the default (e.g. to use a root/parent session id for subagents).The
node-evee2e app now uses the hook and assertsgen_ai.conversation.idon each gen_ai span equals the eve session id returned by the session endpoint.🤖 Generated with Claude Code