Skip to content
Draft
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
16 changes: 16 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,22 @@ export { instrumentStateGraphCompile, instrumentLangGraph } from './tracing/lang
export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants';
export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types';
export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types';
export { setTokenUsageAttributes } from './tracing/ai/utils';
export {
GEN_AI_AGENT_NAME_ATTRIBUTE,
GEN_AI_OPERATION_NAME_ATTRIBUTE,
GEN_AI_REQUEST_MESSAGES_ATTRIBUTE,
GEN_AI_REQUEST_MODEL_ATTRIBUTE,
GEN_AI_RESPONSE_ID_ATTRIBUTE,
GEN_AI_RESPONSE_MODEL_ATTRIBUTE,
GEN_AI_RESPONSE_TEXT_ATTRIBUTE,
GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE,
GEN_AI_SYSTEM_ATTRIBUTE,
GEN_AI_TOOL_INPUT_ATTRIBUTE,
GEN_AI_TOOL_NAME_ATTRIBUTE,
GEN_AI_TOOL_OUTPUT_ATTRIBUTE,
GEN_AI_TOOL_TYPE_ATTRIBUTE,
} from './tracing/ai/gen-ai-attributes';
export type {
AnthropicAiClient,
AnthropicAiOptions,
Expand Down
29 changes: 29 additions & 0 deletions packages/core/src/tracing/ai/gen-ai-attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,35 @@ export const GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE = 'gen_ai.usage.input_to
*/
export const GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE = 'gen_ai.invoke_agent';

// =============================================================================
// AI AGENT ATTRIBUTES
// =============================================================================

/**
* The name of the AI agent
*/
export const GEN_AI_AGENT_NAME_ATTRIBUTE = 'gen_ai.agent.name';

/**
* The name of the tool being executed
*/
export const GEN_AI_TOOL_NAME_ATTRIBUTE = 'gen_ai.tool.name';

/**
* The type of the tool: 'function', 'extension', or 'datastore'
*/
export const GEN_AI_TOOL_TYPE_ATTRIBUTE = 'gen_ai.tool.type';

/**
* The input parameters for a tool call
*/
export const GEN_AI_TOOL_INPUT_ATTRIBUTE = 'gen_ai.tool.input';

/**
* The output/result of a tool call
*/
export const GEN_AI_TOOL_OUTPUT_ATTRIBUTE = 'gen_ai.tool.output';

// =============================================================================
// OPENAI-SPECIFIC ATTRIBUTES
// =============================================================================
Expand Down
3 changes: 3 additions & 0 deletions packages/nextjs/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export declare const contextLinesIntegration: typeof clientSdk.contextLinesInteg
// Different implementation in server and worker
export declare const vercelAIIntegration: typeof serverSdk.vercelAIIntegration;

// Claude Code integration (server-only)
export declare const claudeCodeAgentSdkIntegration: typeof serverSdk.claudeCodeAgentSdkIntegration;

export declare const getDefaultIntegrations: (options: Options) => Integration[];
export declare const defaultStackParser: StackParser;

Expand Down
17 changes: 16 additions & 1 deletion packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ import {
stripUrlQueryAndFragment,
} from '@sentry/core';
import type { NodeClient, NodeOptions } from '@sentry/node';
import { getDefaultIntegrations, httpIntegration, init as nodeInit } from '@sentry/node';
import {
getDefaultIntegrations,
httpIntegration,
init as nodeInit,
claudeCodeAgentSdkIntegration,
} from '@sentry/node';
import { getScopesFromContext } from '@sentry/opentelemetry';
import { DEBUG_BUILD } from '../common/debug-build';
import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor';
Expand All @@ -45,6 +50,16 @@ import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegrati

export * from '@sentry/node';

// Explicit re-export for Claude Code integration
// We re-export this explicitly to ensure rollup doesn't tree-shake it
export { claudeCodeAgentSdkIntegration };

// Force rollup to keep the import by "using" it
const _forceInclude = { claudeCodeAgentSdkIntegration };
if (false as boolean) {
console.log(_forceInclude);
}

export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';

// Override core span methods with Next.js-specific implementations that support Cache Components
Expand Down
2 changes: 2 additions & 0 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export { amqplibIntegration } from './integrations/tracing/amqplib';
export { vercelAIIntegration } from './integrations/tracing/vercelai';
export { openAIIntegration } from './integrations/tracing/openai';
export { anthropicAIIntegration } from './integrations/tracing/anthropic-ai';
export { claudeCodeAgentSdkIntegration } from './integrations/tracing/claude-code';
export type { ClaudeCodeOptions } from './integrations/tracing/claude-code';
export { googleGenAIIntegration } from './integrations/tracing/google-genai';
export { langChainIntegration } from './integrations/tracing/langchain';
export { langGraphIntegration } from './integrations/tracing/langgraph';
Expand Down
121 changes: 121 additions & 0 deletions packages/node/src/integrations/tracing/claude-code/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import type { IntegrationFn } from '@sentry/core';
import { defineIntegration } from '@sentry/core';
import { generateInstrumentOnce } from '@sentry/node-core';
import { SentryClaudeCodeAgentSdkInstrumentation } from './otel-instrumentation';
import type { ClaudeCodeOptions } from './types';

export type { ClaudeCodeOptions } from './types';

export const CLAUDE_CODE_AGENT_SDK_INTEGRATION_NAME = 'ClaudeCodeAgentSdk';

/**
* Instruments the Claude Code Agent SDK using OpenTelemetry.
* This is called automatically when the integration is added to Sentry.
*/
export const instrumentClaudeCodeAgentSdk = generateInstrumentOnce<ClaudeCodeOptions>(
CLAUDE_CODE_AGENT_SDK_INTEGRATION_NAME,
options => new SentryClaudeCodeAgentSdkInstrumentation(options),
);

const _claudeCodeAgentSdkIntegration = ((options: ClaudeCodeOptions = {}) => {
return {
name: CLAUDE_CODE_AGENT_SDK_INTEGRATION_NAME,
options,
setupOnce() {
instrumentClaudeCodeAgentSdk(options);
},
};
}) satisfies IntegrationFn;

/**
* Adds Sentry tracing instrumentation for the Claude Code Agent SDK.
*
* This integration automatically instruments the `query` function from
* `@anthropic-ai/claude-agent-sdk` to capture telemetry data following
* OpenTelemetry Semantic Conventions for Generative AI.
*
* **Important**: Sentry must be initialized BEFORE importing `@anthropic-ai/claude-agent-sdk`.
*
* @example
* ```typescript
* // Initialize Sentry FIRST
* import * as Sentry from '@sentry/node';
*
* Sentry.init({
* dsn: 'your-dsn',
* integrations: [
* Sentry.claudeCodeAgentSdkIntegration({
* recordInputs: true,
* recordOutputs: true
* })
* ],
* });
*
* // THEN import the SDK - it will be automatically instrumented!
* import { query } from '@anthropic-ai/claude-agent-sdk';
*
* // Use query as normal - spans are created automatically
* for await (const message of query({
* prompt: 'Hello',
* options: { model: 'claude-sonnet-4-20250514' }
* })) {
* console.log(message);
* }
* ```
*
* ## Captured Telemetry
*
* This integration captures:
* - Agent invocation spans (`gen_ai.invoke_agent`)
* - LLM chat spans (`gen_ai.chat`)
* - Tool execution spans (`gen_ai.execute_tool`)
* - Token usage, model info, and session tracking
*
* ## Options
*
* - `recordInputs`: Whether to record prompt messages (default: respects `sendDefaultPii` client option)
* - `recordOutputs`: Whether to record response text, tool calls, and outputs (default: respects `sendDefaultPii` client option)
* - `agentName`: Custom agent name for differentiation (default: 'claude-code')
*
* ### Default Behavior
*
* By default, the integration will:
* - Record inputs and outputs ONLY if `sendDefaultPii` is set to `true` in your Sentry client options
* - Otherwise, inputs and outputs are NOT recorded unless explicitly enabled
*
* @example
* ```typescript
* // Record inputs and outputs when sendDefaultPii is false
* Sentry.init({
* integrations: [
* Sentry.claudeCodeAgentSdkIntegration({
* recordInputs: true,
* recordOutputs: true
* })
* ],
* });
*
* // Never record inputs/outputs regardless of sendDefaultPii
* Sentry.init({
* sendDefaultPii: true,
* integrations: [
* Sentry.claudeCodeAgentSdkIntegration({
* recordInputs: false,
* recordOutputs: false
* })
* ],
* });
*
* // Custom agent name
* Sentry.init({
* integrations: [
* Sentry.claudeCodeAgentSdkIntegration({
* agentName: 'my-coding-assistant'
* })
* ],
* });
* ```
*
* @see https://docs.sentry.io/platforms/javascript/guides/node/ai-monitoring/
*/
export const claudeCodeAgentSdkIntegration = defineIntegration(_claudeCodeAgentSdkIntegration);
Loading
Loading