@posthog/ai 8.14.4 → 8.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/adk/index.cjs +22 -7
- package/dist/adk/index.cjs.map +1 -1
- package/dist/adk/index.mjs +22 -7
- package/dist/adk/index.mjs.map +1 -1
- package/dist/anthropic/index.cjs +12 -5
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +12 -5
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/claude-agent-sdk/index.cjs +12 -5
- package/dist/claude-agent-sdk/index.cjs.map +1 -1
- package/dist/claude-agent-sdk/index.mjs +12 -5
- package/dist/claude-agent-sdk/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +403 -19
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.d.ts +28 -0
- package/dist/gemini/index.mjs +403 -19
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +12 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +12 -5
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +12 -5
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +12 -5
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/langchain/middleware/index.cjs +12 -5
- package/dist/langchain/middleware/index.cjs.map +1 -1
- package/dist/langchain/middleware/index.mjs +12 -5
- package/dist/langchain/middleware/index.mjs.map +1 -1
- package/dist/openai/index.cjs +12 -5
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +12 -5
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +12 -5
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.mjs +12 -5
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +11 -4
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.mjs +11 -4
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +12 -5
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +12 -5
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["redactor","uuidv4","toJsonSafeValue","uuidv7","toJsonSafeValue","SYSTEM_PROMPT_DYNAMIC_BOUNDARY","uuidv4","originalQuery"],"sources":["../../package.json","../../src/captureAiEvent.ts","../../src/sanitization/base64_recognizer.ts","../../src/sanitization/media_type_context.ts","../../src/sanitization/binary_content_redactor.ts","../../src/sanitization.ts","../../src/utils.ts","../../src/serializeError.ts","../../src/gatewayWarning.ts","../../src/captureAiGeneration.ts","../../src/toolResult.ts","../../src/claude-agent-sdk/formatting.ts","../../src/claude-agent-sdk/processor.ts","../../src/claude-agent-sdk/index.ts"],"sourcesContent":["","import type { EventMessage } from 'posthog-node'\n\n/** @internal */\nexport type FullAiCaptureGate = {\n readonly enableFullAiCapture?: boolean\n}\n\n/** @internal */\nexport interface AiLaneCapableClient extends FullAiCaptureGate {\n capture(props: EventMessage): void\n captureImmediate(props: EventMessage): Promise<void>\n captureAi?(props: EventMessage): string | undefined\n captureAiImmediate?(props: EventMessage): Promise<string | undefined>\n}\n\n/** @internal */\nexport function isFullAiCaptureEnabled(client?: FullAiCaptureGate): boolean {\n return client?.enableFullAiCapture === true\n}\n\n/** @internal */\nexport function captureAiEvent(client: AiLaneCapableClient, event: EventMessage): void {\n if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {\n client.captureAi(event)\n return\n }\n client.capture(event)\n}\n\n/** @internal */\nexport async function captureAiEventImmediate(client: AiLaneCapableClient, event: EventMessage): Promise<void> {\n if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {\n await client.captureAiImmediate(event)\n return\n }\n await client.captureImmediate(event)\n}\n","const DATA_URL_PREFIX_RE = /^data:([^;,\\s]+)(?:;[^;,\\s]+)*;base64,/i\nconst BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/\n\nexport type Base64Recognition = { kind: 'data-url'; mediaType: string } | { kind: 'raw' } | { kind: 'none' }\n\nexport class Base64Recognizer {\n recognize(value: string, minLength: number): Base64Recognition {\n const dataUrl = DATA_URL_PREFIX_RE.exec(value)\n if (dataUrl) return { kind: 'data-url', mediaType: dataUrl[1] }\n\n if (value.length < minLength) return { kind: 'none' }\n\n const confidencePrefix = value.slice(0, minLength)\n if (BASE64_ALPHABET_RE.test(confidencePrefix)) {\n return { kind: 'raw' }\n } else {\n return { kind: 'none' }\n }\n }\n}\n","const MIME_HINT_KEYS = ['mediaType', 'media_type', 'mimeType', 'mime_type'] as const\n\nconst STRONG_CONTEXT_KEYS = new Set([\n 'data',\n 'file_data',\n 'fileData',\n 'image_url',\n 'imageUrl',\n 'video_url',\n 'videoUrl',\n 'audio',\n 'audio_data',\n 'audioData',\n 'inline_data',\n 'inlineData',\n 'source',\n 'result',\n])\n\nconst STRONG_CONTEXT_TYPES = new Set([\n 'image',\n 'image_url',\n 'input_image',\n 'audio',\n 'input_audio',\n 'video',\n 'video_url',\n 'file',\n 'input_file',\n 'document',\n 'media',\n 'file-data',\n])\n\nconst FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'file-data'])\n\nconst KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm'])\n\nexport class MediaTypeContext {\n static readonly EMPTY = new MediaTypeContext(undefined, undefined)\n\n constructor(\n private readonly parent: Record<string, unknown> | undefined,\n private readonly key: string | undefined,\n private readonly explicitMediaType?: string\n ) {}\n\n inferMediaType(): string | undefined {\n return (\n this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey()\n )\n }\n\n inferFromSiblingMime(): string | undefined {\n if (this.explicitMediaType) return this.explicitMediaType\n if (!this.parent) return undefined\n for (const hint of MIME_HINT_KEYS) {\n const v = this.parent[hint]\n if (typeof v === 'string') return v\n }\n return undefined\n }\n\n inferFromSiblingFormat(): string | undefined {\n if (!this.parent) return undefined\n const fmt = this.parent.format\n if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) {\n return `audio/${fmt.toLowerCase()}`\n }\n return undefined\n }\n\n inferFromParentType(): string | undefined {\n if (!this.parent) return undefined\n const t = this.parent.type\n if (typeof t !== 'string') return undefined\n if (t === 'image' || t === 'image_url' || t === 'input_image') return 'image'\n if (t === 'audio' || t === 'input_audio') return 'audio'\n if (t === 'video' || t === 'video_url') return 'video'\n if (FILE_FAMILY_TYPES.has(t)) return 'application/octet-stream'\n return undefined\n }\n\n inferFromKey(): string | undefined {\n if (!this.key) return undefined\n const key = this.key.toLowerCase()\n if (key.includes('audio')) return 'audio'\n if (key.includes('video')) return 'video'\n if (key.includes('image')) return 'image'\n if (key.includes('file') || key.includes('document')) return 'application/octet-stream'\n return undefined\n }\n\n hasExplicitBinaryMediaType(): boolean {\n if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false\n const mediaType = this.inferFromSiblingMime()\n return mediaType !== undefined && !mediaType.toLowerCase().startsWith('text/')\n }\n\n signalsBinary(): boolean {\n if (this.explicitMediaType) return true\n if (this.parent) {\n for (const hint of MIME_HINT_KEYS) {\n if (typeof this.parent[hint] === 'string') return true\n }\n const fmt = this.parent.format\n if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true\n const t = this.parent.type\n if (typeof t === 'string' && STRONG_CONTEXT_TYPES.has(t)) return true\n }\n if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true\n return false\n }\n}\n","import { Base64Recognizer } from './base64_recognizer'\nimport { MediaTypeContext } from './media_type_context'\n\nconst STRONG_CONTEXT_MIN_LENGTH = 64\nconst WEAK_CONTEXT_MIN_LENGTH = 1024\n\nexport class BinaryContentRedactor {\n private visited: WeakSet<object> = new WeakSet()\n\n constructor(private readonly recognizer: Base64Recognizer = new Base64Recognizer()) {}\n\n redact<T>(value: T, mediaType?: string): T\n redact(value: unknown, mediaType?: string): unknown {\n this.visited = new WeakSet()\n return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY)\n }\n\n /** Redact one leaf while a caller owns bounded, type-preserving traversal. @internal */\n redactLeaf(value: string | Uint8Array, context: MediaTypeContext): string {\n return typeof value === 'string' ? this.redactString(value, context) : this.placeholderFor(context.inferMediaType())\n }\n\n private walk(value: unknown, ctx: MediaTypeContext): unknown {\n if (value === null || value === undefined) return value\n if (typeof value === 'string') return this.redactLeaf(value, ctx)\n if (typeof value !== 'object') return value\n\n // Buffer extends Uint8Array, so this branch catches both.\n if (typeof Uint8Array !== 'undefined' && value instanceof Uint8Array) {\n return this.redactLeaf(value, ctx)\n }\n\n if (this.visited.has(value)) return null\n this.visited.add(value)\n\n if (Array.isArray(value)) {\n return value.map((item) => this.walk(item, ctx))\n }\n\n const obj = value as Record<string, unknown>\n const out: Record<string, unknown> = {}\n for (const k of Object.keys(obj)) {\n Object.defineProperty(out, k, {\n value: this.walk(obj[k], new MediaTypeContext(obj, k)),\n enumerable: true,\n writable: true,\n configurable: true,\n })\n }\n return out\n }\n\n private redactString(value: string, ctx: MediaTypeContext): string {\n const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType()\n const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\\r\\n]/g, '') : value\n const minLength = hasExplicitBinaryMediaType\n ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH)\n : ctx.signalsBinary()\n ? STRONG_CONTEXT_MIN_LENGTH\n : WEAK_CONTEXT_MIN_LENGTH\n const recognition = this.recognizer.recognize(recognitionValue, minLength)\n switch (recognition.kind) {\n case 'data-url':\n return this.placeholderFor(recognition.mediaType)\n case 'raw':\n return this.placeholderFor(ctx.inferMediaType())\n case 'none':\n return value\n }\n }\n\n private placeholderFor(mediaType?: string): string {\n if (!mediaType) return '[base64 redacted]'\n if (mediaType === 'application/octet-stream') return '[base64 file redacted]'\n return `[base64 ${mediaType} redacted]`\n }\n}\n","import { isFullAiCaptureEnabled, type FullAiCaptureGate } from './captureAiEvent'\nimport { BinaryContentRedactor } from './sanitization/binary_content_redactor'\n\nconst redactor = new BinaryContentRedactor()\n\nexport function redactBase64DataUrl(str: string, mediaType?: string): string\nexport function redactBase64DataUrl(str: unknown, mediaType?: string): unknown\nexport function redactBase64DataUrl(str: unknown, mediaType?: string): unknown {\n return redactor.redact(str, mediaType)\n}\n\nconst sanitize = (data: unknown, client?: FullAiCaptureGate): unknown =>\n isFullAiCaptureEnabled(client) ? data : redactor.redact(data)\n\nexport const sanitizeOpenAI = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeOpenAIResponse = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeAnthropic = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeGemini = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeLangChain = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeVercel = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\n","import { PostHog } from 'posthog-node'\nimport type OpenAIOrignal from 'openai'\nimport type AnthropicOriginal from '@anthropic-ai/sdk'\nimport type { ResponseCreateParamsWithTools } from 'openai/lib/ResponsesParser'\nimport type {\n FormattedMessage,\n FormattedContent,\n FormattedAudioContent,\n FormattedImageContent,\n FormattedDocumentContent,\n} from './types'\nimport { v4 as uuidv4 } from 'uuid'\nimport { isString } from './typeGuards'\nimport { redactBase64DataUrl } from './sanitization'\nimport { isFullAiCaptureEnabled, type FullAiCaptureGate } from './captureAiEvent'\n\ntype ChatCompletionCreateParamsBase = OpenAIOrignal.Chat.Completions.ChatCompletionCreateParams\ntype MessageCreateParams = AnthropicOriginal.Messages.MessageCreateParams\ntype ResponseCreateParams = OpenAIOrignal.Responses.ResponseCreateParams\ntype EmbeddingCreateParams = OpenAIOrignal.EmbeddingCreateParams\ntype TranscriptionCreateParams = OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParams\n\nconst TOKEN_PROPERTY_KEYS = new Set([\n '$ai_input_tokens',\n '$ai_output_tokens',\n '$ai_cache_read_input_tokens',\n '$ai_cache_creation_input_tokens',\n '$ai_total_tokens',\n '$ai_reasoning_tokens',\n])\n\n/**\n * Whether the caller supplied their own token counts, which override the ones the SDK\n * derived from the provider response.\n */\nexport function hasTokenOverrides(posthogProperties?: Record<string, unknown>): boolean {\n return !!posthogProperties && Object.keys(posthogProperties).some((key) => TOKEN_PROPERTY_KEYS.has(key))\n}\n\nexport function getTokensSource(posthogProperties?: Record<string, unknown>): string {\n return hasTokenOverrides(posthogProperties) ? 'passthrough' : 'sdk'\n}\n\n// limit large outputs by truncating to 200kb (approx 200k bytes)\nexport const MAX_OUTPUT_SIZE = 200000\nconst STRING_FORMAT = 'utf8'\n\n// Reused across calls to avoid per-invocation allocation; truncate() runs\n// hundreds of times for prompts with many parts.\nconst sharedTextEncoder = new TextEncoder()\nconst sharedTextDecoder = new TextDecoder(STRING_FORMAT, { fatal: false })\n\nexport const utf8ByteLength = (str: string): number => sharedTextEncoder.encode(str).byteLength\n\n/**\n * Safely converts content to a string, preserving structure for objects/arrays.\n * - If content is already a string, returns it as-is\n * - If content is an object or array, stringifies it with JSON.stringify to preserve structure\n * - Otherwise, converts to string with String()\n *\n * This prevents the \"[object Object]\" bug when objects are naively converted to strings.\n *\n * @param content - The content to convert to a string\n * @returns A string representation that preserves structure for complex types\n */\nexport function toContentString(content: unknown): string {\n if (typeof content === 'string') {\n return content\n }\n if (content !== undefined && content !== null && typeof content === 'object') {\n try {\n return JSON.stringify(content)\n } catch {\n // Fallback for circular refs, BigInt, or objects with throwing toJSON\n try {\n return String(content)\n } catch {\n // Custom coercion can throw, and null-prototype objects may have none.\n return ''\n }\n }\n }\n return String(content)\n}\n\nexport interface MonitoringEventPropertiesWithDefaults {\n distinctId?: string\n traceId: string\n properties?: Record<string, any>\n privacyMode: boolean\n groups?: Record<string, any>\n modelOverride?: string\n providerOverride?: string\n costOverride?: CostOverride\n captureImmediate?: boolean\n}\n\nexport type MonitoringEventProperties = Partial<MonitoringEventPropertiesWithDefaults>\n\nexport type MonitoringParams = {\n [K in keyof MonitoringEventProperties as `posthog${Capitalize<string & K>}`]: MonitoringEventProperties[K]\n}\n\nexport interface CostOverride {\n inputCost: number\n outputCost: number\n}\n\nexport const getModelParams = (\n params:\n | ((\n | ChatCompletionCreateParamsBase\n | MessageCreateParams\n | ResponseCreateParams\n | ResponseCreateParamsWithTools\n | EmbeddingCreateParams\n | TranscriptionCreateParams\n ) &\n MonitoringParams)\n | null,\n responseServiceTier?: string | null\n): Record<string, any> => {\n if (!params) {\n return {}\n }\n const modelParams: Record<string, any> = {}\n const paramKeys = [\n 'temperature',\n 'max_tokens',\n 'max_completion_tokens',\n 'top_p',\n 'frequency_penalty',\n 'presence_penalty',\n 'n',\n 'stop',\n 'stream',\n 'streaming',\n 'language',\n 'response_format',\n 'timestamp_granularities',\n ] as const\n\n for (const key of paramKeys) {\n if (key in params && (params as any)[key] !== undefined) {\n modelParams[key] = (params as any)[key]\n }\n }\n // Only the tier the provider served may appear here: a requested tier can be refused,\n // and cost processing prices from this value.\n if (responseServiceTier != null) {\n modelParams.service_tier = responseServiceTier\n }\n return modelParams\n}\n\nexport const formatResponseAnthropic = (response: any): FormattedMessage[] => {\n const output: FormattedMessage[] = []\n const content: FormattedContent = []\n\n for (const choice of response.content ?? []) {\n if (choice?.type === 'text' && choice?.text) {\n content.push({ type: 'text', text: choice.text })\n } else if (choice?.type === 'tool_use' && choice?.name && choice?.id) {\n content.push({\n type: 'function',\n id: choice.id,\n function: {\n name: choice.name,\n arguments: choice.input || {},\n },\n })\n }\n }\n\n if (content.length > 0) {\n output.push({\n role: 'assistant',\n content,\n })\n }\n\n return output\n}\n\nexport const formatResponseOpenAI = (response: any): FormattedMessage[] => {\n const output: FormattedMessage[] = []\n\n if (response.choices) {\n for (const choice of response.choices) {\n const content: FormattedContent = []\n let role = 'assistant'\n\n if (choice.message) {\n if (choice.message.role) {\n role = choice.message.role\n }\n\n if (choice.message.content) {\n content.push({ type: 'text', text: choice.message.content })\n }\n\n if (choice.message.tool_calls) {\n for (const toolCall of choice.message.tool_calls) {\n // Custom tools carry their free-form text in `custom.input`, with no `function` object.\n const call = toolCall.type === 'custom' ? toolCall.custom : toolCall.function\n if (!call) {\n continue\n }\n content.push({\n type: 'function',\n id: toolCall.id,\n function: {\n name: call.name,\n arguments: toolCall.type === 'custom' ? call.input : call.arguments,\n },\n })\n }\n }\n\n // Handle audio output (gpt-4o-audio-preview)\n if (choice.message.audio) {\n content.push({\n type: 'audio',\n ...choice.message.audio,\n })\n }\n }\n\n if (content.length > 0) {\n output.push({\n role,\n content,\n })\n }\n }\n }\n\n // Handle Responses API format\n if (response.output) {\n const content: FormattedContent = []\n let role = 'assistant'\n\n for (const item of response.output) {\n if (item.type === 'message') {\n role = item.role\n\n if (item.content && Array.isArray(item.content)) {\n for (const contentItem of item.content) {\n if (contentItem.type === 'output_text' && contentItem.text) {\n content.push({ type: 'text', text: contentItem.text })\n } else if (contentItem.text) {\n content.push({ type: 'text', text: contentItem.text })\n } else if (contentItem.type === 'input_image' && contentItem.image_url) {\n content.push({\n type: 'image',\n image: contentItem.image_url,\n })\n }\n }\n } else if (item.content) {\n content.push({ type: 'text', text: String(item.content) })\n }\n } else if (item.type === 'function_call') {\n content.push({\n type: 'function',\n id: item.call_id || item.id || '',\n function: {\n name: item.name,\n arguments: item.arguments || {},\n },\n })\n } else if (item.type === 'image_generation_call' && item.result) {\n content.push({ type: 'image', image: item.result })\n }\n }\n\n if (content.length > 0) {\n output.push({\n role,\n content,\n })\n }\n }\n\n return output\n}\n\nexport const buildInlineDataBlock = (\n mimeType: string,\n data: string\n): FormattedAudioContent | FormattedImageContent | FormattedDocumentContent => {\n if (mimeType.startsWith('audio/')) {\n return { type: 'audio', mime_type: mimeType, data }\n }\n if (mimeType.startsWith('image/')) {\n return { type: 'image', inline_data: { mime_type: mimeType, data } }\n }\n return { type: 'document', inline_data: { mime_type: mimeType, data } }\n}\n\nexport const formatInlineDataBlock = (\n inlineData: { mimeType?: string; mime_type?: string; data?: unknown },\n client?: FullAiCaptureGate\n): FormattedAudioContent | FormattedImageContent | FormattedDocumentContent => {\n const mimeType = inlineData.mimeType || inlineData.mime_type || 'application/octet-stream'\n let data = inlineData.data\n\n if (data instanceof Uint8Array) {\n if (typeof Buffer !== 'undefined') {\n data = Buffer.from(data).toString('base64')\n } else {\n let binary = ''\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i])\n }\n data = btoa(binary)\n }\n }\n\n data = isFullAiCaptureEnabled(client) ? data : redactBase64DataUrl(data, mimeType)\n return buildInlineDataBlock(mimeType, String(data ?? ''))\n}\n\nexport const formatResponseGemini = (response: any, client?: FullAiCaptureGate): FormattedMessage[] => {\n const output: FormattedMessage[] = []\n\n if (response.candidates && Array.isArray(response.candidates)) {\n for (const candidate of response.candidates) {\n if (candidate.content && candidate.content.parts) {\n const content: FormattedContent = []\n\n for (const part of candidate.content.parts) {\n if (part.text) {\n content.push({ type: 'text', text: part.text })\n } else if (part.functionCall) {\n content.push({\n type: 'function',\n ...(part.functionCall.id != null ? { id: part.functionCall.id } : {}),\n function: {\n name: part.functionCall.name,\n arguments: part.functionCall.args,\n },\n })\n } else if (part.inlineData) {\n content.push(formatInlineDataBlock(part.inlineData, client))\n }\n }\n\n if (content.length > 0) {\n output.push({\n role: 'assistant',\n content,\n })\n }\n } else if (candidate.text) {\n output.push({\n role: 'assistant',\n content: [{ type: 'text', text: candidate.text }],\n })\n }\n }\n } else if (response.text) {\n output.push({\n role: 'assistant',\n content: [{ type: 'text', text: response.text }],\n })\n }\n\n return output\n}\n\nexport const mergeSystemPrompt = (params: MessageCreateParams & MonitoringParams, provider: string): any => {\n if (provider == 'anthropic') {\n const messages = params.messages || []\n if (!(params as any).system) {\n return messages\n }\n const systemMessage = (params as any).system\n return [{ role: 'system', content: systemMessage }, ...messages]\n }\n return params.messages\n}\n\nexport const isPrivacyModeEnabled = (client: PostHog, privacyMode: boolean): boolean =>\n Boolean((client as any).privacy_mode || privacyMode)\n\nexport const withPrivacyMode = (client: PostHog, privacyMode: boolean, input: any): any => {\n return isPrivacyModeEnabled(client, privacyMode) ? null : input\n}\n\nfunction toSafeString(input: unknown): string {\n if (input === undefined || input === null) {\n return ''\n }\n if (typeof input === 'string') {\n return input\n }\n try {\n return JSON.stringify(input)\n } catch {\n console.warn('Failed to stringify input', input)\n return ''\n }\n}\n\nexport const truncate = (input: unknown, client?: FullAiCaptureGate, maxBytes = MAX_OUTPUT_SIZE): string => {\n const str = toSafeString(input)\n if (str === '') {\n return ''\n }\n\n if (isFullAiCaptureEnabled(client)) {\n return str\n }\n\n // Check if we need to truncate and ensure STRING_FORMAT is respected\n const buffer = sharedTextEncoder.encode(str)\n if (buffer.length <= maxBytes) {\n // Ensure STRING_FORMAT is respected\n return sharedTextDecoder.decode(buffer)\n }\n\n // Truncate the buffer and ensure a valid string is returned.\n // fatal: false means we get U+FFFD at the end if truncation broke the encoding.\n const truncatedBuffer = buffer.slice(0, maxBytes)\n let truncatedStr = sharedTextDecoder.decode(truncatedBuffer)\n if (truncatedStr.endsWith('\\uFFFD')) {\n truncatedStr = truncatedStr.slice(0, -1)\n }\n return `${truncatedStr}... [truncated]`\n}\n\n/**\n * Calculate web search count from raw API response.\n *\n * Uses a two-tier detection strategy:\n * Priority 1 (Exact Count): Count actual web search calls when available\n * Priority 2 (Binary Detection): Return 1 if web search indicators are present, 0 otherwise\n *\n * @param result - Raw API response from any provider (OpenAI, Perplexity, OpenRouter, Gemini, etc.)\n * @returns Number of web searches performed (exact count or binary 1/0)\n */\nexport function calculateWebSearchCount(result: unknown): number {\n if (!result || typeof result !== 'object') {\n return 0\n }\n\n // Priority 1: Exact Count\n // Check for OpenAI Responses API web_search_call items\n if ('output' in result && Array.isArray(result.output)) {\n let count = 0\n\n for (const item of result.output) {\n if (typeof item === 'object' && item !== null && 'type' in item && item.type === 'web_search_call') {\n count++\n }\n }\n\n if (count > 0) {\n return count\n }\n }\n\n // Priority 2: Binary Detection (1 or 0)\n\n // Check for citations at root level (Perplexity)\n if ('citations' in result && Array.isArray(result.citations) && result.citations.length > 0) {\n return 1\n }\n\n // Check for search_results at root level (Perplexity via OpenRouter)\n if ('search_results' in result && Array.isArray(result.search_results) && result.search_results.length > 0) {\n return 1\n }\n\n // Check for usage.search_context_size (Perplexity via OpenRouter)\n if ('usage' in result && typeof result.usage === 'object' && result.usage !== null) {\n if ('search_context_size' in result.usage && result.usage.search_context_size) {\n return 1\n }\n }\n\n // Check for annotations with url_citation in choices[].message or choices[].delta (OpenAI/Perplexity)\n if ('choices' in result && Array.isArray(result.choices)) {\n for (const choice of result.choices) {\n if (typeof choice === 'object' && choice !== null) {\n // Check both message (non-streaming) and delta (streaming) for annotations\n const content = ('message' in choice ? choice.message : null) || ('delta' in choice ? choice.delta : null)\n\n if (typeof content === 'object' && content !== null && 'annotations' in content) {\n const annotations = content.annotations\n\n if (Array.isArray(annotations)) {\n const hasUrlCitation = annotations.some((ann: unknown) => {\n return typeof ann === 'object' && ann !== null && 'type' in ann && ann.type === 'url_citation'\n })\n\n if (hasUrlCitation) {\n return 1\n }\n }\n }\n }\n }\n }\n\n // Check for annotations in output[].content[] (OpenAI Responses API)\n if ('output' in result && Array.isArray(result.output)) {\n for (const item of result.output) {\n if (typeof item === 'object' && item !== null && 'content' in item) {\n const content = item.content\n\n if (Array.isArray(content)) {\n for (const contentItem of content) {\n if (typeof contentItem === 'object' && contentItem !== null && 'annotations' in contentItem) {\n const annotations = contentItem.annotations\n\n if (Array.isArray(annotations)) {\n const hasUrlCitation = annotations.some((ann: unknown) => {\n return typeof ann === 'object' && ann !== null && 'type' in ann && ann.type === 'url_citation'\n })\n\n if (hasUrlCitation) {\n return 1\n }\n }\n }\n }\n }\n }\n }\n }\n\n // Check for grounding_metadata (Gemini)\n if ('candidates' in result && Array.isArray(result.candidates)) {\n for (const candidate of result.candidates) {\n if (\n typeof candidate === 'object' &&\n candidate !== null &&\n 'grounding_metadata' in candidate &&\n candidate.grounding_metadata\n ) {\n return 1\n }\n }\n }\n\n return 0\n}\n\n/**\n * Extract available tool calls from the request parameters.\n * These are the tools provided to the LLM, not the tool calls in the response.\n */\nexport const extractAvailableToolCalls = (provider: string, params: any): unknown[] | null => {\n if (provider === 'anthropic') {\n if (params.tools) {\n return params.tools\n }\n\n return null\n } else if (provider === 'gemini') {\n if (params.config && params.config.tools) {\n return params.config.tools\n }\n\n return null\n } else if (provider === 'openai') {\n if (params.tools) {\n return params.tools\n }\n\n return null\n } else if (provider === 'vercel') {\n if (params.tools) {\n return params.tools\n }\n\n return null\n }\n\n return null\n}\n\nexport enum AIEvent {\n Generation = '$ai_generation',\n Embedding = '$ai_embedding',\n}\n\nexport function sanitizeValues(obj: any): any {\n if (obj === undefined || obj === null) {\n return obj\n }\n const jsonSafe = JSON.parse(JSON.stringify(obj))\n if (typeof jsonSafe === 'string') {\n // Sanitize lone surrogates by round-tripping through UTF-8\n return new TextDecoder().decode(new TextEncoder().encode(jsonSafe))\n } else if (Array.isArray(jsonSafe)) {\n return jsonSafe.map(sanitizeValues)\n } else if (jsonSafe && typeof jsonSafe === 'object') {\n return Object.fromEntries(Object.entries(jsonSafe).map(([k, v]) => [k, sanitizeValues(v)]))\n }\n return jsonSafe\n}\n\nconst POSTHOG_PARAMS_MAP: Record<keyof MonitoringParams, string> = {\n posthogDistinctId: 'distinctId',\n posthogTraceId: 'traceId',\n posthogProperties: 'properties',\n posthogPrivacyMode: 'privacyMode',\n posthogGroups: 'groups',\n posthogModelOverride: 'modelOverride',\n posthogProviderOverride: 'providerOverride',\n posthogCostOverride: 'costOverride',\n posthogCaptureImmediate: 'captureImmediate',\n}\n\nexport function extractPosthogParams<T>(body: T & MonitoringParams): {\n providerParams: T\n posthogParams: MonitoringEventPropertiesWithDefaults\n} {\n const providerParams: Record<string, unknown> = {}\n const posthogParams: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(body)) {\n if (POSTHOG_PARAMS_MAP[key as keyof MonitoringParams]) {\n posthogParams[POSTHOG_PARAMS_MAP[key as keyof MonitoringParams]] = value\n } else if (key.startsWith('posthog')) {\n console.warn(`Unknown Posthog parameter ${key}`)\n } else {\n providerParams[key] = value\n }\n }\n\n return {\n providerParams: providerParams as T,\n posthogParams: addDefaults(posthogParams),\n }\n}\n\nfunction addDefaults(params: MonitoringEventProperties): MonitoringEventPropertiesWithDefaults {\n return {\n ...params,\n privacyMode: params.privacyMode ?? false,\n traceId: params.traceId ?? uuidv4(),\n }\n}\n\nexport function formatOpenAIResponsesInput(input: unknown, instructions?: string | null): FormattedMessage[] {\n const messages: FormattedMessage[] = []\n\n if (instructions) {\n messages.push({\n role: 'system',\n content: instructions,\n })\n }\n\n if (Array.isArray(input)) {\n for (const item of input) {\n if (typeof item === 'string') {\n messages.push({ role: 'user', content: item })\n } else if (item && typeof item === 'object') {\n const obj = item as Record<string, unknown>\n const role = isString(obj.role) ? obj.role : 'user'\n\n // Handle content properly - preserve structure for objects/arrays\n const content = obj.content ?? obj.text ?? item\n messages.push({ role, content: toContentString(content) })\n } else {\n messages.push({ role: 'user', content: toContentString(item) })\n }\n }\n } else if (typeof input === 'string') {\n messages.push({ role: 'user', content: input })\n } else if (input) {\n messages.push({ role: 'user', content: toContentString(input) })\n }\n\n return messages\n}\n","import { sanitizeValues } from './utils'\n\nconst DEFAULT_MAX_DEPTH = 3\nconst MAX_STACK_LINES = 20\n\nexport function serializeError(value: unknown, depth = DEFAULT_MAX_DEPTH): unknown {\n if (depth < 0 || value === null || typeof value !== 'object') {\n return value\n }\n if (value instanceof Error) {\n const out: Record<string, unknown> = {\n name: value.name,\n message: value.message,\n stack: truncateStack(value.stack),\n }\n for (const key of Object.keys(value)) {\n out[key] = serializeError((value as unknown as Record<string, unknown>)[key], depth - 1)\n }\n if (value.cause !== undefined) {\n out.cause = serializeError(value.cause, depth - 1)\n }\n return out\n }\n if (Array.isArray(value)) {\n return value.map((item) => serializeError(item, depth - 1))\n }\n return value\n}\n\nexport function stringifyError(error: unknown): string {\n try {\n return JSON.stringify(sanitizeValues(serializeError(error)))\n } catch {\n if (error instanceof Error) {\n return JSON.stringify({ name: error.name, message: error.message })\n }\n return JSON.stringify({ message: String(error) })\n }\n}\n\nfunction truncateStack(stack: string | undefined): string | undefined {\n if (!stack) {\n return stack\n }\n const lines = stack.split('\\n')\n if (lines.length <= MAX_STACK_LINES) {\n return stack\n }\n return [...lines.slice(0, MAX_STACK_LINES), '... (truncated)'].join('\\n')\n}\n","// Warn when a wrapper's base_url points at the PostHog AI Gateway: the gateway\n// emits its own $ai_generation, so each call would be captured (and, for billable\n// products, billed) twice. We only warn — the wrapper's event carries data the\n// gateway never sees (groups, custom properties, trace hierarchy).\n\n// Keep in sync with the gateway's deployed hosts (see services/llm-gateway in the\n// main repo). gateway.us.posthog.com is live today; the rest are listed ahead of\n// any traffic moving to them.\nexport const POSTHOG_AI_GATEWAY_HOSTS: readonly string[] = [\n 'gateway.posthog.com',\n 'gateway.us.posthog.com',\n 'gateway.eu.posthog.com',\n 'ai-gateway.us.posthog.com',\n 'ai-gateway.eu.posthog.com',\n]\n\n// Swap for the dedicated AI Gateway page once it ships.\nconst GATEWAY_DOCS_URL = 'https://posthog.com/docs/ai-observability'\n\nconst extractHost = (baseURL: string): string | undefined => {\n try {\n // Tolerate bare hosts that omit a scheme, e.g. \"gateway.us.posthog.com/v1\".\n const hasScheme = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(baseURL)\n return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase()\n } catch {\n return undefined\n }\n}\n\nexport const isPostHogAiGatewayUrl = (baseURL: string | undefined | null): boolean => {\n if (!baseURL) {\n return false\n }\n const host = extractHost(baseURL)\n return host !== undefined && POSTHOG_AI_GATEWAY_HOSTS.includes(host)\n}\n\n// Warns on every gateway call by design: the misconfiguration is impossible to\n// miss that way, and a doubled bill is worse than noisy logs.\nexport const warnIfPostHogAiGateway = (baseURL: string | undefined | null): void => {\n if (!isPostHogAiGatewayUrl(baseURL)) {\n return\n }\n console.warn(\n '[PostHog] The PostHog AI wrapper is pointed at the PostHog AI Gateway. ' +\n 'Both capture $ai_generation, so every call is double-counted and double-billed. ' +\n `Use one or the other — see ${GATEWAY_DOCS_URL}.`\n )\n}\n\n// OTel spans don't pass through captureAiGeneration, so detect the gateway from\n// the span's host/URL attributes instead. These follow the GenAI / HTTP semantic\n// conventions: `server.address` is a bare host, `url.full` a full URL, both of\n// which isPostHogAiGatewayUrl accepts.\nconst OTEL_GATEWAY_URL_ATTRIBUTES = ['server.address', 'url.full'] as const\n\nexport const warnIfPostHogAiGatewayOtelAttributes = (attributes: Record<string, unknown> | undefined): void => {\n if (!attributes) {\n return\n }\n for (const key of OTEL_GATEWAY_URL_ATTRIBUTES) {\n const value = attributes[key]\n if (typeof value === 'string' && isPostHogAiGatewayUrl(value)) {\n warnIfPostHogAiGateway(value)\n return\n }\n }\n}\n","import { EventMessage, PostHog } from 'posthog-node'\nimport { v4 as uuidv4 } from 'uuid'\nimport { uuidv7, ErrorTracking as CoreErrorTracking, toJsonSafeValue } from '@posthog/core'\nimport { version } from '../package.json'\nimport type { TokenUsage } from './types'\nimport { stringifyError } from './serializeError'\nimport { AIEvent, CostOverride, getTokensSource, hasTokenOverrides, isPrivacyModeEnabled } from './utils'\nimport { warnIfPostHogAiGateway } from './gatewayWarning'\nimport { captureAiEvent, captureAiEventImmediate } from './captureAiEvent'\n\n/**\n * Options for `captureAiGeneration`. Mirrors the `$ai_generation` event shape\n * directly so that any caller — first-party SDK wrappers and external code\n * alike — produces an identical event.\n */\nexport interface CaptureAiGenerationOptions {\n distinctId?: string\n /** Auto-generated when omitted. */\n traceId?: string\n /** Defaults to `$ai_generation`. */\n eventType?: AIEvent\n\n /** Required for the event to be useful, but accepted as optional so SDK wrappers can pass through whatever they detect. */\n model?: string\n provider: string\n input: unknown\n output: unknown\n\n /** Maps to `$ai_model_parameters` (temperature, max_tokens, top_p, …). */\n modelParameters?: Record<string, unknown>\n /** The service tier the provider reported serving the request on. Response-derived only:\n * this becomes $ai_service_tier, the explicit served-tier signal cost processing prices from. */\n servedServiceTier?: string | null\n\n /**\n * `null` explicitly signals no base URL and omits `$ai_base_url` from the event.\n * `undefined`/omitted keeps the existing `''` default for backward compatibility.\n */\n baseURL?: string | null\n httpStatus?: number\n /** Wall-clock latency in seconds. */\n latency?: number\n /** Time from request start to the first streamed token, in seconds. */\n timeToFirstToken?: number\n\n usage?: TokenUsage\n\n /** Extra event properties merged into the captured event. */\n properties?: Record<string, unknown>\n /** Mapping of group type to group id, matching `EventMessage.groups`. */\n groups?: Record<string, string | number>\n privacyMode?: boolean\n\n /**\n * For SDK wrappers: overrides the auto-detected model. External callers\n * should pass `model` directly instead.\n */\n modelOverride?: string\n /**\n * For SDK wrappers: overrides the auto-detected provider. External callers\n * should pass `provider` directly instead.\n */\n providerOverride?: string\n costOverride?: CostOverride\n\n tools?: unknown[] | null\n stopReason?: string\n\n /**\n * Provider-assigned ID for the generation (e.g. OpenAI's `chatcmpl-…` /\n * `resp_…`). Maps to `$ai_completion_id`. Response IDs generalize across\n * providers, so this lives in the shared schema rather than under\n * `providerMetadata`.\n */\n completionId?: string\n /**\n * Provider-specific response metadata that has no place in the shared,\n * provider-agnostic `$ai_*` schema (e.g. OpenAI's `system_fingerprint` and\n * `request_id`). Maps to `$ai_provider_metadata`; omitted when empty.\n */\n providerMetadata?: Record<string, unknown>\n\n /** When set, the event is captured as an error. */\n error?: unknown\n\n /** Awaits delivery instead of batching. Useful in serverless environments. */\n captureImmediate?: boolean\n /** Invoked when generation telemetry cannot be captured. Errors thrown by this callback are ignored. */\n onError?: (error: unknown) => void\n}\n\n/**\n * Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog.\n *\n * This is the canonical primitive that every `@posthog/ai` wrapper\n * (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so\n * external code can use it directly to instrument LLM calls made through\n * arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the\n * same events the SDK wrappers produce.\n *\n * When `error` is set, the event is captured as an error. If the error is an\n * object, it is mutated in place to set `__posthog_previously_captured_error`\n * so callers can re-throw the original error reference safely.\n */\nexport const captureAiGeneration = async (client: PostHog, options: CaptureAiGenerationOptions): Promise<void> => {\n try {\n if (!client.capture) {\n return\n }\n\n warnIfPostHogAiGateway(options.baseURL)\n\n const traceId = options.traceId ?? uuidv4()\n const eventType = options.eventType ?? AIEvent.Generation\n const privacyMode = options.privacyMode ?? false\n const usage = options.usage ?? {}\n\n // Check privacy before reading or traversing input/output. Besides avoiding\n // needless work, this ensures hostile getters/proxies cannot observe a value\n // that the caller explicitly requested us to redact.\n const shouldRedact = isPrivacyModeEnabled(client, privacyMode)\n const safeInput = shouldRedact ? null : toJsonSafeValue(options.input)\n const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output)\n\n let httpStatus = options.httpStatus\n let errorData: Record<string, unknown> = {}\n if (options.error) {\n if (httpStatus === undefined) {\n if (\n typeof options.error === 'object' &&\n 'status' in options.error &&\n typeof options.error.status === 'number'\n ) {\n httpStatus = options.error.status\n } else if (\n typeof options.error === 'object' &&\n 'statusCode' in options.error &&\n typeof options.error.statusCode === 'number'\n ) {\n httpStatus = options.error.statusCode\n } else {\n httpStatus = 500\n }\n }\n\n let exceptionId: string | undefined\n if (client.options?.enableExceptionAutocapture) {\n exceptionId = uuidv7()\n client.captureException(options.error, undefined, { $ai_trace_id: traceId }, exceptionId)\n if (typeof options.error === 'object') {\n ;(options.error as CoreErrorTracking.PreviouslyCapturedError).__posthog_previously_captured_error = true\n }\n }\n\n errorData = {\n $ai_is_error: true,\n $ai_error: stringifyError(options.error),\n $exception_event_id: exceptionId,\n }\n }\n httpStatus = httpStatus ?? 200\n\n // A configured price applies only to a count the provider reported, so a call with no\n // reported usage sends no cost instead of asserting $0. $ai_total_cost_usd sums the sides\n // that were priced, which makes it the cost of the known side alone when the other side\n // went unreported: a lower bound on the true total, not an assertion of it.\n const costOverrideData: Record<string, number> = {}\n if (options.costOverride) {\n if (usage.inputTokens !== undefined) {\n costOverrideData.$ai_input_cost_usd = (options.costOverride.inputCost ?? 0) * usage.inputTokens\n }\n if (usage.outputTokens !== undefined) {\n costOverrideData.$ai_output_cost_usd = (options.costOverride.outputCost ?? 0) * usage.outputTokens\n }\n if (Object.keys(costOverrideData).length > 0) {\n costOverrideData.$ai_total_cost_usd =\n (costOverrideData.$ai_input_cost_usd ?? 0) + (costOverrideData.$ai_output_cost_usd ?? 0)\n }\n }\n\n // The caller's own token counts override the SDK-derived ones further down, via the\n // `options.properties` spread.\n const tokensOverridden = hasTokenOverrides(options.properties)\n\n const additionalTokenValues = {\n ...(usage.reasoningTokens ? { $ai_reasoning_tokens: usage.reasoningTokens } : {}),\n ...(usage.cacheReadInputTokens ? { $ai_cache_read_input_tokens: usage.cacheReadInputTokens } : {}),\n ...(usage.cacheCreationInputTokens ? { $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens } : {}),\n // Checked against undefined rather than truthiness, because false is the meaningful\n // value here and a truthiness guard would drop it.\n //\n // Dropped entirely when the caller overrides the token counts: the flag describes how\n // the SDK-derived counts relate to each other, so against passthrough counts it can be\n // wrong in the expensive direction. Declaring inclusive over counts that are actually\n // exclusive makes ingestion subtract the cache pool that was never in the input. A\n // caller who knows their own accounting model can still pass\n // `$ai_cache_reporting_exclusive` themselves, and that value wins.\n ...(usage.cacheReportingExclusive !== undefined && !tokensOverridden\n ? { $ai_cache_reporting_exclusive: usage.cacheReportingExclusive }\n : {}),\n ...(usage.webSearchCount ? { $ai_web_search_count: usage.webSearchCount } : {}),\n ...(usage.rawUsage ? { $ai_usage: usage.rawUsage } : {}),\n }\n\n const properties: Record<string, unknown> = {\n $ai_lib: 'posthog-ai',\n $ai_lib_version: version,\n $ai_provider: options.providerOverride ?? options.provider,\n $ai_model: options.modelOverride ?? options.model,\n $ai_model_parameters: options.modelParameters ?? {},\n ...(options.servedServiceTier != null ? { $ai_service_tier: options.servedServiceTier } : {}),\n $ai_input: safeInput,\n $ai_output_choices: safeOutput,\n $ai_http_status: httpStatus,\n ...(usage.inputTokens !== undefined ? { $ai_input_tokens: usage.inputTokens } : {}),\n ...(usage.outputTokens !== undefined ? { $ai_output_tokens: usage.outputTokens } : {}),\n ...additionalTokenValues,\n ...(options.latency !== undefined ? { $ai_latency: options.latency } : {}),\n ...(options.timeToFirstToken !== undefined ? { $ai_time_to_first_token: options.timeToFirstToken } : {}),\n $ai_trace_id: traceId,\n ...(options.baseURL === null ? {} : { $ai_base_url: options.baseURL ?? '' }),\n ...options.properties,\n $ai_tokens_source: getTokensSource(options.properties),\n ...(options.distinctId ? {} : { $process_person_profile: false }),\n ...(options.stopReason ? { $ai_stop_reason: options.stopReason } : {}),\n ...(options.tools ? { $ai_tools: options.tools } : {}),\n ...(options.completionId ? { $ai_completion_id: options.completionId } : {}),\n ...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0\n ? { $ai_provider_metadata: options.providerMetadata }\n : {}),\n ...errorData,\n ...costOverrideData,\n }\n\n const event: EventMessage = {\n distinctId: options.distinctId ?? traceId,\n event: eventType,\n properties,\n groups: options.groups,\n }\n\n if (options.captureImmediate) {\n await captureAiEventImmediate(client, event)\n } else {\n captureAiEvent(client, event)\n }\n } catch (error) {\n // Telemetry failures must never affect the instrumented provider call.\n try {\n options.onError?.(error)\n } catch {\n // Error reporting must not affect the instrumented provider call either.\n }\n console.warn('[PostHog AI] Failed to capture generation telemetry:', error)\n }\n}\n","import { toJsonSafeValue } from '@posthog/core'\nimport { isFullAiCaptureEnabled, type FullAiCaptureGate } from './captureAiEvent'\nimport { BinaryContentRedactor } from './sanitization/binary_content_redactor'\nimport { MediaTypeContext } from './sanitization/media_type_context'\nimport { truncate } from './utils'\n\n// Match toJsonSafeValue's structural safeguards. A separate visitor is needed\n// here because JSON conversion erases the identity of supported binary values.\nconst MAX_DEPTH = 20\nconst MAX_ITEMS = 1_000\nconst MAX_NODES = 10_000\nconst TRUNCATED = '[Truncated]'\nconst UNSERIALIZABLE = '[Unserializable]'\nconst redactor = new BinaryContentRedactor()\nconst propertyIsEnumerable = Object.prototype.propertyIsEnumerable\n\n/** @internal Preserve tool-result structure while bounding each string value. */\nexport function formatToolResult(content: unknown, client?: FullAiCaptureGate, maxBytes = 5000): unknown {\n try {\n // Full capture still has JSON traversal guards, but no content redaction or string cap.\n if (isFullAiCaptureEnabled(client)) return toJsonSafeValue(content)\n\n const ancestors = new WeakSet<object>()\n let remainingNodes = MAX_NODES\n const convert = (value: unknown, depth: number, context: MediaTypeContext): unknown => {\n if (remainingNodes <= 0) return TRUNCATED\n remainingNodes--\n try {\n if (value === null || typeof value !== 'object') {\n const safe = toJsonSafeValue(value)\n return typeof safe === 'string' ? truncate(redactor.redactLeaf(safe, context), undefined, maxBytes) : safe\n }\n if (depth >= MAX_DEPTH) return TRUNCATED\n\n // Buffer is a Uint8Array. Check before toJSON/ordinary object conversion,\n // so bytes cannot become an unrecognizable numeric object or JSON array.\n if (typeof Uint8Array !== 'undefined' && value instanceof Uint8Array) {\n return redactor.redactLeaf(value, context)\n }\n if (ancestors.has(value)) return '[Circular]'\n ancestors.add(value)\n try {\n if (value instanceof Date) {\n const safe = toJsonSafeValue(value)\n return typeof safe === 'string' ? truncate(redactor.redactLeaf(safe, context), undefined, maxBytes) : safe\n }\n\n let hasToJSONResult = false\n let toJSONResult: unknown\n try {\n const toJSON = (value as { toJSON?: unknown }).toJSON\n if (typeof toJSON === 'function') {\n toJSONResult = toJSON.call(value)\n hasToJSONResult = true\n }\n } catch {\n // Match toJsonSafeValue: a broken toJSON falls back to own properties.\n }\n if (hasToJSONResult) return convert(toJSONResult, depth + 1, context)\n\n if (Array.isArray(value)) {\n const output: unknown[] = []\n const count = Math.min(value.length, MAX_ITEMS)\n let index = 0\n for (; index < count && remainingNodes > 0; index++) {\n output.push(convert(value[index], depth + 1, context))\n }\n if (value.length > index) output.push(TRUNCATED)\n return output\n }\n\n const input = value as Record<string, unknown>\n const output: Record<string, unknown> = {}\n let count = 0\n let truncated = false\n for (const key in input) {\n if (!propertyIsEnumerable.call(input, key)) break\n if (count >= MAX_ITEMS || remainingNodes <= 0) {\n truncated = true\n break\n }\n const converted = convert(input[key], depth + 1, new MediaTypeContext(input, key))\n Object.defineProperty(output, key, {\n value: converted,\n enumerable: true,\n writable: true,\n configurable: true,\n })\n count++\n }\n if (truncated) output[TRUNCATED] = 'Additional properties omitted'\n return output\n } finally {\n ancestors.delete(value)\n }\n } catch {\n // Includes hostile values and media-context getters. Never fall back to raw bytes.\n return UNSERIALIZABLE\n }\n }\n return convert(content, 0, MediaTypeContext.EMPTY)\n } catch {\n // A capture configuration getter can fail too; instrumentation remains best effort.\n return UNSERIALIZABLE\n }\n}\n","import type { Options } from '@anthropic-ai/claude-agent-sdk'\nimport { SYSTEM_PROMPT_DYNAMIC_BOUNDARY } from '@anthropic-ai/claude-agent-sdk'\nimport type { PostHog } from 'posthog-node'\nimport { MAX_OUTPUT_SIZE, toContentString, isPrivacyModeEnabled } from '../utils'\nimport { formatToolResult } from '../toolResult'\nimport type { FormattedContent, FormattedContentItem } from '../types'\n\n/** Thinking blocks, recorded with the `reasoning` shape the other adapters use. */\ninterface FormattedReasoningContent {\n type: 'reasoning'\n text: string\n}\n\nexport type ClaudeAgentContentItem = FormattedContentItem | FormattedReasoningContent\n\nexport function formatContent(content: unknown, client: PostHog, maxBytes = MAX_OUTPUT_SIZE): unknown {\n return formatToolResult(content, client, maxBytes)\n}\n\n/** Read the system prompt out of the SDK options, whatever shape it takes. */\nexport function extractSystemPrompt(options: Options | undefined): string | undefined {\n const systemPrompt = options?.systemPrompt\n if (typeof systemPrompt === 'string') {\n return systemPrompt\n }\n if (Array.isArray(systemPrompt)) {\n return systemPrompt.filter((part) => part !== SYSTEM_PROMPT_DYNAMIC_BOUNDARY).join('')\n }\n return systemPrompt?.append\n}\n\n/** Convert Anthropic assistant content blocks into PostHog content items. */\nexport function formatAssistantBlocks(blocks: unknown, client: PostHog): ClaudeAgentContentItem[] {\n if (!Array.isArray(blocks)) {\n return []\n }\n\n const content: ClaudeAgentContentItem[] = []\n for (const block of formatContent(blocks, client) as Array<Record<string, any>>) {\n if (block == null) {\n continue\n }\n if (typeof block.thinking === 'string') {\n content.push({ type: 'reasoning', text: block.thinking })\n } else if (block.type === 'tool_use') {\n content.push({\n type: 'function',\n id: block.id,\n function: {\n name: block.name,\n arguments: block.input ?? {},\n },\n })\n } else if (typeof block.text === 'string') {\n content.push({ type: 'text', text: block.text })\n } else {\n content.push({ type: 'text', text: toContentString(block) })\n }\n }\n return content\n}\n\n/** Convert an Anthropic user message body into PostHog content items. */\nexport function formatUserContent(content: unknown, client: PostHog, privacyMode = false): FormattedContent | unknown {\n if (isPrivacyModeEnabled(client, privacyMode)) return []\n if (typeof content === 'string') {\n return content\n }\n if (!Array.isArray(content)) {\n return []\n }\n\n const formatted: unknown[] = []\n for (const block of content as Array<Record<string, any>>) {\n if (block == null) {\n continue\n }\n if (block.type === 'tool_result') {\n formatted.push({\n type: 'tool_result',\n tool_use_id: block.tool_use_id,\n content: formatToolResult(block.content, client),\n ...(typeof block.is_error === 'boolean' ? { is_error: block.is_error } : {}),\n })\n } else if (typeof block.text === 'string') {\n formatted.push({ type: 'text', text: block.text })\n } else {\n formatted.push(formatContent(block, client))\n }\n }\n return formatted\n}\n","import { query as originalQuery } from '@anthropic-ai/claude-agent-sdk'\nimport type {\n Options,\n Query,\n SDKAssistantMessage,\n SDKMessage,\n SDKPartialAssistantMessage,\n SDKResultMessage,\n SDKUserMessage,\n} from '@anthropic-ai/claude-agent-sdk'\nimport type { EventMessage, PostHog } from 'posthog-node'\nimport { v4 as uuidv4 } from 'uuid'\nimport { version } from '../../package.json'\nimport { captureAiEvent, captureAiEventImmediate } from '../captureAiEvent'\nimport { captureAiGeneration } from '../captureAiGeneration'\nimport type { CaptureAiGenerationOptions } from '../captureAiGeneration'\nimport { stringifyError } from '../serializeError'\nimport type { FormattedMessage, TokenUsage } from '../types'\nimport { isPrivacyModeEnabled } from '../utils'\nimport { extractSystemPrompt, formatAssistantBlocks, formatContent, formatUserContent } from './formatting'\nimport type { ClaudeAgentContentItem } from './formatting'\n\nconst FRAMEWORK = 'claude-agent-sdk'\nconst PROVIDER = 'anthropic'\n\n/**\n * Resolver for the PostHog distinct ID. Either a static string, or a function\n * that derives it from the SDK result message. Return `null`/`undefined` to\n * capture anonymously (personless), keyed by the trace ID.\n */\nexport type DistinctIdResolver = string | ((result: SDKResultMessage) => string | null | undefined)\n\n/** Per-query tracing options. Each one overrides the processor default. */\nexport interface ClaudeAgentTraceOptions {\n /** Distinct ID for the captured events. Defaults to anonymous (personless) capture. */\n distinctId?: DistinctIdResolver\n /**\n * Trace ID shared by every event of the query. Generated per turn when\n * omitted, so each turn of a streaming-input session is its own trace.\n */\n traceId?: string\n /** Redacts captured input/output content when true. Defaults to false. */\n privacyMode?: boolean\n /** Group analytics mapping (group type -> group id) attached to every event. */\n groups?: Record<string, string | number>\n /** Extra properties merged into every captured AI event. */\n properties?: Record<string, unknown>\n}\n\nexport interface PostHogClaudeAgentProcessorOptions extends ClaudeAgentTraceOptions {\n /** The PostHog client used to capture events. */\n client: PostHog\n /** Awaits event delivery instead of batching. Useful in serverless environments. */\n captureImmediate?: boolean\n /** Invoked when the processor fails to capture an event. Never throws into the query. */\n onError?: (error: unknown) => void\n}\n\nexport interface ClaudeAgentQueryParams {\n /** The prompt, exactly as `query()` from the Claude Agent SDK takes it. */\n prompt: string | AsyncIterable<SDKUserMessage>\n /** Claude Agent SDK options. `includePartialMessages` is enabled internally. */\n options?: Options\n /** Tracing options for this query, overriding the processor defaults. */\n posthog?: ClaudeAgentTraceOptions\n}\n\ntype StreamEvent = SDKPartialAssistantMessage['event']\n\ninterface AnthropicUsage {\n input_tokens?: number | null\n output_tokens?: number | null\n cache_read_input_tokens?: number | null\n cache_creation_input_tokens?: number | null\n server_tool_use?: { web_search_requests?: number | null } | null\n}\n\n/** Metrics of a single model call, reconstructed from the streamed events. */\ninterface GenerationData {\n spanId: string\n error?: unknown\n input?: FormattedMessage[]\n startTime: number\n endTime?: number\n timeToFirstToken?: number\n model?: string\n usage: TokenUsage\n stopReason?: string\n streamedOutput?: ClaudeAgentContentItem[]\n}\n\n/**\n * Reconstructs per-generation metrics from the Anthropic streaming protocol the\n * Agent SDK relays: every `message_start` to `message_stop` cycle is one model\n * call.\n */\nclass GenerationTracker {\n private _current?: GenerationData\n private readonly _completed: GenerationData[] = []\n private _lastModel?: string\n private _sawStreamEvents = false\n private _pendingInput?: FormattedMessage[]\n\n /**\n * Queue the input of the next model call. The first call answers the prompt,\n * and every later one answers the tool results of the call before it.\n */\n setPendingInput(input: FormattedMessage[] | undefined): void {\n this._pendingInput = input\n }\n\n appendPendingInput(input: FormattedMessage[]): void {\n this._pendingInput = [...(this._pendingInput ?? []), ...input]\n }\n\n /** The queued input, still waiting for its model call. */\n get pendingInput(): FormattedMessage[] | undefined {\n return this._pendingInput\n }\n\n processStreamEvent(event: StreamEvent, client: PostHog, ttftMs?: number): void {\n this._sawStreamEvents = true\n\n if (event.type === 'message_start') {\n this.retryCurrent(new Error('Model stream restarted before completing'))\n const usage = event.message.usage as AnthropicUsage | undefined\n this._current = {\n spanId: uuidv4(),\n input: this._pendingInput,\n startTime: performance.now() - (ttftMs ?? 0),\n timeToFirstToken: ttftMs != null ? ttftMs / 1000 : undefined,\n model: event.message.model,\n usage: readUsage(usage),\n streamedOutput: [],\n }\n this._pendingInput = undefined\n } else if (event.type === 'content_block_start' && this._current?.streamedOutput) {\n this._current.streamedOutput[event.index] = formatAssistantBlocks([event.content_block], client)[0]\n } else if (event.type === 'content_block_delta') {\n const block = this._current?.streamedOutput?.[event.index]\n if (event.delta.type === 'text_delta' && block?.type === 'text') {\n block.text += event.delta.text\n } else if (event.delta.type === 'thinking_delta' && block?.type === 'reasoning') {\n block.text += event.delta.thinking\n } else if (event.delta.type === 'input_json_delta' && block?.type === 'function') {\n const previous = block.function.arguments\n block.function.arguments = (typeof previous === 'string' ? previous : '') + event.delta.partial_json\n }\n } else if (event.type === 'message_delta' && this._current) {\n const usage = event.usage as AnthropicUsage | undefined\n // `message_delta` reports the cumulative counts of the call so far.\n this._current.usage = {\n ...this._current.usage,\n ...readUsage(usage),\n rawUsage: { ...(this._current.usage.rawUsage as Record<string, unknown>), ...usage },\n }\n if (event.delta?.stop_reason != null) {\n this._current.stopReason = event.delta.stop_reason\n }\n } else if (event.type === 'message_stop') {\n this.finishCurrent()\n }\n }\n\n finishCurrent(): void {\n if (this._current) {\n this._current.endTime = performance.now()\n this._completed.push(this._current)\n this._lastModel = this._current.model\n this._current = undefined\n }\n }\n\n retryCurrent(error: unknown): void {\n if (this._current) {\n this._pendingInput = this._current.input\n this._current.error = error\n this.finishCurrent()\n }\n }\n\n setModel(model: string | undefined): void {\n if (model) {\n this._lastModel = model\n }\n }\n\n popCompleted(): GenerationData | undefined {\n return this._completed.shift()\n }\n\n /** Span ID of the generation in progress, before its `message_stop`. */\n get currentSpanId(): string | undefined {\n return this._current?.spanId\n }\n\n get lastModel(): string | undefined {\n return this._lastModel\n }\n\n get sawStreamEvents(): boolean {\n return this._sawStreamEvents\n }\n}\n\n/** Everything the instrumentation tracks for the turn in flight. */\ninterface QueryState {\n tracker: GenerationTracker\n systemPrompt?: string\n pendingCaptures: Array<(distinctId?: string) => Promise<void>>\n pendingTools: Map<string, { startTime: number; properties: Record<string, unknown> }>\n traceId: string\n turnStart: number\n // Subagent tool spans can arrive outside a main-agent generation.\n turnCaptured: boolean\n turnActive: boolean\n hasCapturedTrace: boolean\n pendingPrompts: SDKUserMessage[]\n userMessageUuid?: string\n failure?: unknown\n generationIndex: number\n lastGenerationSpanId?: string\n sessionId?: string\n totalCost: number\n turnCost?: number\n // One model call can deliver its blocks over several assistant messages, so\n // the content of a turn accumulates until the turn closes.\n pendingOutput: ClaudeAgentContentItem[]\n}\n\n/**\n * Wraps `query()` from `@anthropic-ai/claude-agent-sdk` to capture PostHog LLM\n * analytics.\n *\n * The Agent SDK runs Claude Code itself, so no request passes through an\n * Anthropic client the `@posthog/ai` Anthropic wrapper could patch. This\n * processor reads the SDK's own message stream instead and captures:\n *\n * - `$ai_generation` for each main-agent model call, reconstructed from the streamed\n * Anthropic events;\n * - `$ai_span` for every tool use, parented to the generation that asked for it;\n * - `$ai_trace` for every turn, carrying its latency and reported cost.\n *\n * @example\n * ```typescript\n * import { PostHogClaudeAgentProcessor } from '@posthog/ai/claude-agent-sdk'\n * import { PostHog } from 'posthog-node'\n *\n * const phClient = new PostHog('<POSTHOG_API_KEY>')\n * const processor = new PostHogClaudeAgentProcessor({ client: phClient, distinctId: 'user@example.com' })\n *\n * for await (const message of processor.query({ prompt: 'Explain this repo' })) {\n * console.log(message)\n * }\n * ```\n */\nexport class PostHogClaudeAgentProcessor {\n private readonly _client: PostHog\n private readonly _traceOptions: ClaudeAgentTraceOptions\n private readonly _captureImmediate: boolean\n private readonly _onError?: (error: unknown) => void\n\n constructor(options: PostHogClaudeAgentProcessorOptions) {\n this._client = options.client\n this._traceOptions = options\n this._captureImmediate = options.captureImmediate ?? false\n this._onError = options.onError\n }\n\n /**\n * Drop-in replacement for `query()` from the Claude Agent SDK. Hidden partial\n * messages pass their reply correlation to the first assistant message.\n * Other messages and the control methods (`interrupt`,\n * `setPermissionMode`, …) retain the SDK behavior.\n */\n query({ prompt, options, posthog }: ClaudeAgentQueryParams): Query {\n const trace: ClaudeAgentTraceOptions = {\n ...this._traceOptions,\n ...posthog,\n // A per-query setting can turn redaction on but never off, so a processor\n // configured for privacy stays private.\n privacyMode: this._traceOptions.privacyMode === true || posthog?.privacyMode === true,\n properties: { ...this._traceOptions.properties, ...posthog?.properties },\n }\n for (const key of ['$ai_trace_id', '$ai_span_id', '$ai_parent_id', '$ai_session_id']) {\n delete trace.properties?.[key]\n }\n\n // Partial messages carry the per-generation metrics, so they are always\n // requested. Callers who did not ask for them never see them.\n const forwardStreamEvents = options?.includePartialMessages === true\n const state = this._createState(prompt, options, trace)\n const inner = originalQuery({\n prompt: typeof prompt === 'string' ? prompt : this._observePrompt(prompt, state),\n options: { ...options, includePartialMessages: true },\n })\n\n const instrumented = this._instrument(inner, state, trace, forwardStreamEvents)\n // A `for await` loop reads `next` once per message, so the iteration\n // methods are bound once rather than on every read.\n const iteration: Record<string | symbol, unknown> = {\n next: instrumented.next.bind(instrumented),\n streamInput: (stream: AsyncIterable<SDKUserMessage>) => inner.streamInput(this._observePrompt(stream, state)),\n // The SDK starts its subprocess before the first next(), so cleanup must reach it directly.\n return: async (value?: void) => {\n try {\n return await inner.return(value)\n } finally {\n await instrumented.return()\n }\n },\n throw: async (error?: unknown) => {\n state.failure = error\n try {\n return await inner.throw(error)\n } finally {\n await instrumented.return()\n }\n },\n close: () => {\n try {\n inner.close()\n } finally {\n void instrumented.return().catch((error) => this._handleError(error))\n }\n },\n [Symbol.asyncDispose]: async () => {\n try {\n const dispose = Reflect.get(inner, Symbol.asyncDispose)\n if (typeof dispose === 'function') {\n await dispose.call(inner)\n } else {\n await inner.return()\n }\n } finally {\n await instrumented.return()\n }\n },\n [Symbol.asyncIterator]: () => wrapper,\n }\n\n // The SDK returns a generator that also carries control methods\n // (`interrupt`, `setPermissionMode`, …), so iteration goes to the wrapper\n // and every other read goes to the original object.\n const wrapper: Query = new Proxy(inner, {\n get(target, property) {\n if (Object.hasOwn(iteration, property)) {\n return iteration[property]\n }\n // boffin: SDK getters can read private fields on the original query.\n const value = Reflect.get(target, property, target)\n return typeof value === 'function' && property !== 'constructor' ? value.bind(target) : value\n },\n })\n return wrapper\n }\n\n private async *_instrument(\n inner: Query,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n forwardStreamEvents: boolean\n ): AsyncGenerator<SDKMessage, void> {\n try {\n for await (const message of inner) {\n try {\n await this._handleMessage(message, state, trace)\n } catch (error) {\n this._handleError(error)\n }\n if (message.type !== 'stream_event' || forwardStreamEvents) {\n if (\n !forwardStreamEvents &&\n message.type === 'assistant' &&\n !message.parent_tool_use_id &&\n state.userMessageUuid\n ) {\n const userMessageUuid = state.userMessageUuid\n state.userMessageUuid = undefined\n yield { ...message, user_message_uuid: message.user_message_uuid ?? userMessageUuid }\n } else {\n yield message\n }\n }\n }\n } catch (error) {\n state.failure = error\n throw error\n } finally {\n const failure = state.failure\n // An aborted query, or a caller that stops iterating, never delivers a\n // result message. The turn is still closed so its generations and spans\n // have a trace.\n // The SDK can throw after an error result already closed the last turn.\n if (state.turnActive || state.turnCaptured || (failure !== undefined && !state.hasCapturedTrace)) {\n try {\n state.tracker.finishCurrent()\n await this._captureCompletedGenerations(state, trace, failure)\n if (!state.tracker.sawStreamEvents && state.pendingOutput.length > 0) {\n state.generationIndex += 1\n await this._captureGeneration(\n {\n spanId: uuidv4(),\n input: state.tracker.pendingInput,\n startTime: state.turnStart,\n endTime: performance.now(),\n usage: {},\n error: failure,\n },\n state,\n trace\n )\n }\n } catch (error) {\n this._handleError(error)\n }\n try {\n await this._captureTrace(state, trace, undefined, failure)\n } catch (error) {\n this._handleError(error)\n }\n }\n }\n }\n\n private _createState(\n prompt: string | AsyncIterable<SDKUserMessage>,\n options: Options | undefined,\n trace: ClaudeAgentTraceOptions\n ): QueryState {\n const initialInput: FormattedMessage[] = []\n const systemPrompt = extractSystemPrompt(options)\n if (systemPrompt) {\n initialInput.push({ role: 'system', content: systemPrompt })\n }\n\n const tracker = new GenerationTracker()\n tracker.setPendingInput(initialInput.length > 0 ? initialInput : undefined)\n\n return {\n tracker,\n systemPrompt,\n pendingCaptures: [],\n pendingTools: new Map(),\n traceId: trace.traceId ?? uuidv4(),\n turnStart: performance.now(),\n turnCaptured: false,\n turnActive: false,\n hasCapturedTrace: false,\n pendingPrompts:\n typeof prompt === 'string'\n ? [{ type: 'user', message: { role: 'user', content: prompt }, parent_tool_use_id: null }]\n : [],\n generationIndex: 0,\n pendingOutput: [],\n totalCost: 0,\n }\n }\n\n private async *_observePrompt(\n prompt: AsyncIterable<SDKUserMessage>,\n state: QueryState\n ): AsyncGenerator<SDKUserMessage> {\n for await (const message of prompt) {\n state.pendingPrompts.push(message)\n yield message\n }\n }\n\n private _beginTurn(state: QueryState, trace: ClaudeAgentTraceOptions, userMessageUuid?: string): void {\n if (state.turnActive) {\n return\n }\n state.turnActive = true\n\n // The SDK can read ahead or prioritize a later prompt before answering it.\n let end = userMessageUuid ? state.pendingPrompts.findIndex((message) => message.uuid === userMessageUuid) : -1\n if (end < 0) {\n const candidates = state.pendingPrompts.filter((message) => message.shouldQuery !== false)\n if (candidates.length !== 1 || (userMessageUuid && candidates[0].uuid)) {\n return\n }\n end = state.pendingPrompts.indexOf(candidates[0])\n }\n if (end < 0) {\n return\n }\n let start = end\n while (start > 0 && state.pendingPrompts[start - 1].shouldQuery === false) {\n start -= 1\n }\n for (const message of state.pendingPrompts.splice(start, end - start + 1)) {\n state.tracker.appendPendingInput([\n { role: 'user', content: formatUserContent(message.message.content, this._client, trace.privacyMode) },\n ])\n }\n }\n\n private async _handleMessage(message: SDKMessage, state: QueryState, trace: ClaudeAgentTraceOptions): Promise<void> {\n if ('session_id' in message && message.session_id) {\n state.sessionId = message.session_id\n }\n\n if (message.type === 'conversation_reset') {\n state.totalCost = 0\n } else if (message.type === 'system' && message.subtype === 'api_retry') {\n state.tracker.retryCurrent(Object.assign(new Error(message.error), { status: message.error_status }))\n await this._captureCompletedGenerations(state, trace)\n } else if (message.type === 'stream_event') {\n if (message.parent_tool_use_id) {\n return\n }\n if (message.user_message_uuid) {\n state.userMessageUuid = message.user_message_uuid\n }\n if (message.event.type === 'message_start') {\n this._beginTurn(state, trace, message.user_message_uuid)\n }\n state.tracker.processStreamEvent(message.event, this._client, message.ttft_ms)\n await this._captureCompletedGenerations(state, trace)\n } else if (message.type === 'assistant') {\n if (!message.parent_tool_use_id) {\n this._beginTurn(state, trace, message.user_message_uuid)\n }\n this._handleAssistantMessage(message, state, trace)\n } else if (message.type === 'user') {\n if ('isReplay' in message && message.isReplay) {\n return\n }\n if (Array.isArray(message.message.content)) {\n for (const block of message.message.content) {\n if (block.type === 'tool_result') {\n await this._finishToolSpan(block.tool_use_id, state, trace, block)\n }\n }\n }\n if (message.parent_tool_use_id) {\n return\n }\n // A user message carries the tool results of the model call that asked\n // for them, so it becomes the input of the next model call.\n const content = formatUserContent(message.message?.content, this._client, trace.privacyMode)\n if (typeof content === 'string' || (Array.isArray(content) && content.length > 0)) {\n state.tracker.appendPendingInput([{ role: 'user', content }])\n }\n } else if (message.type === 'result') {\n try {\n this._beginTurn(state, trace, message.user_message_uuid)\n state.tracker.finishCurrent()\n await this._captureCompletedGenerations(state, trace, resultError(message), message)\n if (message.total_cost_usd != null) {\n // The SDK reports cumulative cost and can reset it when the session is cleared.\n state.turnCost = message.total_cost_usd - (message.total_cost_usd >= state.totalCost ? state.totalCost : 0)\n state.totalCost = message.total_cost_usd\n }\n // Without partial messages there is no per-call metric, so the result's\n // aggregate becomes one generation.\n if (!state.tracker.sawStreamEvents) {\n await this._captureGenerationFromResult(message, state, trace)\n }\n } finally {\n await this._captureTrace(state, trace, message)\n }\n }\n }\n\n private async _captureCompletedGenerations(\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n failure?: unknown,\n result?: SDKResultMessage\n ): Promise<void> {\n let generation = state.tracker.popCompleted()\n while (generation) {\n generation.error ??= failure\n state.generationIndex += 1\n state.lastGenerationSpanId = generation.spanId\n state.turnCaptured = true\n await this._captureGeneration(generation, state, trace, {}, result)\n state.pendingOutput = []\n generation = state.tracker.popCompleted()\n }\n }\n\n private _handleAssistantMessage(\n message: SDKAssistantMessage,\n state: QueryState,\n trace: ClaudeAgentTraceOptions\n ): void {\n if (!message.parent_tool_use_id) {\n state.tracker.setModel(message.message?.model)\n }\n\n // An assistant message arrives before its `message_stop`, so the generation\n // in progress is the parent of its tool calls.\n const parentSpanId = message.parent_tool_use_id ?? state.tracker.currentSpanId ?? state.lastGenerationSpanId\n const blocks = Array.isArray(message.message?.content)\n ? (message.message.content as Array<Record<string, any>>)\n : []\n for (const block of blocks) {\n if (block?.type === 'tool_use') {\n state.turnCaptured = true\n this._startToolSpan(block, state, trace, parentSpanId)\n }\n }\n\n if (!message.parent_tool_use_id) {\n state.pendingOutput.push(...formatAssistantBlocks(message.message?.content, this._client))\n }\n }\n\n private async _captureGeneration(\n generation: GenerationData,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n extraProperties: Record<string, unknown> = {},\n result?: SDKResultMessage\n ): Promise<void> {\n const options: CaptureAiGenerationOptions = {\n traceId: state.traceId,\n model: generation.model ?? state.tracker.lastModel,\n provider: PROVIDER,\n baseURL: null,\n httpStatus: result?.subtype === 'success' ? (result.api_error_status ?? undefined) : undefined,\n error: generation.error,\n input: generation.input ?? [],\n // Complete assistant messages replace the streamed prefix, leaving only unfinished blocks to append.\n output: formatContent(\n formatOutput([\n ...state.pendingOutput,\n ...(generation.streamedOutput?.slice(state.pendingOutput.length).filter(Boolean) ?? []),\n ]),\n this._client\n ),\n latency: generation.endTime != null ? (generation.endTime - generation.startTime) / 1000 : undefined,\n timeToFirstToken: generation.timeToFirstToken,\n usage: generation.usage,\n stopReason: generation.stopReason,\n groups: trace.groups,\n privacyMode: trace.privacyMode ?? false,\n captureImmediate: this._captureImmediate,\n onError: this._onError,\n properties: {\n $ai_framework: FRAMEWORK,\n $ai_span_id: generation.spanId,\n $ai_span_name: `generation_${state.generationIndex}`,\n ...(state.sessionId ? { $ai_session_id: state.sessionId } : {}),\n ...extraProperties,\n ...trace.properties,\n },\n }\n await this._withDistinctId(state, trace, (distinctId) =>\n captureAiGeneration(this._client, { ...options, distinctId })\n )\n }\n\n /**\n * Without partial messages there is no per-call metric, so the aggregate the\n * result message reports becomes one generation.\n */\n private async _captureGenerationFromResult(\n result: SDKResultMessage,\n state: QueryState,\n trace: ClaudeAgentTraceOptions\n ): Promise<void> {\n state.generationIndex += 1\n const endTime = performance.now()\n\n const generation: GenerationData = {\n spanId: uuidv4(),\n input: state.tracker.pendingInput ?? [],\n startTime: endTime - (result.duration_api_ms ?? 0),\n endTime,\n usage: readUsage(result.usage as AnthropicUsage | undefined),\n stopReason: result.stop_reason ?? undefined,\n error: resultError(result),\n }\n\n await this._captureGeneration(\n generation,\n state,\n trace,\n {\n ...(state.turnCost != null ? { $ai_total_cost_usd: state.turnCost } : {}),\n },\n result\n )\n }\n\n private _startToolSpan(\n block: Record<string, any>,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n parentSpanId: string | undefined\n ): void {\n const properties = {\n $ai_span_id: block.id ?? uuidv4(),\n ...(parentSpanId ? { $ai_parent_id: parentSpanId } : {}),\n $ai_span_name: block.name,\n $ai_span_type: 'tool',\n $ai_input_state: isPrivacyModeEnabled(this._client, trace.privacyMode ?? false)\n ? null\n : formatContent(block.input ?? {}, this._client),\n }\n state.pendingTools.set(properties.$ai_span_id, { startTime: performance.now(), properties })\n }\n\n private async _finishToolSpan(\n id: string,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n result?: { content?: unknown; is_error?: boolean }\n ): Promise<void> {\n const tool = state.pendingTools.get(id)\n if (!tool) {\n return\n }\n state.pendingTools.delete(id)\n const properties = {\n ...tool.properties,\n $ai_latency: (performance.now() - tool.startTime) / 1000,\n ...(result\n ? {\n $ai_output_state: isPrivacyModeEnabled(this._client, trace.privacyMode ?? false)\n ? null\n : formatContent(result.content, this._client),\n $ai_is_error: result.is_error === true,\n }\n : {}),\n }\n await this._withDistinctId(state, trace, (distinctId) =>\n this._captureLifecycleEvent('$ai_span', distinctId, state, trace, properties)\n )\n }\n\n private async _withDistinctId(\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n capture: (distinctId?: string) => Promise<void>\n ): Promise<void> {\n if (typeof trace.distinctId === 'function') {\n // boffin: Resolve the person once per turn so all its events use the same identity.\n state.pendingCaptures.push(capture)\n } else {\n await capture(resolveDistinctId(trace.distinctId))\n }\n }\n\n private async _captureTrace(\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n result?: SDKResultMessage,\n failure?: unknown\n ): Promise<void> {\n try {\n const latency =\n result?.duration_ms != null ? result.duration_ms / 1000 : (performance.now() - state.turnStart) / 1000\n const isError = failure !== undefined || result?.is_error === true\n const error = failure ?? (result && resultError(result))\n\n for (const id of state.pendingTools.keys()) {\n await this._finishToolSpan(id, state, trace)\n }\n let distinctId: string | undefined\n try {\n distinctId = resolveDistinctId(trace.distinctId, result)\n } catch (error) {\n this._handleError(error)\n }\n for (const capture of state.pendingCaptures.splice(0)) {\n try {\n await capture(distinctId)\n } catch (error) {\n this._handleError(error)\n }\n }\n await this._captureLifecycleEvent('$ai_trace', distinctId, state, trace, {\n $ai_trace_name: 'claude_agent_sdk_query',\n $ai_latency: latency,\n ...(state.turnCost != null ? { $ai_total_cost_usd: state.turnCost } : {}),\n ...(isError ? { $ai_is_error: true } : {}),\n ...(error !== undefined ? { $ai_error: stringifyError(error) } : {}),\n ...(result?.subtype === 'success' && result.api_error_status != null\n ? { $ai_http_status: result.api_error_status }\n : {}),\n })\n } finally {\n // A streaming-input session produces one result per turn. Each turn becomes\n // its own trace unless the caller pinned a trace ID for the whole session.\n state.hasCapturedTrace = true\n state.traceId = trace.traceId ?? uuidv4()\n state.turnStart = performance.now()\n state.turnCaptured = false\n state.turnActive = false\n state.userMessageUuid = undefined\n state.failure = undefined\n state.generationIndex = 0\n state.lastGenerationSpanId = undefined\n state.turnCost = undefined\n state.pendingCaptures = []\n state.pendingTools.clear()\n if (result?.queued_turn_count === 0) {\n state.pendingPrompts = state.pendingPrompts.filter((message) => message.shouldQuery === false)\n }\n state.tracker = new GenerationTracker()\n if (state.systemPrompt) {\n state.tracker.setPendingInput([{ role: 'system', content: state.systemPrompt }])\n }\n state.pendingOutput = []\n }\n }\n\n private async _captureLifecycleEvent(\n event: '$ai_trace' | '$ai_span',\n distinctId: string | undefined,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n properties: Record<string, unknown>\n ): Promise<void> {\n const message: EventMessage = {\n distinctId: distinctId ?? state.traceId,\n event,\n properties: {\n $ai_lib: 'posthog-ai',\n $ai_lib_version: version,\n $ai_framework: FRAMEWORK,\n $ai_provider: PROVIDER,\n $ai_trace_id: state.traceId,\n ...(state.sessionId ? { $ai_session_id: state.sessionId } : {}),\n ...properties,\n ...trace.properties,\n ...(distinctId ? {} : { $process_person_profile: false }),\n },\n groups: trace.groups,\n }\n\n try {\n if (this._captureImmediate) {\n await captureAiEventImmediate(this._client, message)\n } else {\n captureAiEvent(this._client, message)\n }\n } catch (error) {\n this._handleError(error)\n }\n }\n\n private _handleError(error: unknown): void {\n try {\n this._onError?.(error)\n } catch {\n // Instrumentation must never throw into the query.\n }\n }\n}\n\nfunction formatOutput(content: ClaudeAgentContentItem[]): FormattedMessage[] {\n return content.length > 0 ? [{ role: 'assistant', content }] : []\n}\n\nfunction resultError(result: SDKResultMessage): string | undefined {\n if (!result.is_error) {\n return undefined\n }\n return (result.subtype === 'success' ? result.result : result.errors.join('\\n')) || result.subtype\n}\n\nfunction readUsage(usage: AnthropicUsage | undefined): TokenUsage {\n return {\n ...(usage && Object.keys(usage).length > 0 ? { rawUsage: { ...usage } } : {}),\n ...(usage?.input_tokens != null ? { inputTokens: usage.input_tokens } : {}),\n ...(usage?.output_tokens != null ? { outputTokens: usage.output_tokens } : {}),\n ...(usage?.cache_read_input_tokens != null ? { cacheReadInputTokens: usage.cache_read_input_tokens } : {}),\n ...(usage?.cache_creation_input_tokens != null\n ? { cacheCreationInputTokens: usage.cache_creation_input_tokens }\n : {}),\n ...(usage?.server_tool_use?.web_search_requests != null\n ? { webSearchCount: usage.server_tool_use.web_search_requests }\n : {}),\n }\n}\n\nfunction resolveDistinctId(resolver: DistinctIdResolver | undefined, result?: SDKResultMessage): string | undefined {\n if (typeof resolver === 'function') {\n if (!result) {\n return undefined\n }\n const resolved = resolver(result)\n return resolved ? String(resolved) : undefined\n }\n return resolver ? String(resolver) : undefined\n}\n","import type { Query } from '@anthropic-ai/claude-agent-sdk'\nimport { PostHogClaudeAgentProcessor } from './processor'\nimport type { ClaudeAgentQueryParams, PostHogClaudeAgentProcessorOptions } from './processor'\n\nexport { PostHogClaudeAgentProcessor } from './processor'\nexport type {\n PostHogClaudeAgentProcessorOptions,\n ClaudeAgentQueryParams,\n ClaudeAgentTraceOptions,\n DistinctIdResolver,\n} from './processor'\n\nexport type InstrumentOptions = PostHogClaudeAgentProcessorOptions\n\n/**\n * Create a PostHog-instrumented `query()` for the Claude Agent SDK.\n *\n * @param options - Configuration options\n * @returns A processor whose `query()` method replaces `query()` from the SDK\n *\n * @example\n * ```typescript\n * import { instrument } from '@posthog/ai/claude-agent-sdk'\n * import { PostHog } from 'posthog-node'\n *\n * const phClient = new PostHog('<POSTHOG_API_KEY>')\n * const claude = instrument({ client: phClient, distinctId: 'user@example.com' })\n *\n * for await (const message of claude.query({ prompt: 'Explain this repo' })) {\n * console.log(message)\n * }\n * ```\n */\nexport function instrument(options: InstrumentOptions): PostHogClaudeAgentProcessor {\n return new PostHogClaudeAgentProcessor(options)\n}\n\n/**\n * Drop-in replacement for `query()` from the Claude Agent SDK, for a single\n * query. Use {@link instrument} to share one configuration across queries.\n *\n * @example\n * ```typescript\n * import { query } from '@posthog/ai/claude-agent-sdk'\n * import { PostHog } from 'posthog-node'\n *\n * const phClient = new PostHog('<POSTHOG_API_KEY>')\n *\n * for await (const message of query({\n * prompt: 'Explain this repo',\n * posthog: { client: phClient, distinctId: 'user@example.com' },\n * })) {\n * console.log(message)\n * }\n * ```\n */\nexport function query(\n params: Omit<ClaudeAgentQueryParams, 'posthog'> & { posthog: PostHogClaudeAgentProcessorOptions }\n): Query {\n const { prompt, options, posthog } = params\n return new PostHogClaudeAgentProcessor(posthog).query({ prompt, options })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["redactor","uuidv4","toJsonSafeValue","uuidv7","toJsonSafeValue","SYSTEM_PROMPT_DYNAMIC_BOUNDARY","uuidv4","originalQuery"],"sources":["../../package.json","../../src/captureAiEvent.ts","../../src/sanitization/base64_recognizer.ts","../../src/sanitization/media_type_context.ts","../../src/sanitization/binary_content_redactor.ts","../../src/sanitization.ts","../../src/utils.ts","../../src/serializeError.ts","../../src/gatewayWarning.ts","../../src/captureAiGeneration.ts","../../src/toolResult.ts","../../src/claude-agent-sdk/formatting.ts","../../src/claude-agent-sdk/processor.ts","../../src/claude-agent-sdk/index.ts"],"sourcesContent":["","import type { EventMessage } from 'posthog-node'\n\n/** @internal */\nexport type FullAiCaptureGate = {\n readonly enableFullAiCapture?: boolean\n}\n\n/** @internal */\nexport interface AiLaneCapableClient extends FullAiCaptureGate {\n capture(props: EventMessage): void\n captureImmediate(props: EventMessage): Promise<void>\n captureAi?(props: EventMessage): string | undefined\n captureAiImmediate?(props: EventMessage): Promise<string | undefined>\n}\n\n/** @internal */\nexport function isFullAiCaptureEnabled(client?: FullAiCaptureGate): boolean {\n return client?.enableFullAiCapture === true\n}\n\n/** @internal */\nexport function captureAiEvent(client: AiLaneCapableClient, event: EventMessage): void {\n if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {\n client.captureAi(event)\n return\n }\n client.capture(event)\n}\n\n/** @internal */\nexport async function captureAiEventImmediate(client: AiLaneCapableClient, event: EventMessage): Promise<void> {\n if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {\n await client.captureAiImmediate(event)\n return\n }\n await client.captureImmediate(event)\n}\n","const DATA_URL_PREFIX_RE = /^data:([^;,\\s]+)(?:;[^;,\\s]+)*;base64,/i\nconst BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/\n\nexport type Base64Recognition = { kind: 'data-url'; mediaType: string } | { kind: 'raw' } | { kind: 'none' }\n\nexport class Base64Recognizer {\n recognize(value: string, minLength: number): Base64Recognition {\n const dataUrl = DATA_URL_PREFIX_RE.exec(value)\n if (dataUrl) return { kind: 'data-url', mediaType: dataUrl[1] }\n\n if (value.length < minLength) return { kind: 'none' }\n\n const confidencePrefix = value.slice(0, minLength)\n if (BASE64_ALPHABET_RE.test(confidencePrefix)) {\n return { kind: 'raw' }\n } else {\n return { kind: 'none' }\n }\n }\n}\n","const MIME_HINT_KEYS = ['mediaType', 'media_type', 'mimeType', 'mime_type'] as const\n\nconst STRONG_CONTEXT_KEYS = new Set([\n 'data',\n 'file_data',\n 'fileData',\n 'image_url',\n 'imageUrl',\n 'video_url',\n 'videoUrl',\n 'audio',\n 'audio_data',\n 'audioData',\n 'inline_data',\n 'inlineData',\n 'source',\n 'result',\n])\n\nconst STRONG_CONTEXT_TYPES = new Set([\n 'image',\n 'image_url',\n 'input_image',\n 'audio',\n 'input_audio',\n 'video',\n 'video_url',\n 'file',\n 'input_file',\n 'document',\n 'media',\n 'file-data',\n])\n\nconst FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'file-data'])\nconst TYPED_BINARY_DATA_TYPES = new Set(['image', 'audio', 'video', 'document'])\n\nconst KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm'])\n\nexport class MediaTypeContext {\n static readonly EMPTY = new MediaTypeContext(undefined, undefined)\n\n constructor(\n private readonly parent: Record<string, unknown> | undefined,\n private readonly key: string | undefined,\n private readonly explicitMediaType?: string\n ) {}\n\n inferMediaType(): string | undefined {\n return (\n this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey()\n )\n }\n\n inferFromSiblingMime(): string | undefined {\n if (this.explicitMediaType) return this.explicitMediaType\n if (!this.parent) return undefined\n for (const hint of MIME_HINT_KEYS) {\n const v = this.parent[hint]\n if (typeof v === 'string') return v\n }\n return undefined\n }\n\n inferFromSiblingFormat(): string | undefined {\n if (!this.parent) return undefined\n const fmt = this.parent.format\n if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) {\n return `audio/${fmt.toLowerCase()}`\n }\n return undefined\n }\n\n inferFromParentType(): string | undefined {\n if (!this.parent) return undefined\n const t = this.parent.type\n if (typeof t !== 'string') return undefined\n if (t === 'image' || t === 'image_url' || t === 'input_image') return 'image'\n if (t === 'audio' || t === 'input_audio') return 'audio'\n if (t === 'video' || t === 'video_url') return 'video'\n if (FILE_FAMILY_TYPES.has(t)) return 'application/octet-stream'\n return undefined\n }\n\n inferFromKey(): string | undefined {\n if (!this.key) return undefined\n const key = this.key.toLowerCase()\n if (key.includes('audio')) return 'audio'\n if (key.includes('video')) return 'video'\n if (key.includes('image')) return 'image'\n if (key.includes('file') || key.includes('document')) return 'application/octet-stream'\n return undefined\n }\n\n hasDefiniteBinaryData(): boolean {\n if (\n this.parent &&\n this.key === 'data' &&\n typeof this.parent.type === 'string' &&\n TYPED_BINARY_DATA_TYPES.has(this.parent.type)\n )\n return true\n if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false\n const mediaType = this.inferFromSiblingMime()\n return mediaType !== undefined && !mediaType.toLowerCase().startsWith('text/')\n }\n\n signalsBinary(): boolean {\n if (this.explicitMediaType) return true\n if (this.parent) {\n for (const hint of MIME_HINT_KEYS) {\n if (typeof this.parent[hint] === 'string') return true\n }\n const fmt = this.parent.format\n if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true\n const t = this.parent.type\n if (typeof t === 'string' && STRONG_CONTEXT_TYPES.has(t)) return true\n }\n if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true\n return false\n }\n}\n","import { Base64Recognizer } from './base64_recognizer'\nimport { MediaTypeContext } from './media_type_context'\n\nconst STRONG_CONTEXT_MIN_LENGTH = 64\nconst WEAK_CONTEXT_MIN_LENGTH = 1024\n\nexport class BinaryContentRedactor {\n private visited: WeakSet<object> = new WeakSet()\n\n constructor(private readonly recognizer: Base64Recognizer = new Base64Recognizer()) {}\n\n redact<T>(value: T, mediaType?: string): T\n redact(value: unknown, mediaType?: string): unknown {\n this.visited = new WeakSet()\n return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY)\n }\n\n /** Redact one leaf while a caller owns bounded, type-preserving traversal. @internal */\n redactLeaf(value: string | Uint8Array, context: MediaTypeContext): string {\n return typeof value === 'string' ? this.redactString(value, context) : this.placeholderFor(context.inferMediaType())\n }\n\n private walk(value: unknown, ctx: MediaTypeContext): unknown {\n if (value === null || value === undefined) return value\n if (typeof value === 'string') return this.redactLeaf(value, ctx)\n if (typeof value !== 'object') return value\n\n // Buffer extends Uint8Array, so this branch catches both.\n if (typeof Uint8Array !== 'undefined' && value instanceof Uint8Array) {\n return this.redactLeaf(value, ctx)\n }\n\n if (this.visited.has(value)) return null\n this.visited.add(value)\n\n if (Array.isArray(value)) {\n return value.map((item) => this.walk(item, ctx))\n }\n\n const obj = value as Record<string, unknown>\n const out: Record<string, unknown> = {}\n for (const k of Object.keys(obj)) {\n Object.defineProperty(out, k, {\n value: this.walk(obj[k], new MediaTypeContext(obj, k)),\n enumerable: true,\n writable: true,\n configurable: true,\n })\n }\n return out\n }\n\n private redactString(value: string, ctx: MediaTypeContext): string {\n const hasDefiniteBinaryData = ctx.hasDefiniteBinaryData()\n const recognitionValue = hasDefiniteBinaryData ? value.replace(/[\\r\\n]/g, '') : value\n const minLength = hasDefiniteBinaryData\n ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH)\n : ctx.signalsBinary()\n ? STRONG_CONTEXT_MIN_LENGTH\n : WEAK_CONTEXT_MIN_LENGTH\n const recognition = this.recognizer.recognize(recognitionValue, minLength)\n switch (recognition.kind) {\n case 'data-url':\n return this.placeholderFor(recognition.mediaType)\n case 'raw':\n return this.placeholderFor(ctx.inferMediaType())\n case 'none':\n return value\n }\n }\n\n private placeholderFor(mediaType?: string): string {\n if (!mediaType) return '[base64 redacted]'\n if (mediaType === 'application/octet-stream') return '[base64 file redacted]'\n return `[base64 ${mediaType} redacted]`\n }\n}\n","import { isFullAiCaptureEnabled, type FullAiCaptureGate } from './captureAiEvent'\nimport { BinaryContentRedactor } from './sanitization/binary_content_redactor'\n\nconst redactor = new BinaryContentRedactor()\n\nexport function redactBase64DataUrl(str: string, mediaType?: string): string\nexport function redactBase64DataUrl(str: unknown, mediaType?: string): unknown\nexport function redactBase64DataUrl(str: unknown, mediaType?: string): unknown {\n return redactor.redact(str, mediaType)\n}\n\nconst sanitize = (data: unknown, client?: FullAiCaptureGate): unknown =>\n isFullAiCaptureEnabled(client) ? data : redactor.redact(data)\n\nexport const sanitizeOpenAI = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeOpenAIResponse = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeAnthropic = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeGemini = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeLangChain = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\nexport const sanitizeVercel = (data: unknown, client?: FullAiCaptureGate): unknown => sanitize(data, client)\n","import { PostHog } from 'posthog-node'\nimport type OpenAIOrignal from 'openai'\nimport type AnthropicOriginal from '@anthropic-ai/sdk'\nimport type { ResponseCreateParamsWithTools } from 'openai/lib/ResponsesParser'\nimport type {\n FormattedMessage,\n FormattedContent,\n FormattedAudioContent,\n FormattedImageContent,\n FormattedDocumentContent,\n} from './types'\nimport { v4 as uuidv4 } from 'uuid'\nimport { isString } from './typeGuards'\nimport { redactBase64DataUrl } from './sanitization'\nimport { isFullAiCaptureEnabled, type FullAiCaptureGate } from './captureAiEvent'\n\ntype ChatCompletionCreateParamsBase = OpenAIOrignal.Chat.Completions.ChatCompletionCreateParams\ntype MessageCreateParams = AnthropicOriginal.Messages.MessageCreateParams\ntype ResponseCreateParams = OpenAIOrignal.Responses.ResponseCreateParams\ntype EmbeddingCreateParams = OpenAIOrignal.EmbeddingCreateParams\ntype TranscriptionCreateParams = OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParams\n\nconst TOKEN_PROPERTY_KEYS = new Set([\n '$ai_input_tokens',\n '$ai_output_tokens',\n '$ai_cache_read_input_tokens',\n '$ai_cache_creation_input_tokens',\n '$ai_total_tokens',\n '$ai_reasoning_tokens',\n])\n\n/**\n * Whether the caller supplied their own token counts, which override the ones the SDK\n * derived from the provider response.\n */\nexport function hasTokenOverrides(posthogProperties?: Record<string, unknown>): boolean {\n return !!posthogProperties && Object.keys(posthogProperties).some((key) => TOKEN_PROPERTY_KEYS.has(key))\n}\n\nexport function getTokensSource(posthogProperties?: Record<string, unknown>): string {\n return hasTokenOverrides(posthogProperties) ? 'passthrough' : 'sdk'\n}\n\n// limit large outputs by truncating to 200kb (approx 200k bytes)\nexport const MAX_OUTPUT_SIZE = 200000\nconst STRING_FORMAT = 'utf8'\n\n// Reused across calls to avoid per-invocation allocation; truncate() runs\n// hundreds of times for prompts with many parts.\nconst sharedTextEncoder = new TextEncoder()\nconst sharedTextDecoder = new TextDecoder(STRING_FORMAT, { fatal: false })\n\nexport const utf8ByteLength = (str: string): number => sharedTextEncoder.encode(str).byteLength\n\n/**\n * Safely converts content to a string, preserving structure for objects/arrays.\n * - If content is already a string, returns it as-is\n * - If content is an object or array, stringifies it with JSON.stringify to preserve structure\n * - Otherwise, converts to string with String()\n *\n * This prevents the \"[object Object]\" bug when objects are naively converted to strings.\n *\n * @param content - The content to convert to a string\n * @returns A string representation that preserves structure for complex types\n */\nexport function toContentString(content: unknown): string {\n if (typeof content === 'string') {\n return content\n }\n if (content !== undefined && content !== null && typeof content === 'object') {\n try {\n return JSON.stringify(content)\n } catch {\n // Fallback for circular refs, BigInt, or objects with throwing toJSON\n try {\n return String(content)\n } catch {\n // Custom coercion can throw, and null-prototype objects may have none.\n return ''\n }\n }\n }\n return String(content)\n}\n\nexport interface MonitoringEventPropertiesWithDefaults {\n distinctId?: string\n traceId: string\n properties?: Record<string, any>\n privacyMode: boolean\n groups?: Record<string, any>\n modelOverride?: string\n providerOverride?: string\n costOverride?: CostOverride\n captureImmediate?: boolean\n}\n\nexport type MonitoringEventProperties = Partial<MonitoringEventPropertiesWithDefaults>\n\nexport type MonitoringParams = {\n [K in keyof MonitoringEventProperties as `posthog${Capitalize<string & K>}`]: MonitoringEventProperties[K]\n}\n\nexport interface CostOverride {\n inputCost: number\n outputCost: number\n}\n\nexport const getModelParams = (\n params:\n | ((\n | ChatCompletionCreateParamsBase\n | MessageCreateParams\n | ResponseCreateParams\n | ResponseCreateParamsWithTools\n | EmbeddingCreateParams\n | TranscriptionCreateParams\n ) &\n MonitoringParams)\n | null,\n responseServiceTier?: string | null\n): Record<string, any> => {\n if (!params) {\n return {}\n }\n const modelParams: Record<string, any> = {}\n const paramKeys = [\n 'temperature',\n 'max_tokens',\n 'max_completion_tokens',\n 'top_p',\n 'frequency_penalty',\n 'presence_penalty',\n 'n',\n 'stop',\n 'stream',\n 'streaming',\n 'language',\n 'response_format',\n 'timestamp_granularities',\n ] as const\n\n for (const key of paramKeys) {\n if (key in params && (params as any)[key] !== undefined) {\n modelParams[key] = (params as any)[key]\n }\n }\n // Only the tier the provider served may appear here: a requested tier can be refused,\n // and cost processing prices from this value.\n if (responseServiceTier != null) {\n modelParams.service_tier = responseServiceTier\n }\n return modelParams\n}\n\nexport const formatResponseAnthropic = (response: any): FormattedMessage[] => {\n const output: FormattedMessage[] = []\n const content: FormattedContent = []\n\n for (const choice of response.content ?? []) {\n if (choice?.type === 'text' && choice?.text) {\n content.push({ type: 'text', text: choice.text })\n } else if (choice?.type === 'tool_use' && choice?.name && choice?.id) {\n content.push({\n type: 'function',\n id: choice.id,\n function: {\n name: choice.name,\n arguments: choice.input || {},\n },\n })\n }\n }\n\n if (content.length > 0) {\n output.push({\n role: 'assistant',\n content,\n })\n }\n\n return output\n}\n\nexport const formatResponseOpenAI = (response: any): FormattedMessage[] => {\n const output: FormattedMessage[] = []\n\n if (response.choices) {\n for (const choice of response.choices) {\n const content: FormattedContent = []\n let role = 'assistant'\n\n if (choice.message) {\n if (choice.message.role) {\n role = choice.message.role\n }\n\n if (choice.message.content) {\n content.push({ type: 'text', text: choice.message.content })\n }\n\n if (choice.message.tool_calls) {\n for (const toolCall of choice.message.tool_calls) {\n // Custom tools carry their free-form text in `custom.input`, with no `function` object.\n const call = toolCall.type === 'custom' ? toolCall.custom : toolCall.function\n if (!call) {\n continue\n }\n content.push({\n type: 'function',\n id: toolCall.id,\n function: {\n name: call.name,\n arguments: toolCall.type === 'custom' ? call.input : call.arguments,\n },\n })\n }\n }\n\n // Handle audio output (gpt-4o-audio-preview)\n if (choice.message.audio) {\n content.push({\n type: 'audio',\n ...choice.message.audio,\n })\n }\n }\n\n if (content.length > 0) {\n output.push({\n role,\n content,\n })\n }\n }\n }\n\n // Handle Responses API format\n if (response.output) {\n const content: FormattedContent = []\n let role = 'assistant'\n\n for (const item of response.output) {\n if (item.type === 'message') {\n role = item.role\n\n if (item.content && Array.isArray(item.content)) {\n for (const contentItem of item.content) {\n if (contentItem.type === 'output_text' && contentItem.text) {\n content.push({ type: 'text', text: contentItem.text })\n } else if (contentItem.text) {\n content.push({ type: 'text', text: contentItem.text })\n } else if (contentItem.type === 'input_image' && contentItem.image_url) {\n content.push({\n type: 'image',\n image: contentItem.image_url,\n })\n }\n }\n } else if (item.content) {\n content.push({ type: 'text', text: String(item.content) })\n }\n } else if (item.type === 'function_call') {\n content.push({\n type: 'function',\n id: item.call_id || item.id || '',\n function: {\n name: item.name,\n arguments: item.arguments || {},\n },\n })\n } else if (item.type === 'image_generation_call' && item.result) {\n content.push({ type: 'image', image: item.result })\n }\n }\n\n if (content.length > 0) {\n output.push({\n role,\n content,\n })\n }\n }\n\n return output\n}\n\nexport const buildInlineDataBlock = (\n mimeType: string,\n data: string\n): FormattedAudioContent | FormattedImageContent | FormattedDocumentContent => {\n if (mimeType.startsWith('audio/')) {\n return { type: 'audio', mime_type: mimeType, data }\n }\n if (mimeType.startsWith('image/')) {\n return { type: 'image', inline_data: { mime_type: mimeType, data } }\n }\n return { type: 'document', inline_data: { mime_type: mimeType, data } }\n}\n\nexport const formatInlineDataBlock = (\n inlineData: { mimeType?: string; mime_type?: string; data?: unknown },\n client?: FullAiCaptureGate\n): FormattedAudioContent | FormattedImageContent | FormattedDocumentContent => {\n const mimeType = inlineData.mimeType || inlineData.mime_type || 'application/octet-stream'\n let data = inlineData.data\n\n if (data instanceof Uint8Array) {\n if (typeof Buffer !== 'undefined') {\n data = Buffer.from(data).toString('base64')\n } else {\n let binary = ''\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i])\n }\n data = btoa(binary)\n }\n }\n\n data = isFullAiCaptureEnabled(client) ? data : redactBase64DataUrl(data, mimeType)\n return buildInlineDataBlock(mimeType, String(data ?? ''))\n}\n\nexport const formatResponseGemini = (response: any, client?: FullAiCaptureGate): FormattedMessage[] => {\n const output: FormattedMessage[] = []\n\n if (response.candidates && Array.isArray(response.candidates)) {\n for (const candidate of response.candidates) {\n if (candidate.content && candidate.content.parts) {\n const content: FormattedContent = []\n\n for (const part of candidate.content.parts) {\n if (part.thought === true && part.text) {\n content.push({ type: 'reasoning', text: part.text })\n } else if (part.text) {\n content.push({ type: 'text', text: part.text })\n } else if (part.functionCall) {\n content.push({\n type: 'function',\n ...(part.functionCall.id != null ? { id: part.functionCall.id } : {}),\n function: {\n name: part.functionCall.name,\n arguments: part.functionCall.args,\n },\n })\n } else if (part.inlineData) {\n content.push(formatInlineDataBlock(part.inlineData, client))\n }\n }\n\n if (content.length > 0) {\n output.push({\n role: 'assistant',\n content,\n })\n }\n } else if (candidate.text) {\n output.push({\n role: 'assistant',\n content: [{ type: 'text', text: candidate.text }],\n })\n }\n }\n } else if (response.text) {\n output.push({\n role: 'assistant',\n content: [{ type: 'text', text: response.text }],\n })\n }\n\n return output\n}\n\nexport const mergeSystemPrompt = (params: MessageCreateParams & MonitoringParams, provider: string): any => {\n if (provider == 'anthropic') {\n const messages = params.messages || []\n if (!(params as any).system) {\n return messages\n }\n const systemMessage = (params as any).system\n return [{ role: 'system', content: systemMessage }, ...messages]\n }\n return params.messages\n}\n\nexport const isPrivacyModeEnabled = (client: PostHog, privacyMode: boolean): boolean =>\n Boolean((client as any).privacy_mode || privacyMode)\n\nexport const withPrivacyMode = (client: PostHog, privacyMode: boolean, input: any): any => {\n return isPrivacyModeEnabled(client, privacyMode) ? null : input\n}\n\nfunction toSafeString(input: unknown): string {\n if (input === undefined || input === null) {\n return ''\n }\n if (typeof input === 'string') {\n return input\n }\n try {\n return JSON.stringify(input)\n } catch {\n console.warn('Failed to stringify input', input)\n return ''\n }\n}\n\nexport const truncate = (input: unknown, client?: FullAiCaptureGate, maxBytes = MAX_OUTPUT_SIZE): string => {\n const str = toSafeString(input)\n if (str === '') {\n return ''\n }\n\n if (isFullAiCaptureEnabled(client)) {\n return str\n }\n\n // Check if we need to truncate and ensure STRING_FORMAT is respected\n const buffer = sharedTextEncoder.encode(str)\n if (buffer.length <= maxBytes) {\n // Ensure STRING_FORMAT is respected\n return sharedTextDecoder.decode(buffer)\n }\n\n // Truncate the buffer and ensure a valid string is returned.\n // fatal: false means we get U+FFFD at the end if truncation broke the encoding.\n const truncatedBuffer = buffer.slice(0, maxBytes)\n let truncatedStr = sharedTextDecoder.decode(truncatedBuffer)\n if (truncatedStr.endsWith('\\uFFFD')) {\n truncatedStr = truncatedStr.slice(0, -1)\n }\n return `${truncatedStr}... [truncated]`\n}\n\n/**\n * Calculate web search count from raw API response.\n *\n * Uses a two-tier detection strategy:\n * Priority 1 (Exact Count): Count actual web search calls when available\n * Priority 2 (Binary Detection): Return 1 if web search indicators are present, 0 otherwise\n *\n * @param result - Raw API response from any provider (OpenAI, Perplexity, OpenRouter, Gemini, etc.)\n * @returns Number of web searches performed (exact count or binary 1/0)\n */\nexport function calculateWebSearchCount(result: unknown): number {\n if (!result || typeof result !== 'object') {\n return 0\n }\n\n // Priority 1: Exact Count\n // Check for OpenAI Responses API web_search_call items\n if ('output' in result && Array.isArray(result.output)) {\n let count = 0\n\n for (const item of result.output) {\n if (typeof item === 'object' && item !== null && 'type' in item && item.type === 'web_search_call') {\n count++\n }\n }\n\n if (count > 0) {\n return count\n }\n }\n\n // Priority 2: Binary Detection (1 or 0)\n\n // Check for citations at root level (Perplexity)\n if ('citations' in result && Array.isArray(result.citations) && result.citations.length > 0) {\n return 1\n }\n\n // Check for search_results at root level (Perplexity via OpenRouter)\n if ('search_results' in result && Array.isArray(result.search_results) && result.search_results.length > 0) {\n return 1\n }\n\n // Check for usage.search_context_size (Perplexity via OpenRouter)\n if ('usage' in result && typeof result.usage === 'object' && result.usage !== null) {\n if ('search_context_size' in result.usage && result.usage.search_context_size) {\n return 1\n }\n }\n\n // Check for annotations with url_citation in choices[].message or choices[].delta (OpenAI/Perplexity)\n if ('choices' in result && Array.isArray(result.choices)) {\n for (const choice of result.choices) {\n if (typeof choice === 'object' && choice !== null) {\n // Check both message (non-streaming) and delta (streaming) for annotations\n const content = ('message' in choice ? choice.message : null) || ('delta' in choice ? choice.delta : null)\n\n if (typeof content === 'object' && content !== null && 'annotations' in content) {\n const annotations = content.annotations\n\n if (Array.isArray(annotations)) {\n const hasUrlCitation = annotations.some((ann: unknown) => {\n return typeof ann === 'object' && ann !== null && 'type' in ann && ann.type === 'url_citation'\n })\n\n if (hasUrlCitation) {\n return 1\n }\n }\n }\n }\n }\n }\n\n // Check for annotations in output[].content[] (OpenAI Responses API)\n if ('output' in result && Array.isArray(result.output)) {\n for (const item of result.output) {\n if (typeof item === 'object' && item !== null && 'content' in item) {\n const content = item.content\n\n if (Array.isArray(content)) {\n for (const contentItem of content) {\n if (typeof contentItem === 'object' && contentItem !== null && 'annotations' in contentItem) {\n const annotations = contentItem.annotations\n\n if (Array.isArray(annotations)) {\n const hasUrlCitation = annotations.some((ann: unknown) => {\n return typeof ann === 'object' && ann !== null && 'type' in ann && ann.type === 'url_citation'\n })\n\n if (hasUrlCitation) {\n return 1\n }\n }\n }\n }\n }\n }\n }\n }\n\n // Check for grounding_metadata (Gemini)\n if ('candidates' in result && Array.isArray(result.candidates)) {\n for (const candidate of result.candidates) {\n if (\n typeof candidate === 'object' &&\n candidate !== null &&\n 'grounding_metadata' in candidate &&\n candidate.grounding_metadata\n ) {\n return 1\n }\n }\n }\n\n return 0\n}\n\n/**\n * Extract available tool calls from the request parameters.\n * These are the tools provided to the LLM, not the tool calls in the response.\n */\nexport const extractAvailableToolCalls = (provider: string, params: any): unknown[] | null => {\n if (provider === 'anthropic') {\n if (params.tools) {\n return params.tools\n }\n\n return null\n } else if (provider === 'gemini') {\n if (params.config && params.config.tools) {\n return params.config.tools\n }\n\n return null\n } else if (provider === 'openai') {\n if (params.tools) {\n return params.tools\n }\n\n return null\n } else if (provider === 'vercel') {\n if (params.tools) {\n return params.tools\n }\n\n return null\n }\n\n return null\n}\n\nexport enum AIEvent {\n Generation = '$ai_generation',\n Embedding = '$ai_embedding',\n}\n\nexport function sanitizeValues(obj: any): any {\n if (obj === undefined || obj === null) {\n return obj\n }\n const jsonSafe = JSON.parse(JSON.stringify(obj))\n if (typeof jsonSafe === 'string') {\n // Sanitize lone surrogates by round-tripping through UTF-8\n return new TextDecoder().decode(new TextEncoder().encode(jsonSafe))\n } else if (Array.isArray(jsonSafe)) {\n return jsonSafe.map(sanitizeValues)\n } else if (jsonSafe && typeof jsonSafe === 'object') {\n return Object.fromEntries(Object.entries(jsonSafe).map(([k, v]) => [k, sanitizeValues(v)]))\n }\n return jsonSafe\n}\n\nconst POSTHOG_PARAMS_MAP: Record<keyof MonitoringParams, string> = {\n posthogDistinctId: 'distinctId',\n posthogTraceId: 'traceId',\n posthogProperties: 'properties',\n posthogPrivacyMode: 'privacyMode',\n posthogGroups: 'groups',\n posthogModelOverride: 'modelOverride',\n posthogProviderOverride: 'providerOverride',\n posthogCostOverride: 'costOverride',\n posthogCaptureImmediate: 'captureImmediate',\n}\n\nexport function extractPosthogParams<T>(body: T & MonitoringParams): {\n providerParams: T\n posthogParams: MonitoringEventPropertiesWithDefaults\n} {\n const providerParams: Record<string, unknown> = {}\n const posthogParams: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(body)) {\n if (POSTHOG_PARAMS_MAP[key as keyof MonitoringParams]) {\n posthogParams[POSTHOG_PARAMS_MAP[key as keyof MonitoringParams]] = value\n } else if (key.startsWith('posthog')) {\n console.warn(`Unknown Posthog parameter ${key}`)\n } else {\n providerParams[key] = value\n }\n }\n\n return {\n providerParams: providerParams as T,\n posthogParams: addDefaults(posthogParams),\n }\n}\n\nfunction addDefaults(params: MonitoringEventProperties): MonitoringEventPropertiesWithDefaults {\n return {\n ...params,\n privacyMode: params.privacyMode ?? false,\n traceId: params.traceId ?? uuidv4(),\n }\n}\n\nexport function formatOpenAIResponsesInput(input: unknown, instructions?: string | null): FormattedMessage[] {\n const messages: FormattedMessage[] = []\n\n if (instructions) {\n messages.push({\n role: 'system',\n content: instructions,\n })\n }\n\n if (Array.isArray(input)) {\n for (const item of input) {\n if (typeof item === 'string') {\n messages.push({ role: 'user', content: item })\n } else if (item && typeof item === 'object') {\n const obj = item as Record<string, unknown>\n const role = isString(obj.role) ? obj.role : 'user'\n\n // Handle content properly - preserve structure for objects/arrays\n const content = obj.content ?? obj.text ?? item\n messages.push({ role, content: toContentString(content) })\n } else {\n messages.push({ role: 'user', content: toContentString(item) })\n }\n }\n } else if (typeof input === 'string') {\n messages.push({ role: 'user', content: input })\n } else if (input) {\n messages.push({ role: 'user', content: toContentString(input) })\n }\n\n return messages\n}\n","import { sanitizeValues } from './utils'\n\nconst DEFAULT_MAX_DEPTH = 3\nconst MAX_STACK_LINES = 20\n\nexport function serializeError(value: unknown, depth = DEFAULT_MAX_DEPTH): unknown {\n if (depth < 0 || value === null || typeof value !== 'object') {\n return value\n }\n if (value instanceof Error) {\n const out: Record<string, unknown> = {\n name: value.name,\n message: value.message,\n stack: truncateStack(value.stack),\n }\n for (const key of Object.keys(value)) {\n out[key] = serializeError((value as unknown as Record<string, unknown>)[key], depth - 1)\n }\n if (value.cause !== undefined) {\n out.cause = serializeError(value.cause, depth - 1)\n }\n return out\n }\n if (Array.isArray(value)) {\n return value.map((item) => serializeError(item, depth - 1))\n }\n return value\n}\n\nexport function stringifyError(error: unknown): string {\n try {\n return JSON.stringify(sanitizeValues(serializeError(error)))\n } catch {\n if (error instanceof Error) {\n return JSON.stringify({ name: error.name, message: error.message })\n }\n return JSON.stringify({ message: String(error) })\n }\n}\n\nfunction truncateStack(stack: string | undefined): string | undefined {\n if (!stack) {\n return stack\n }\n const lines = stack.split('\\n')\n if (lines.length <= MAX_STACK_LINES) {\n return stack\n }\n return [...lines.slice(0, MAX_STACK_LINES), '... (truncated)'].join('\\n')\n}\n","// Warn when a wrapper's base_url points at the PostHog AI Gateway: the gateway\n// emits its own $ai_generation, so each call would be captured (and, for billable\n// products, billed) twice. We only warn — the wrapper's event carries data the\n// gateway never sees (groups, custom properties, trace hierarchy).\n\n// Keep in sync with the gateway's deployed hosts (see services/llm-gateway in the\n// main repo). gateway.us.posthog.com is live today; the rest are listed ahead of\n// any traffic moving to them.\nexport const POSTHOG_AI_GATEWAY_HOSTS: readonly string[] = [\n 'gateway.posthog.com',\n 'gateway.us.posthog.com',\n 'gateway.eu.posthog.com',\n 'ai-gateway.us.posthog.com',\n 'ai-gateway.eu.posthog.com',\n]\n\n// Swap for the dedicated AI Gateway page once it ships.\nconst GATEWAY_DOCS_URL = 'https://posthog.com/docs/ai-observability'\n\nconst extractHost = (baseURL: string): string | undefined => {\n try {\n // Tolerate bare hosts that omit a scheme, e.g. \"gateway.us.posthog.com/v1\".\n const hasScheme = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(baseURL)\n return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase()\n } catch {\n return undefined\n }\n}\n\nexport const isPostHogAiGatewayUrl = (baseURL: string | undefined | null): boolean => {\n if (!baseURL) {\n return false\n }\n const host = extractHost(baseURL)\n return host !== undefined && POSTHOG_AI_GATEWAY_HOSTS.includes(host)\n}\n\n// Warns on every gateway call by design: the misconfiguration is impossible to\n// miss that way, and a doubled bill is worse than noisy logs.\nexport const warnIfPostHogAiGateway = (baseURL: string | undefined | null): void => {\n if (!isPostHogAiGatewayUrl(baseURL)) {\n return\n }\n console.warn(\n '[PostHog] The PostHog AI wrapper is pointed at the PostHog AI Gateway. ' +\n 'Both capture $ai_generation, so every call is double-counted and double-billed. ' +\n `Use one or the other — see ${GATEWAY_DOCS_URL}.`\n )\n}\n\n// OTel spans don't pass through captureAiGeneration, so detect the gateway from\n// the span's host/URL attributes instead. These follow the GenAI / HTTP semantic\n// conventions: `server.address` is a bare host, `url.full` a full URL, both of\n// which isPostHogAiGatewayUrl accepts.\nconst OTEL_GATEWAY_URL_ATTRIBUTES = ['server.address', 'url.full'] as const\n\nexport const warnIfPostHogAiGatewayOtelAttributes = (attributes: Record<string, unknown> | undefined): void => {\n if (!attributes) {\n return\n }\n for (const key of OTEL_GATEWAY_URL_ATTRIBUTES) {\n const value = attributes[key]\n if (typeof value === 'string' && isPostHogAiGatewayUrl(value)) {\n warnIfPostHogAiGateway(value)\n return\n }\n }\n}\n","import { EventMessage, PostHog } from 'posthog-node'\nimport { v4 as uuidv4 } from 'uuid'\nimport { uuidv7, ErrorTracking as CoreErrorTracking, toJsonSafeValue } from '@posthog/core'\nimport { version } from '../package.json'\nimport type { TokenUsage } from './types'\nimport { stringifyError } from './serializeError'\nimport { AIEvent, CostOverride, getTokensSource, hasTokenOverrides, isPrivacyModeEnabled } from './utils'\nimport { warnIfPostHogAiGateway } from './gatewayWarning'\nimport { captureAiEvent, captureAiEventImmediate } from './captureAiEvent'\n\n/**\n * Options for `captureAiGeneration`. Mirrors the `$ai_generation` event shape\n * directly so that any caller — first-party SDK wrappers and external code\n * alike — produces an identical event.\n */\nexport interface CaptureAiGenerationOptions {\n distinctId?: string\n /** Auto-generated when omitted. */\n traceId?: string\n /** Defaults to `$ai_generation`. */\n eventType?: AIEvent\n\n /** Required for the event to be useful, but accepted as optional so SDK wrappers can pass through whatever they detect. */\n model?: string\n provider: string\n input: unknown\n output: unknown\n\n /** Maps to `$ai_model_parameters` (temperature, max_tokens, top_p, …). */\n modelParameters?: Record<string, unknown>\n /** The service tier the provider reported serving the request on. Response-derived only:\n * this becomes $ai_service_tier, the explicit served-tier signal cost processing prices from. */\n servedServiceTier?: string | null\n\n /**\n * `null` explicitly signals no base URL and omits `$ai_base_url` from the event.\n * `undefined`/omitted keeps the existing `''` default for backward compatibility.\n */\n baseURL?: string | null\n httpStatus?: number\n /** Wall-clock latency in seconds. */\n latency?: number\n /** Time from request start to the first streamed token, in seconds. */\n timeToFirstToken?: number\n\n usage?: TokenUsage\n\n /** Extra event properties merged into the captured event. */\n properties?: Record<string, unknown>\n /** Mapping of group type to group id, matching `EventMessage.groups`. */\n groups?: Record<string, string | number>\n privacyMode?: boolean\n\n /**\n * For SDK wrappers: overrides the auto-detected model. External callers\n * should pass `model` directly instead.\n */\n modelOverride?: string\n /**\n * For SDK wrappers: overrides the auto-detected provider. External callers\n * should pass `provider` directly instead.\n */\n providerOverride?: string\n costOverride?: CostOverride\n\n tools?: unknown[] | null\n stopReason?: string\n\n /**\n * Provider-assigned ID for the generation (e.g. OpenAI's `chatcmpl-…` /\n * `resp_…`). Maps to `$ai_completion_id`. Response IDs generalize across\n * providers, so this lives in the shared schema rather than under\n * `providerMetadata`.\n */\n completionId?: string\n /**\n * Provider-specific response metadata that has no place in the shared,\n * provider-agnostic `$ai_*` schema (e.g. OpenAI's `system_fingerprint` and\n * `request_id`). Maps to `$ai_provider_metadata`; omitted when empty.\n */\n providerMetadata?: Record<string, unknown>\n\n /** When set, the event is captured as an error. */\n error?: unknown\n\n /** Awaits delivery instead of batching. Useful in serverless environments. */\n captureImmediate?: boolean\n /** Invoked when generation telemetry cannot be captured. Errors thrown by this callback are ignored. */\n onError?: (error: unknown) => void\n}\n\n/**\n * Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog.\n *\n * This is the canonical primitive that every `@posthog/ai` wrapper\n * (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so\n * external code can use it directly to instrument LLM calls made through\n * arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the\n * same events the SDK wrappers produce.\n *\n * When `error` is set, the event is captured as an error. If the error is an\n * object, it is mutated in place to set `__posthog_previously_captured_error`\n * so callers can re-throw the original error reference safely.\n */\nexport const captureAiGeneration = async (client: PostHog, options: CaptureAiGenerationOptions): Promise<void> => {\n try {\n if (!client.capture) {\n return\n }\n\n warnIfPostHogAiGateway(options.baseURL)\n\n const traceId = options.traceId ?? uuidv4()\n const eventType = options.eventType ?? AIEvent.Generation\n const privacyMode = options.privacyMode ?? false\n const usage = options.usage ?? {}\n\n // Check privacy before reading or traversing input/output. Besides avoiding\n // needless work, this ensures hostile getters/proxies cannot observe a value\n // that the caller explicitly requested us to redact.\n const shouldRedact = isPrivacyModeEnabled(client, privacyMode)\n const safeInput = shouldRedact ? null : toJsonSafeValue(options.input)\n const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output)\n\n let httpStatus = options.httpStatus\n let errorData: Record<string, unknown> = {}\n if (options.error) {\n if (httpStatus === undefined) {\n if (\n typeof options.error === 'object' &&\n 'status' in options.error &&\n typeof options.error.status === 'number'\n ) {\n httpStatus = options.error.status\n } else if (\n typeof options.error === 'object' &&\n 'statusCode' in options.error &&\n typeof options.error.statusCode === 'number'\n ) {\n httpStatus = options.error.statusCode\n } else {\n httpStatus = 500\n }\n }\n\n let exceptionId: string | undefined\n if (client.options?.enableExceptionAutocapture) {\n exceptionId = uuidv7()\n client.captureException(options.error, undefined, { $ai_trace_id: traceId }, exceptionId)\n if (typeof options.error === 'object') {\n ;(options.error as CoreErrorTracking.PreviouslyCapturedError).__posthog_previously_captured_error = true\n }\n }\n\n errorData = {\n $ai_is_error: true,\n $ai_error: stringifyError(options.error),\n $exception_event_id: exceptionId,\n }\n }\n httpStatus = httpStatus ?? 200\n\n // A configured price applies only to a count the provider reported, so a call with no\n // reported usage sends no cost instead of asserting $0. $ai_total_cost_usd sums the sides\n // that were priced, which makes it the cost of the known side alone when the other side\n // went unreported: a lower bound on the true total, not an assertion of it.\n const costOverrideData: Record<string, number> = {}\n if (options.costOverride) {\n if (usage.inputTokens !== undefined) {\n costOverrideData.$ai_input_cost_usd = (options.costOverride.inputCost ?? 0) * usage.inputTokens\n }\n if (usage.outputTokens !== undefined) {\n costOverrideData.$ai_output_cost_usd = (options.costOverride.outputCost ?? 0) * usage.outputTokens\n }\n if (Object.keys(costOverrideData).length > 0) {\n costOverrideData.$ai_total_cost_usd =\n (costOverrideData.$ai_input_cost_usd ?? 0) + (costOverrideData.$ai_output_cost_usd ?? 0)\n }\n }\n\n // The caller's own token counts override the SDK-derived ones further down, via the\n // `options.properties` spread.\n const tokensOverridden = hasTokenOverrides(options.properties)\n\n const additionalTokenValues = {\n ...(usage.reasoningTokens ? { $ai_reasoning_tokens: usage.reasoningTokens } : {}),\n ...(usage.cacheReadInputTokens ? { $ai_cache_read_input_tokens: usage.cacheReadInputTokens } : {}),\n ...(usage.cacheCreationInputTokens ? { $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens } : {}),\n // Checked against undefined rather than truthiness, because false is the meaningful\n // value here and a truthiness guard would drop it.\n //\n // Dropped entirely when the caller overrides the token counts: the flag describes how\n // the SDK-derived counts relate to each other, so against passthrough counts it can be\n // wrong in the expensive direction. Declaring inclusive over counts that are actually\n // exclusive makes ingestion subtract the cache pool that was never in the input. A\n // caller who knows their own accounting model can still pass\n // `$ai_cache_reporting_exclusive` themselves, and that value wins.\n ...(usage.cacheReportingExclusive !== undefined && !tokensOverridden\n ? { $ai_cache_reporting_exclusive: usage.cacheReportingExclusive }\n : {}),\n ...(usage.webSearchCount ? { $ai_web_search_count: usage.webSearchCount } : {}),\n ...(usage.rawUsage ? { $ai_usage: usage.rawUsage } : {}),\n }\n\n const properties: Record<string, unknown> = {\n $ai_lib: 'posthog-ai',\n $ai_lib_version: version,\n $ai_provider: options.providerOverride ?? options.provider,\n $ai_model: options.modelOverride ?? options.model,\n $ai_model_parameters: options.modelParameters ?? {},\n ...(options.servedServiceTier != null ? { $ai_service_tier: options.servedServiceTier } : {}),\n $ai_input: safeInput,\n $ai_output_choices: safeOutput,\n $ai_http_status: httpStatus,\n ...(usage.inputTokens !== undefined ? { $ai_input_tokens: usage.inputTokens } : {}),\n ...(usage.outputTokens !== undefined ? { $ai_output_tokens: usage.outputTokens } : {}),\n ...additionalTokenValues,\n ...(options.latency !== undefined ? { $ai_latency: options.latency } : {}),\n ...(options.timeToFirstToken !== undefined ? { $ai_time_to_first_token: options.timeToFirstToken } : {}),\n $ai_trace_id: traceId,\n ...(options.baseURL === null ? {} : { $ai_base_url: options.baseURL ?? '' }),\n ...options.properties,\n $ai_tokens_source: getTokensSource(options.properties),\n ...(options.distinctId ? {} : { $process_person_profile: false }),\n ...(options.stopReason ? { $ai_stop_reason: options.stopReason } : {}),\n ...(options.tools ? { $ai_tools: options.tools } : {}),\n ...(options.completionId ? { $ai_completion_id: options.completionId } : {}),\n ...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0\n ? { $ai_provider_metadata: options.providerMetadata }\n : {}),\n ...errorData,\n ...costOverrideData,\n }\n\n const event: EventMessage = {\n distinctId: options.distinctId ?? traceId,\n event: eventType,\n properties,\n groups: options.groups,\n }\n\n if (options.captureImmediate) {\n await captureAiEventImmediate(client, event)\n } else {\n captureAiEvent(client, event)\n }\n } catch (error) {\n // Telemetry failures must never affect the instrumented provider call.\n try {\n options.onError?.(error)\n } catch {\n // Error reporting must not affect the instrumented provider call either.\n }\n console.warn('[PostHog AI] Failed to capture generation telemetry:', error)\n }\n}\n","import { toJsonSafeValue } from '@posthog/core'\nimport { isFullAiCaptureEnabled, type FullAiCaptureGate } from './captureAiEvent'\nimport { BinaryContentRedactor } from './sanitization/binary_content_redactor'\nimport { MediaTypeContext } from './sanitization/media_type_context'\nimport { truncate } from './utils'\n\n// Match toJsonSafeValue's structural safeguards. A separate visitor is needed\n// here because JSON conversion erases the identity of supported binary values.\nconst MAX_DEPTH = 20\nconst MAX_ITEMS = 1_000\nconst MAX_NODES = 10_000\nconst TRUNCATED = '[Truncated]'\nconst UNSERIALIZABLE = '[Unserializable]'\nconst redactor = new BinaryContentRedactor()\nconst propertyIsEnumerable = Object.prototype.propertyIsEnumerable\n\n/** @internal Preserve tool-result structure while bounding each string value. */\nexport function formatToolResult(content: unknown, client?: FullAiCaptureGate, maxBytes = 5000): unknown {\n try {\n // Full capture still has JSON traversal guards, but no content redaction or string cap.\n if (isFullAiCaptureEnabled(client)) return toJsonSafeValue(content)\n\n const ancestors = new WeakSet<object>()\n let remainingNodes = MAX_NODES\n const convert = (value: unknown, depth: number, context: MediaTypeContext): unknown => {\n if (remainingNodes <= 0) return TRUNCATED\n remainingNodes--\n try {\n if (value === null || typeof value !== 'object') {\n const safe = toJsonSafeValue(value)\n return typeof safe === 'string' ? truncate(redactor.redactLeaf(safe, context), undefined, maxBytes) : safe\n }\n if (depth >= MAX_DEPTH) return TRUNCATED\n\n // Buffer is a Uint8Array. Check before toJSON/ordinary object conversion,\n // so bytes cannot become an unrecognizable numeric object or JSON array.\n if (typeof Uint8Array !== 'undefined' && value instanceof Uint8Array) {\n return redactor.redactLeaf(value, context)\n }\n if (ancestors.has(value)) return '[Circular]'\n ancestors.add(value)\n try {\n if (value instanceof Date) {\n const safe = toJsonSafeValue(value)\n return typeof safe === 'string' ? truncate(redactor.redactLeaf(safe, context), undefined, maxBytes) : safe\n }\n\n let hasToJSONResult = false\n let toJSONResult: unknown\n try {\n const toJSON = (value as { toJSON?: unknown }).toJSON\n if (typeof toJSON === 'function') {\n toJSONResult = toJSON.call(value)\n hasToJSONResult = true\n }\n } catch {\n // Match toJsonSafeValue: a broken toJSON falls back to own properties.\n }\n if (hasToJSONResult) return convert(toJSONResult, depth + 1, context)\n\n if (Array.isArray(value)) {\n const output: unknown[] = []\n const count = Math.min(value.length, MAX_ITEMS)\n let index = 0\n for (; index < count && remainingNodes > 0; index++) {\n output.push(convert(value[index], depth + 1, context))\n }\n if (value.length > index) output.push(TRUNCATED)\n return output\n }\n\n const input = value as Record<string, unknown>\n const output: Record<string, unknown> = {}\n let count = 0\n let truncated = false\n for (const key in input) {\n if (!propertyIsEnumerable.call(input, key)) break\n if (count >= MAX_ITEMS || remainingNodes <= 0) {\n truncated = true\n break\n }\n const converted = convert(input[key], depth + 1, new MediaTypeContext(input, key))\n Object.defineProperty(output, key, {\n value: converted,\n enumerable: true,\n writable: true,\n configurable: true,\n })\n count++\n }\n if (truncated) output[TRUNCATED] = 'Additional properties omitted'\n return output\n } finally {\n ancestors.delete(value)\n }\n } catch {\n // Includes hostile values and media-context getters. Never fall back to raw bytes.\n return UNSERIALIZABLE\n }\n }\n return convert(content, 0, MediaTypeContext.EMPTY)\n } catch {\n // A capture configuration getter can fail too; instrumentation remains best effort.\n return UNSERIALIZABLE\n }\n}\n","import type { Options } from '@anthropic-ai/claude-agent-sdk'\nimport { SYSTEM_PROMPT_DYNAMIC_BOUNDARY } from '@anthropic-ai/claude-agent-sdk'\nimport type { PostHog } from 'posthog-node'\nimport { MAX_OUTPUT_SIZE, toContentString, isPrivacyModeEnabled } from '../utils'\nimport { formatToolResult } from '../toolResult'\nimport type { FormattedContent, FormattedContentItem } from '../types'\n\n/** Thinking blocks, recorded with the `reasoning` shape the other adapters use. */\ninterface FormattedReasoningContent {\n type: 'reasoning'\n text: string\n}\n\nexport type ClaudeAgentContentItem = FormattedContentItem | FormattedReasoningContent\n\nexport function formatContent(content: unknown, client: PostHog, maxBytes = MAX_OUTPUT_SIZE): unknown {\n return formatToolResult(content, client, maxBytes)\n}\n\n/** Read the system prompt out of the SDK options, whatever shape it takes. */\nexport function extractSystemPrompt(options: Options | undefined): string | undefined {\n const systemPrompt = options?.systemPrompt\n if (typeof systemPrompt === 'string') {\n return systemPrompt\n }\n if (Array.isArray(systemPrompt)) {\n return systemPrompt.filter((part) => part !== SYSTEM_PROMPT_DYNAMIC_BOUNDARY).join('')\n }\n return systemPrompt?.append\n}\n\n/** Convert Anthropic assistant content blocks into PostHog content items. */\nexport function formatAssistantBlocks(blocks: unknown, client: PostHog): ClaudeAgentContentItem[] {\n if (!Array.isArray(blocks)) {\n return []\n }\n\n const content: ClaudeAgentContentItem[] = []\n for (const block of formatContent(blocks, client) as Array<Record<string, any>>) {\n if (block == null) {\n continue\n }\n if (typeof block.thinking === 'string') {\n content.push({ type: 'reasoning', text: block.thinking })\n } else if (block.type === 'tool_use') {\n content.push({\n type: 'function',\n id: block.id,\n function: {\n name: block.name,\n arguments: block.input ?? {},\n },\n })\n } else if (typeof block.text === 'string') {\n content.push({ type: 'text', text: block.text })\n } else {\n content.push({ type: 'text', text: toContentString(block) })\n }\n }\n return content\n}\n\n/** Convert an Anthropic user message body into PostHog content items. */\nexport function formatUserContent(content: unknown, client: PostHog, privacyMode = false): FormattedContent | unknown {\n if (isPrivacyModeEnabled(client, privacyMode)) return []\n if (typeof content === 'string') {\n return content\n }\n if (!Array.isArray(content)) {\n return []\n }\n\n const formatted: unknown[] = []\n for (const block of content as Array<Record<string, any>>) {\n if (block == null) {\n continue\n }\n if (block.type === 'tool_result') {\n formatted.push({\n type: 'tool_result',\n tool_use_id: block.tool_use_id,\n content: formatToolResult(block.content, client),\n ...(typeof block.is_error === 'boolean' ? { is_error: block.is_error } : {}),\n })\n } else if (typeof block.text === 'string') {\n formatted.push({ type: 'text', text: block.text })\n } else {\n formatted.push(formatContent(block, client))\n }\n }\n return formatted\n}\n","import { query as originalQuery } from '@anthropic-ai/claude-agent-sdk'\nimport type {\n Options,\n Query,\n SDKAssistantMessage,\n SDKMessage,\n SDKPartialAssistantMessage,\n SDKResultMessage,\n SDKUserMessage,\n} from '@anthropic-ai/claude-agent-sdk'\nimport type { EventMessage, PostHog } from 'posthog-node'\nimport { v4 as uuidv4 } from 'uuid'\nimport { version } from '../../package.json'\nimport { captureAiEvent, captureAiEventImmediate } from '../captureAiEvent'\nimport { captureAiGeneration } from '../captureAiGeneration'\nimport type { CaptureAiGenerationOptions } from '../captureAiGeneration'\nimport { stringifyError } from '../serializeError'\nimport type { FormattedMessage, TokenUsage } from '../types'\nimport { isPrivacyModeEnabled } from '../utils'\nimport { extractSystemPrompt, formatAssistantBlocks, formatContent, formatUserContent } from './formatting'\nimport type { ClaudeAgentContentItem } from './formatting'\n\nconst FRAMEWORK = 'claude-agent-sdk'\nconst PROVIDER = 'anthropic'\n\n/**\n * Resolver for the PostHog distinct ID. Either a static string, or a function\n * that derives it from the SDK result message. Return `null`/`undefined` to\n * capture anonymously (personless), keyed by the trace ID.\n */\nexport type DistinctIdResolver = string | ((result: SDKResultMessage) => string | null | undefined)\n\n/** Per-query tracing options. Each one overrides the processor default. */\nexport interface ClaudeAgentTraceOptions {\n /** Distinct ID for the captured events. Defaults to anonymous (personless) capture. */\n distinctId?: DistinctIdResolver\n /**\n * Trace ID shared by every event of the query. Generated per turn when\n * omitted, so each turn of a streaming-input session is its own trace.\n */\n traceId?: string\n /** Redacts captured input/output content when true. Defaults to false. */\n privacyMode?: boolean\n /** Group analytics mapping (group type -> group id) attached to every event. */\n groups?: Record<string, string | number>\n /** Extra properties merged into every captured AI event. */\n properties?: Record<string, unknown>\n}\n\nexport interface PostHogClaudeAgentProcessorOptions extends ClaudeAgentTraceOptions {\n /** The PostHog client used to capture events. */\n client: PostHog\n /** Awaits event delivery instead of batching. Useful in serverless environments. */\n captureImmediate?: boolean\n /** Invoked when the processor fails to capture an event. Never throws into the query. */\n onError?: (error: unknown) => void\n}\n\nexport interface ClaudeAgentQueryParams {\n /** The prompt, exactly as `query()` from the Claude Agent SDK takes it. */\n prompt: string | AsyncIterable<SDKUserMessage>\n /** Claude Agent SDK options. `includePartialMessages` is enabled internally. */\n options?: Options\n /** Tracing options for this query, overriding the processor defaults. */\n posthog?: ClaudeAgentTraceOptions\n}\n\ntype StreamEvent = SDKPartialAssistantMessage['event']\n\ninterface AnthropicUsage {\n input_tokens?: number | null\n output_tokens?: number | null\n cache_read_input_tokens?: number | null\n cache_creation_input_tokens?: number | null\n server_tool_use?: { web_search_requests?: number | null } | null\n}\n\n/** Metrics of a single model call, reconstructed from the streamed events. */\ninterface GenerationData {\n spanId: string\n error?: unknown\n input?: FormattedMessage[]\n startTime: number\n endTime?: number\n timeToFirstToken?: number\n model?: string\n usage: TokenUsage\n stopReason?: string\n streamedOutput?: ClaudeAgentContentItem[]\n}\n\n/**\n * Reconstructs per-generation metrics from the Anthropic streaming protocol the\n * Agent SDK relays: every `message_start` to `message_stop` cycle is one model\n * call.\n */\nclass GenerationTracker {\n private _current?: GenerationData\n private readonly _completed: GenerationData[] = []\n private _lastModel?: string\n private _sawStreamEvents = false\n private _pendingInput?: FormattedMessage[]\n\n /**\n * Queue the input of the next model call. The first call answers the prompt,\n * and every later one answers the tool results of the call before it.\n */\n setPendingInput(input: FormattedMessage[] | undefined): void {\n this._pendingInput = input\n }\n\n appendPendingInput(input: FormattedMessage[]): void {\n this._pendingInput = [...(this._pendingInput ?? []), ...input]\n }\n\n /** The queued input, still waiting for its model call. */\n get pendingInput(): FormattedMessage[] | undefined {\n return this._pendingInput\n }\n\n processStreamEvent(event: StreamEvent, client: PostHog, ttftMs?: number): void {\n this._sawStreamEvents = true\n\n if (event.type === 'message_start') {\n this.retryCurrent(new Error('Model stream restarted before completing'))\n const usage = event.message.usage as AnthropicUsage | undefined\n this._current = {\n spanId: uuidv4(),\n input: this._pendingInput,\n startTime: performance.now() - (ttftMs ?? 0),\n timeToFirstToken: ttftMs != null ? ttftMs / 1000 : undefined,\n model: event.message.model,\n usage: readUsage(usage),\n streamedOutput: [],\n }\n this._pendingInput = undefined\n } else if (event.type === 'content_block_start' && this._current?.streamedOutput) {\n this._current.streamedOutput[event.index] = formatAssistantBlocks([event.content_block], client)[0]\n } else if (event.type === 'content_block_delta') {\n const block = this._current?.streamedOutput?.[event.index]\n if (event.delta.type === 'text_delta' && block?.type === 'text') {\n block.text += event.delta.text\n } else if (event.delta.type === 'thinking_delta' && block?.type === 'reasoning') {\n block.text += event.delta.thinking\n } else if (event.delta.type === 'input_json_delta' && block?.type === 'function') {\n const previous = block.function.arguments\n block.function.arguments = (typeof previous === 'string' ? previous : '') + event.delta.partial_json\n }\n } else if (event.type === 'message_delta' && this._current) {\n const usage = event.usage as AnthropicUsage | undefined\n // `message_delta` reports the cumulative counts of the call so far.\n this._current.usage = {\n ...this._current.usage,\n ...readUsage(usage),\n rawUsage: { ...(this._current.usage.rawUsage as Record<string, unknown>), ...usage },\n }\n if (event.delta?.stop_reason != null) {\n this._current.stopReason = event.delta.stop_reason\n }\n } else if (event.type === 'message_stop') {\n this.finishCurrent()\n }\n }\n\n finishCurrent(): void {\n if (this._current) {\n this._current.endTime = performance.now()\n this._completed.push(this._current)\n this._lastModel = this._current.model\n this._current = undefined\n }\n }\n\n retryCurrent(error: unknown): void {\n if (this._current) {\n this._pendingInput = this._current.input\n this._current.error = error\n this.finishCurrent()\n }\n }\n\n setModel(model: string | undefined): void {\n if (model) {\n this._lastModel = model\n }\n }\n\n popCompleted(): GenerationData | undefined {\n return this._completed.shift()\n }\n\n /** Span ID of the generation in progress, before its `message_stop`. */\n get currentSpanId(): string | undefined {\n return this._current?.spanId\n }\n\n get lastModel(): string | undefined {\n return this._lastModel\n }\n\n get sawStreamEvents(): boolean {\n return this._sawStreamEvents\n }\n}\n\n/** Everything the instrumentation tracks for the turn in flight. */\ninterface QueryState {\n tracker: GenerationTracker\n systemPrompt?: string\n pendingCaptures: Array<(distinctId?: string) => Promise<void>>\n pendingTools: Map<string, { startTime: number; properties: Record<string, unknown> }>\n traceId: string\n turnStart: number\n // Subagent tool spans can arrive outside a main-agent generation.\n turnCaptured: boolean\n turnActive: boolean\n hasCapturedTrace: boolean\n pendingPrompts: SDKUserMessage[]\n userMessageUuid?: string\n failure?: unknown\n generationIndex: number\n lastGenerationSpanId?: string\n sessionId?: string\n totalCost: number\n turnCost?: number\n // One model call can deliver its blocks over several assistant messages, so\n // the content of a turn accumulates until the turn closes.\n pendingOutput: ClaudeAgentContentItem[]\n}\n\n/**\n * Wraps `query()` from `@anthropic-ai/claude-agent-sdk` to capture PostHog LLM\n * analytics.\n *\n * The Agent SDK runs Claude Code itself, so no request passes through an\n * Anthropic client the `@posthog/ai` Anthropic wrapper could patch. This\n * processor reads the SDK's own message stream instead and captures:\n *\n * - `$ai_generation` for each main-agent model call, reconstructed from the streamed\n * Anthropic events;\n * - `$ai_span` for every tool use, parented to the generation that asked for it;\n * - `$ai_trace` for every turn, carrying its latency and reported cost.\n *\n * @example\n * ```typescript\n * import { PostHogClaudeAgentProcessor } from '@posthog/ai/claude-agent-sdk'\n * import { PostHog } from 'posthog-node'\n *\n * const phClient = new PostHog('<POSTHOG_API_KEY>')\n * const processor = new PostHogClaudeAgentProcessor({ client: phClient, distinctId: 'user@example.com' })\n *\n * for await (const message of processor.query({ prompt: 'Explain this repo' })) {\n * console.log(message)\n * }\n * ```\n */\nexport class PostHogClaudeAgentProcessor {\n private readonly _client: PostHog\n private readonly _traceOptions: ClaudeAgentTraceOptions\n private readonly _captureImmediate: boolean\n private readonly _onError?: (error: unknown) => void\n\n constructor(options: PostHogClaudeAgentProcessorOptions) {\n this._client = options.client\n this._traceOptions = options\n this._captureImmediate = options.captureImmediate ?? false\n this._onError = options.onError\n }\n\n /**\n * Drop-in replacement for `query()` from the Claude Agent SDK. Hidden partial\n * messages pass their reply correlation to the first assistant message.\n * Other messages and the control methods (`interrupt`,\n * `setPermissionMode`, …) retain the SDK behavior.\n */\n query({ prompt, options, posthog }: ClaudeAgentQueryParams): Query {\n const trace: ClaudeAgentTraceOptions = {\n ...this._traceOptions,\n ...posthog,\n // A per-query setting can turn redaction on but never off, so a processor\n // configured for privacy stays private.\n privacyMode: this._traceOptions.privacyMode === true || posthog?.privacyMode === true,\n properties: { ...this._traceOptions.properties, ...posthog?.properties },\n }\n for (const key of ['$ai_trace_id', '$ai_span_id', '$ai_parent_id', '$ai_session_id']) {\n delete trace.properties?.[key]\n }\n\n // Partial messages carry the per-generation metrics, so they are always\n // requested. Callers who did not ask for them never see them.\n const forwardStreamEvents = options?.includePartialMessages === true\n const state = this._createState(prompt, options, trace)\n const inner = originalQuery({\n prompt: typeof prompt === 'string' ? prompt : this._observePrompt(prompt, state),\n options: { ...options, includePartialMessages: true },\n })\n\n const instrumented = this._instrument(inner, state, trace, forwardStreamEvents)\n // A `for await` loop reads `next` once per message, so the iteration\n // methods are bound once rather than on every read.\n const iteration: Record<string | symbol, unknown> = {\n next: instrumented.next.bind(instrumented),\n streamInput: (stream: AsyncIterable<SDKUserMessage>) => inner.streamInput(this._observePrompt(stream, state)),\n // The SDK starts its subprocess before the first next(), so cleanup must reach it directly.\n return: async (value?: void) => {\n try {\n return await inner.return(value)\n } finally {\n await instrumented.return()\n }\n },\n throw: async (error?: unknown) => {\n state.failure = error\n try {\n return await inner.throw(error)\n } finally {\n await instrumented.return()\n }\n },\n close: () => {\n try {\n inner.close()\n } finally {\n void instrumented.return().catch((error) => this._handleError(error))\n }\n },\n [Symbol.asyncDispose]: async () => {\n try {\n const dispose = Reflect.get(inner, Symbol.asyncDispose)\n if (typeof dispose === 'function') {\n await dispose.call(inner)\n } else {\n await inner.return()\n }\n } finally {\n await instrumented.return()\n }\n },\n [Symbol.asyncIterator]: () => wrapper,\n }\n\n // The SDK returns a generator that also carries control methods\n // (`interrupt`, `setPermissionMode`, …), so iteration goes to the wrapper\n // and every other read goes to the original object.\n const wrapper: Query = new Proxy(inner, {\n get(target, property) {\n if (Object.hasOwn(iteration, property)) {\n return iteration[property]\n }\n // boffin: SDK getters can read private fields on the original query.\n const value = Reflect.get(target, property, target)\n return typeof value === 'function' && property !== 'constructor' ? value.bind(target) : value\n },\n })\n return wrapper\n }\n\n private async *_instrument(\n inner: Query,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n forwardStreamEvents: boolean\n ): AsyncGenerator<SDKMessage, void> {\n try {\n for await (const message of inner) {\n try {\n await this._handleMessage(message, state, trace)\n } catch (error) {\n this._handleError(error)\n }\n if (message.type !== 'stream_event' || forwardStreamEvents) {\n if (\n !forwardStreamEvents &&\n message.type === 'assistant' &&\n !message.parent_tool_use_id &&\n state.userMessageUuid\n ) {\n const userMessageUuid = state.userMessageUuid\n state.userMessageUuid = undefined\n yield { ...message, user_message_uuid: message.user_message_uuid ?? userMessageUuid }\n } else {\n yield message\n }\n }\n }\n } catch (error) {\n state.failure = error\n throw error\n } finally {\n const failure = state.failure\n // An aborted query, or a caller that stops iterating, never delivers a\n // result message. The turn is still closed so its generations and spans\n // have a trace.\n // The SDK can throw after an error result already closed the last turn.\n if (state.turnActive || state.turnCaptured || (failure !== undefined && !state.hasCapturedTrace)) {\n try {\n state.tracker.finishCurrent()\n await this._captureCompletedGenerations(state, trace, failure)\n if (!state.tracker.sawStreamEvents && state.pendingOutput.length > 0) {\n state.generationIndex += 1\n await this._captureGeneration(\n {\n spanId: uuidv4(),\n input: state.tracker.pendingInput,\n startTime: state.turnStart,\n endTime: performance.now(),\n usage: {},\n error: failure,\n },\n state,\n trace\n )\n }\n } catch (error) {\n this._handleError(error)\n }\n try {\n await this._captureTrace(state, trace, undefined, failure)\n } catch (error) {\n this._handleError(error)\n }\n }\n }\n }\n\n private _createState(\n prompt: string | AsyncIterable<SDKUserMessage>,\n options: Options | undefined,\n trace: ClaudeAgentTraceOptions\n ): QueryState {\n const initialInput: FormattedMessage[] = []\n const systemPrompt = extractSystemPrompt(options)\n if (systemPrompt) {\n initialInput.push({ role: 'system', content: systemPrompt })\n }\n\n const tracker = new GenerationTracker()\n tracker.setPendingInput(initialInput.length > 0 ? initialInput : undefined)\n\n return {\n tracker,\n systemPrompt,\n pendingCaptures: [],\n pendingTools: new Map(),\n traceId: trace.traceId ?? uuidv4(),\n turnStart: performance.now(),\n turnCaptured: false,\n turnActive: false,\n hasCapturedTrace: false,\n pendingPrompts:\n typeof prompt === 'string'\n ? [{ type: 'user', message: { role: 'user', content: prompt }, parent_tool_use_id: null }]\n : [],\n generationIndex: 0,\n pendingOutput: [],\n totalCost: 0,\n }\n }\n\n private async *_observePrompt(\n prompt: AsyncIterable<SDKUserMessage>,\n state: QueryState\n ): AsyncGenerator<SDKUserMessage> {\n for await (const message of prompt) {\n state.pendingPrompts.push(message)\n yield message\n }\n }\n\n private _beginTurn(state: QueryState, trace: ClaudeAgentTraceOptions, userMessageUuid?: string): void {\n if (state.turnActive) {\n return\n }\n state.turnActive = true\n\n // The SDK can read ahead or prioritize a later prompt before answering it.\n let end = userMessageUuid ? state.pendingPrompts.findIndex((message) => message.uuid === userMessageUuid) : -1\n if (end < 0) {\n const candidates = state.pendingPrompts.filter((message) => message.shouldQuery !== false)\n if (candidates.length !== 1 || (userMessageUuid && candidates[0].uuid)) {\n return\n }\n end = state.pendingPrompts.indexOf(candidates[0])\n }\n if (end < 0) {\n return\n }\n let start = end\n while (start > 0 && state.pendingPrompts[start - 1].shouldQuery === false) {\n start -= 1\n }\n for (const message of state.pendingPrompts.splice(start, end - start + 1)) {\n state.tracker.appendPendingInput([\n { role: 'user', content: formatUserContent(message.message.content, this._client, trace.privacyMode) },\n ])\n }\n }\n\n private async _handleMessage(message: SDKMessage, state: QueryState, trace: ClaudeAgentTraceOptions): Promise<void> {\n if ('session_id' in message && message.session_id) {\n state.sessionId = message.session_id\n }\n\n if (message.type === 'conversation_reset') {\n state.totalCost = 0\n } else if (message.type === 'system' && message.subtype === 'api_retry') {\n state.tracker.retryCurrent(Object.assign(new Error(message.error), { status: message.error_status }))\n await this._captureCompletedGenerations(state, trace)\n } else if (message.type === 'stream_event') {\n if (message.parent_tool_use_id) {\n return\n }\n if (message.user_message_uuid) {\n state.userMessageUuid = message.user_message_uuid\n }\n if (message.event.type === 'message_start') {\n this._beginTurn(state, trace, message.user_message_uuid)\n }\n state.tracker.processStreamEvent(message.event, this._client, message.ttft_ms)\n await this._captureCompletedGenerations(state, trace)\n } else if (message.type === 'assistant') {\n if (!message.parent_tool_use_id) {\n this._beginTurn(state, trace, message.user_message_uuid)\n }\n this._handleAssistantMessage(message, state, trace)\n } else if (message.type === 'user') {\n if ('isReplay' in message && message.isReplay) {\n return\n }\n if (Array.isArray(message.message.content)) {\n for (const block of message.message.content) {\n if (block.type === 'tool_result') {\n await this._finishToolSpan(block.tool_use_id, state, trace, block)\n }\n }\n }\n if (message.parent_tool_use_id) {\n return\n }\n // A user message carries the tool results of the model call that asked\n // for them, so it becomes the input of the next model call.\n const content = formatUserContent(message.message?.content, this._client, trace.privacyMode)\n if (typeof content === 'string' || (Array.isArray(content) && content.length > 0)) {\n state.tracker.appendPendingInput([{ role: 'user', content }])\n }\n } else if (message.type === 'result') {\n try {\n this._beginTurn(state, trace, message.user_message_uuid)\n state.tracker.finishCurrent()\n await this._captureCompletedGenerations(state, trace, resultError(message), message)\n if (message.total_cost_usd != null) {\n // The SDK reports cumulative cost and can reset it when the session is cleared.\n state.turnCost = message.total_cost_usd - (message.total_cost_usd >= state.totalCost ? state.totalCost : 0)\n state.totalCost = message.total_cost_usd\n }\n // Without partial messages there is no per-call metric, so the result's\n // aggregate becomes one generation.\n if (!state.tracker.sawStreamEvents) {\n await this._captureGenerationFromResult(message, state, trace)\n }\n } finally {\n await this._captureTrace(state, trace, message)\n }\n }\n }\n\n private async _captureCompletedGenerations(\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n failure?: unknown,\n result?: SDKResultMessage\n ): Promise<void> {\n let generation = state.tracker.popCompleted()\n while (generation) {\n generation.error ??= failure\n state.generationIndex += 1\n state.lastGenerationSpanId = generation.spanId\n state.turnCaptured = true\n await this._captureGeneration(generation, state, trace, {}, result)\n state.pendingOutput = []\n generation = state.tracker.popCompleted()\n }\n }\n\n private _handleAssistantMessage(\n message: SDKAssistantMessage,\n state: QueryState,\n trace: ClaudeAgentTraceOptions\n ): void {\n if (!message.parent_tool_use_id) {\n state.tracker.setModel(message.message?.model)\n }\n\n // An assistant message arrives before its `message_stop`, so the generation\n // in progress is the parent of its tool calls.\n const parentSpanId = message.parent_tool_use_id ?? state.tracker.currentSpanId ?? state.lastGenerationSpanId\n const blocks = Array.isArray(message.message?.content)\n ? (message.message.content as Array<Record<string, any>>)\n : []\n for (const block of blocks) {\n if (block?.type === 'tool_use') {\n state.turnCaptured = true\n this._startToolSpan(block, state, trace, parentSpanId)\n }\n }\n\n if (!message.parent_tool_use_id) {\n state.pendingOutput.push(...formatAssistantBlocks(message.message?.content, this._client))\n }\n }\n\n private async _captureGeneration(\n generation: GenerationData,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n extraProperties: Record<string, unknown> = {},\n result?: SDKResultMessage\n ): Promise<void> {\n const options: CaptureAiGenerationOptions = {\n traceId: state.traceId,\n model: generation.model ?? state.tracker.lastModel,\n provider: PROVIDER,\n baseURL: null,\n httpStatus: result?.subtype === 'success' ? (result.api_error_status ?? undefined) : undefined,\n error: generation.error,\n input: generation.input ?? [],\n // Complete assistant messages replace the streamed prefix, leaving only unfinished blocks to append.\n output: formatContent(\n formatOutput([\n ...state.pendingOutput,\n ...(generation.streamedOutput?.slice(state.pendingOutput.length).filter(Boolean) ?? []),\n ]),\n this._client\n ),\n latency: generation.endTime != null ? (generation.endTime - generation.startTime) / 1000 : undefined,\n timeToFirstToken: generation.timeToFirstToken,\n usage: generation.usage,\n stopReason: generation.stopReason,\n groups: trace.groups,\n privacyMode: trace.privacyMode ?? false,\n captureImmediate: this._captureImmediate,\n onError: this._onError,\n properties: {\n $ai_framework: FRAMEWORK,\n $ai_span_id: generation.spanId,\n $ai_span_name: `generation_${state.generationIndex}`,\n ...(state.sessionId ? { $ai_session_id: state.sessionId } : {}),\n ...extraProperties,\n ...trace.properties,\n },\n }\n await this._withDistinctId(state, trace, (distinctId) =>\n captureAiGeneration(this._client, { ...options, distinctId })\n )\n }\n\n /**\n * Without partial messages there is no per-call metric, so the aggregate the\n * result message reports becomes one generation.\n */\n private async _captureGenerationFromResult(\n result: SDKResultMessage,\n state: QueryState,\n trace: ClaudeAgentTraceOptions\n ): Promise<void> {\n state.generationIndex += 1\n const endTime = performance.now()\n\n const generation: GenerationData = {\n spanId: uuidv4(),\n input: state.tracker.pendingInput ?? [],\n startTime: endTime - (result.duration_api_ms ?? 0),\n endTime,\n usage: readUsage(result.usage as AnthropicUsage | undefined),\n stopReason: result.stop_reason ?? undefined,\n error: resultError(result),\n }\n\n await this._captureGeneration(\n generation,\n state,\n trace,\n {\n ...(state.turnCost != null ? { $ai_total_cost_usd: state.turnCost } : {}),\n },\n result\n )\n }\n\n private _startToolSpan(\n block: Record<string, any>,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n parentSpanId: string | undefined\n ): void {\n const properties = {\n $ai_span_id: block.id ?? uuidv4(),\n ...(parentSpanId ? { $ai_parent_id: parentSpanId } : {}),\n $ai_span_name: block.name,\n $ai_span_type: 'tool',\n $ai_input_state: isPrivacyModeEnabled(this._client, trace.privacyMode ?? false)\n ? null\n : formatContent(block.input ?? {}, this._client),\n }\n state.pendingTools.set(properties.$ai_span_id, { startTime: performance.now(), properties })\n }\n\n private async _finishToolSpan(\n id: string,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n result?: { content?: unknown; is_error?: boolean }\n ): Promise<void> {\n const tool = state.pendingTools.get(id)\n if (!tool) {\n return\n }\n state.pendingTools.delete(id)\n const properties = {\n ...tool.properties,\n $ai_latency: (performance.now() - tool.startTime) / 1000,\n ...(result\n ? {\n $ai_output_state: isPrivacyModeEnabled(this._client, trace.privacyMode ?? false)\n ? null\n : formatContent(result.content, this._client),\n $ai_is_error: result.is_error === true,\n }\n : {}),\n }\n await this._withDistinctId(state, trace, (distinctId) =>\n this._captureLifecycleEvent('$ai_span', distinctId, state, trace, properties)\n )\n }\n\n private async _withDistinctId(\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n capture: (distinctId?: string) => Promise<void>\n ): Promise<void> {\n if (typeof trace.distinctId === 'function') {\n // boffin: Resolve the person once per turn so all its events use the same identity.\n state.pendingCaptures.push(capture)\n } else {\n await capture(resolveDistinctId(trace.distinctId))\n }\n }\n\n private async _captureTrace(\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n result?: SDKResultMessage,\n failure?: unknown\n ): Promise<void> {\n try {\n const latency =\n result?.duration_ms != null ? result.duration_ms / 1000 : (performance.now() - state.turnStart) / 1000\n const isError = failure !== undefined || result?.is_error === true\n const error = failure ?? (result && resultError(result))\n\n for (const id of state.pendingTools.keys()) {\n await this._finishToolSpan(id, state, trace)\n }\n let distinctId: string | undefined\n try {\n distinctId = resolveDistinctId(trace.distinctId, result)\n } catch (error) {\n this._handleError(error)\n }\n for (const capture of state.pendingCaptures.splice(0)) {\n try {\n await capture(distinctId)\n } catch (error) {\n this._handleError(error)\n }\n }\n await this._captureLifecycleEvent('$ai_trace', distinctId, state, trace, {\n $ai_trace_name: 'claude_agent_sdk_query',\n $ai_latency: latency,\n ...(state.turnCost != null ? { $ai_total_cost_usd: state.turnCost } : {}),\n ...(isError ? { $ai_is_error: true } : {}),\n ...(error !== undefined ? { $ai_error: stringifyError(error) } : {}),\n ...(result?.subtype === 'success' && result.api_error_status != null\n ? { $ai_http_status: result.api_error_status }\n : {}),\n })\n } finally {\n // A streaming-input session produces one result per turn. Each turn becomes\n // its own trace unless the caller pinned a trace ID for the whole session.\n state.hasCapturedTrace = true\n state.traceId = trace.traceId ?? uuidv4()\n state.turnStart = performance.now()\n state.turnCaptured = false\n state.turnActive = false\n state.userMessageUuid = undefined\n state.failure = undefined\n state.generationIndex = 0\n state.lastGenerationSpanId = undefined\n state.turnCost = undefined\n state.pendingCaptures = []\n state.pendingTools.clear()\n if (result?.queued_turn_count === 0) {\n state.pendingPrompts = state.pendingPrompts.filter((message) => message.shouldQuery === false)\n }\n state.tracker = new GenerationTracker()\n if (state.systemPrompt) {\n state.tracker.setPendingInput([{ role: 'system', content: state.systemPrompt }])\n }\n state.pendingOutput = []\n }\n }\n\n private async _captureLifecycleEvent(\n event: '$ai_trace' | '$ai_span',\n distinctId: string | undefined,\n state: QueryState,\n trace: ClaudeAgentTraceOptions,\n properties: Record<string, unknown>\n ): Promise<void> {\n const message: EventMessage = {\n distinctId: distinctId ?? state.traceId,\n event,\n properties: {\n $ai_lib: 'posthog-ai',\n $ai_lib_version: version,\n $ai_framework: FRAMEWORK,\n $ai_provider: PROVIDER,\n $ai_trace_id: state.traceId,\n ...(state.sessionId ? { $ai_session_id: state.sessionId } : {}),\n ...properties,\n ...trace.properties,\n ...(distinctId ? {} : { $process_person_profile: false }),\n },\n groups: trace.groups,\n }\n\n try {\n if (this._captureImmediate) {\n await captureAiEventImmediate(this._client, message)\n } else {\n captureAiEvent(this._client, message)\n }\n } catch (error) {\n this._handleError(error)\n }\n }\n\n private _handleError(error: unknown): void {\n try {\n this._onError?.(error)\n } catch {\n // Instrumentation must never throw into the query.\n }\n }\n}\n\nfunction formatOutput(content: ClaudeAgentContentItem[]): FormattedMessage[] {\n return content.length > 0 ? [{ role: 'assistant', content }] : []\n}\n\nfunction resultError(result: SDKResultMessage): string | undefined {\n if (!result.is_error) {\n return undefined\n }\n return (result.subtype === 'success' ? result.result : result.errors.join('\\n')) || result.subtype\n}\n\nfunction readUsage(usage: AnthropicUsage | undefined): TokenUsage {\n return {\n ...(usage && Object.keys(usage).length > 0 ? { rawUsage: { ...usage } } : {}),\n ...(usage?.input_tokens != null ? { inputTokens: usage.input_tokens } : {}),\n ...(usage?.output_tokens != null ? { outputTokens: usage.output_tokens } : {}),\n ...(usage?.cache_read_input_tokens != null ? { cacheReadInputTokens: usage.cache_read_input_tokens } : {}),\n ...(usage?.cache_creation_input_tokens != null\n ? { cacheCreationInputTokens: usage.cache_creation_input_tokens }\n : {}),\n ...(usage?.server_tool_use?.web_search_requests != null\n ? { webSearchCount: usage.server_tool_use.web_search_requests }\n : {}),\n }\n}\n\nfunction resolveDistinctId(resolver: DistinctIdResolver | undefined, result?: SDKResultMessage): string | undefined {\n if (typeof resolver === 'function') {\n if (!result) {\n return undefined\n }\n const resolved = resolver(result)\n return resolved ? String(resolved) : undefined\n }\n return resolver ? String(resolver) : undefined\n}\n","import type { Query } from '@anthropic-ai/claude-agent-sdk'\nimport { PostHogClaudeAgentProcessor } from './processor'\nimport type { ClaudeAgentQueryParams, PostHogClaudeAgentProcessorOptions } from './processor'\n\nexport { PostHogClaudeAgentProcessor } from './processor'\nexport type {\n PostHogClaudeAgentProcessorOptions,\n ClaudeAgentQueryParams,\n ClaudeAgentTraceOptions,\n DistinctIdResolver,\n} from './processor'\n\nexport type InstrumentOptions = PostHogClaudeAgentProcessorOptions\n\n/**\n * Create a PostHog-instrumented `query()` for the Claude Agent SDK.\n *\n * @param options - Configuration options\n * @returns A processor whose `query()` method replaces `query()` from the SDK\n *\n * @example\n * ```typescript\n * import { instrument } from '@posthog/ai/claude-agent-sdk'\n * import { PostHog } from 'posthog-node'\n *\n * const phClient = new PostHog('<POSTHOG_API_KEY>')\n * const claude = instrument({ client: phClient, distinctId: 'user@example.com' })\n *\n * for await (const message of claude.query({ prompt: 'Explain this repo' })) {\n * console.log(message)\n * }\n * ```\n */\nexport function instrument(options: InstrumentOptions): PostHogClaudeAgentProcessor {\n return new PostHogClaudeAgentProcessor(options)\n}\n\n/**\n * Drop-in replacement for `query()` from the Claude Agent SDK, for a single\n * query. Use {@link instrument} to share one configuration across queries.\n *\n * @example\n * ```typescript\n * import { query } from '@posthog/ai/claude-agent-sdk'\n * import { PostHog } from 'posthog-node'\n *\n * const phClient = new PostHog('<POSTHOG_API_KEY>')\n *\n * for await (const message of query({\n * prompt: 'Explain this repo',\n * posthog: { client: phClient, distinctId: 'user@example.com' },\n * })) {\n * console.log(message)\n * }\n * ```\n */\nexport function query(\n params: Omit<ClaudeAgentQueryParams, 'posthog'> & { posthog: PostHogClaudeAgentProcessorOptions }\n): Query {\n const { prompt, options, posthog } = params\n return new PostHogClaudeAgentProcessor(posthog).query({ prompt, options })\n}\n"],"mappings":"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"}
|