@mastra/posthog 1.2.2 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @mastra/posthog
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added `$ai_tools` to exported `$ai_generation` events. When tool definitions are present on the generation span (requires @mastra/core with tool-definition capture), they are sent to PostHog in the OpenAI function format, so PostHog LLM Analytics shows the tool schemas each generation ran with. Related: #20242 ([#20243](https://github.com/mastra-ai/mastra/pull/20243))
8
+
9
+ ```typescript
10
+ // No configuration change needed:
11
+ new PosthogExporter({ apiKey: process.env.POSTHOG_KEY, host: 'https://us.i.posthog.com' });
12
+ // $ai_generation events now include:
13
+ // $ai_tools: [
14
+ // {
15
+ // type: 'function',
16
+ // function: { name: 'get_weather', description: 'Get the weather for a city', parameters: { ... } },
17
+ // },
18
+ // ]
19
+ ```
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [[`8d2399b`](https://github.com/mastra-ai/mastra/commit/8d2399b638f8e0945cf2cda0187dbea8dcf0b784), [`c8002da`](https://github.com/mastra-ai/mastra/commit/c8002da7775c468e2965b6ff5f82045450fa8cb9), [`92be47f`](https://github.com/mastra-ai/mastra/commit/92be47fbd26ffccec0e2131ef7c1d9e70dd5ef4a), [`89200ba`](https://github.com/mastra-ai/mastra/commit/89200bafa05444bb7949b363ce7b743e29867561), [`3d6ae10`](https://github.com/mastra-ai/mastra/commit/3d6ae107c009a40cef08e83da6866c20783d7ac1), [`c950138`](https://github.com/mastra-ai/mastra/commit/c950138e72e4f317a40187e3800588731ab790ce), [`810c7e7`](https://github.com/mastra-ai/mastra/commit/810c7e74929989d8b8b5db52cd3af22cd0998af4), [`063c8b2`](https://github.com/mastra-ai/mastra/commit/063c8b2eb14e4e5ca021779bc33e8c3c031c8604), [`f9f9884`](https://github.com/mastra-ai/mastra/commit/f9f98848ee194dc71a787a709ec430b065cdc41b), [`e0904dc`](https://github.com/mastra-ai/mastra/commit/e0904dc538792e54e1806b70172e5900ac49bff4), [`9672fab`](https://github.com/mastra-ai/mastra/commit/9672fabfbcadb961a35c22a2d6722e077f7b24b9), [`f4e964c`](https://github.com/mastra-ai/mastra/commit/f4e964cad57057301d6bed5c55bcdd730175b941), [`1f7bbd7`](https://github.com/mastra-ai/mastra/commit/1f7bbd7785a8d230aad02454ecabeb4a0b2cc96f), [`e47ff36`](https://github.com/mastra-ai/mastra/commit/e47ff36945720f4ee4caa09f6e83514d7d188608), [`64d6781`](https://github.com/mastra-ai/mastra/commit/64d67814bccddd314f7e09643243821e57cb87b6), [`fb9a6ac`](https://github.com/mastra-ai/mastra/commit/fb9a6ac11c9560518742ece60b49d6b062845fd3), [`aa2cec8`](https://github.com/mastra-ai/mastra/commit/aa2cec8501f634d51c2f3ebfb3dd3aa7af8d2ca2), [`c848e65`](https://github.com/mastra-ai/mastra/commit/c848e655a64ff10331a8ceafafe7f18e70a0f092), [`2adf8eb`](https://github.com/mastra-ai/mastra/commit/2adf8eb4a70ed2b6cff2dd39281496ea0e025fac), [`0494489`](https://github.com/mastra-ai/mastra/commit/049448906e4c3d2d615bbe865b073a0d890ddb7c), [`8d1aeb8`](https://github.com/mastra-ai/mastra/commit/8d1aeb8acf7c20c4bb8e4d8e4bdc6569c83ac561), [`8264611`](https://github.com/mastra-ai/mastra/commit/8264611510e421b818bc7395dc2ae4d9c2d518b2), [`d8fa243`](https://github.com/mastra-ai/mastra/commit/d8fa2430d21113e330c4e676ac65e1235cf44f81), [`44fc98b`](https://github.com/mastra-ai/mastra/commit/44fc98b9d1242aa87a3ab44bdce9e9f12c44d8c9), [`f933ba3`](https://github.com/mastra-ai/mastra/commit/f933ba32700e1d0bf143311c1a08f88300b840b6), [`83065bf`](https://github.com/mastra-ai/mastra/commit/83065bfee9e47c3c6f09132a9034501f6cfb69cf), [`0f2ef41`](https://github.com/mastra-ai/mastra/commit/0f2ef4118da022e4f30dac4e9856cc3a8c97671c), [`01b162f`](https://github.com/mastra-ai/mastra/commit/01b162fe435295881aa7ea55f1759407ad5175ad)]:
24
+ - @mastra/core@1.57.0
25
+ - @mastra/observability@1.16.5
26
+
27
+ ## 1.3.0-alpha.0
28
+
29
+ ### Minor Changes
30
+
31
+ - Added `$ai_tools` to exported `$ai_generation` events. When tool definitions are present on the generation span (requires @mastra/core with tool-definition capture), they are sent to PostHog in the OpenAI function format, so PostHog LLM Analytics shows the tool schemas each generation ran with. Related: #20242 ([#20243](https://github.com/mastra-ai/mastra/pull/20243))
32
+
33
+ ```typescript
34
+ // No configuration change needed:
35
+ new PosthogExporter({ apiKey: process.env.POSTHOG_KEY, host: 'https://us.i.posthog.com' });
36
+ // $ai_generation events now include:
37
+ // $ai_tools: [
38
+ // {
39
+ // type: 'function',
40
+ // function: { name: 'get_weather', description: 'Get the weather for a city', parameters: { ... } },
41
+ // },
42
+ // ]
43
+ ```
44
+
45
+ ### Patch Changes
46
+
47
+ - Updated dependencies [[`89200ba`](https://github.com/mastra-ai/mastra/commit/89200bafa05444bb7949b363ce7b743e29867561), [`c950138`](https://github.com/mastra-ai/mastra/commit/c950138e72e4f317a40187e3800588731ab790ce), [`063c8b2`](https://github.com/mastra-ai/mastra/commit/063c8b2eb14e4e5ca021779bc33e8c3c031c8604), [`f4e964c`](https://github.com/mastra-ai/mastra/commit/f4e964cad57057301d6bed5c55bcdd730175b941), [`1f7bbd7`](https://github.com/mastra-ai/mastra/commit/1f7bbd7785a8d230aad02454ecabeb4a0b2cc96f), [`e47ff36`](https://github.com/mastra-ai/mastra/commit/e47ff36945720f4ee4caa09f6e83514d7d188608), [`fb9a6ac`](https://github.com/mastra-ai/mastra/commit/fb9a6ac11c9560518742ece60b49d6b062845fd3), [`aa2cec8`](https://github.com/mastra-ai/mastra/commit/aa2cec8501f634d51c2f3ebfb3dd3aa7af8d2ca2), [`2adf8eb`](https://github.com/mastra-ai/mastra/commit/2adf8eb4a70ed2b6cff2dd39281496ea0e025fac), [`8264611`](https://github.com/mastra-ai/mastra/commit/8264611510e421b818bc7395dc2ae4d9c2d518b2), [`44fc98b`](https://github.com/mastra-ai/mastra/commit/44fc98b9d1242aa87a3ab44bdce9e9f12c44d8c9), [`0f2ef41`](https://github.com/mastra-ai/mastra/commit/0f2ef4118da022e4f30dac4e9856cc3a8c97671c)]:
48
+ - @mastra/core@1.57.0-alpha.1
49
+
3
50
  ## 1.2.2
4
51
 
5
52
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -299,6 +299,14 @@ var PosthogExporter = class PosthogExporter extends _mastra_observability.Tracki
299
299
  if (attrs.parameters.maxOutputTokens !== void 0) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;
300
300
  }
301
301
  if (attrs.streaming !== void 0) props.$ai_stream = attrs.streaming;
302
+ if (attrs.tools?.length) props.$ai_tools = attrs.tools.map((tool) => tool.type === "function" ? {
303
+ type: "function",
304
+ function: {
305
+ name: tool.name,
306
+ description: tool.description,
307
+ parameters: tool.parameters
308
+ }
309
+ } : tool);
302
310
  return {
303
311
  ...props,
304
312
  ...this.extractErrorProperties(span.errorInfo),
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["TrackingExporter","#client","PostHog","SpanType"],"sources":["../src/tracing.ts"],"sourcesContent":["import type {\n AnyExportedSpan,\n ExportedFeedback,\n FeedbackEvent,\n ModelGenerationAttributes,\n SpanErrorInfo,\n UsageStats,\n} from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { TrackingExporter } from '@mastra/observability';\nimport { PostHog } from 'posthog-node';\nimport type { EventMessage } from 'posthog-node';\n\n/**\n * Token usage format compatible with PostHog.\n * @see https://posthog.com/docs/llm-analytics/generations#event-properties\n */\nexport interface PostHogUsageMetrics {\n $ai_input_tokens?: number;\n $ai_output_tokens?: number;\n $ai_cache_read_input_tokens?: number;\n $ai_cache_creation_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to PostHog's expected property format.\n *\n * Pass through gross input token counts with cache fields as subsets.\n * PostHog subtracts cache tokens when computing costs for non-Anthropic\n * providers and detects Anthropic-style exclusive reporting on its own.\n *\n * @param usage - The UsageStats from span attributes\n * @returns PostHog-formatted usage properties\n */\nexport function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics {\n if (!usage) return {};\n\n const props: PostHogUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) {\n props.$ai_input_tokens = usage.inputTokens;\n }\n\n if (usage.inputDetails?.cacheRead !== undefined) {\n props.$ai_cache_read_input_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage.inputDetails?.cacheWrite !== undefined) {\n props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;\n }\n\n if (usage.outputTokens !== undefined) {\n props.$ai_output_tokens = usage.outputTokens;\n }\n\n return props;\n}\n\ninterface PostHogMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: PostHogContent[];\n}\n\ninterface PostHogContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface MastraMessage {\n role: string;\n content: string | MastraContent[];\n}\n\ninterface MastraContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ntype SpanData = string | MastraMessage[] | Record<string, unknown> | unknown;\n\nconst DISTINCT_ID = 'distinctId';\n\nexport interface PosthogExporterConfig extends TrackingExporterConfig {\n /** PostHog API key. Defaults to POSTHOG_API_KEY environment variable. */\n apiKey?: string;\n /** PostHog host URL. Defaults to POSTHOG_HOST environment variable or US region. */\n host?: string;\n flushAt?: number;\n flushInterval?: number;\n serverless?: boolean;\n defaultDistinctId?: string;\n enablePrivacyMode?: boolean;\n}\n\ntype PosthogRoot = unknown;\ntype PosthogSpan = AnyExportedSpan;\n// used as a placeholder for event data since we don't need to cache\n// event data for Posthog\ntype PosthogEvent = boolean;\ntype PosthogMetadata = unknown;\ntype PosthogTraceData = TraceData<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata>;\n\nexport class PosthogExporter extends TrackingExporter<\n PosthogRoot,\n PosthogSpan,\n PosthogEvent,\n PosthogMetadata,\n PosthogExporterConfig\n> {\n name = 'posthog';\n #client: PostHog | undefined;\n\n private static readonly SERVERLESS_FLUSH_AT = 10;\n private static readonly SERVERLESS_FLUSH_INTERVAL = 2000;\n private static readonly DEFAULT_FLUSH_AT = 20;\n private static readonly DEFAULT_FLUSH_INTERVAL = 10000;\n\n constructor(config: PosthogExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const apiKey = config.apiKey ?? process.env.POSTHOG_API_KEY;\n\n super({ ...config, apiKey });\n\n if (!apiKey) {\n this.setDisabled('Missing required API key. Set POSTHOG_API_KEY environment variable or pass apiKey in config.');\n return;\n }\n\n const clientConfig = this.buildClientConfig(this.config);\n this.#client = new PostHog(apiKey, clientConfig);\n const message =\n (config.serverless ?? false) ? 'PostHog exporter initialized in serverless mode' : 'PostHog exporter initialized';\n this.logger.debug(message, config);\n }\n\n private buildClientConfig(config: PosthogExporterConfig) {\n const isServerless = config.serverless ?? false;\n const flushAt =\n config.flushAt ?? (isServerless ? PosthogExporter.SERVERLESS_FLUSH_AT : PosthogExporter.DEFAULT_FLUSH_AT);\n const flushInterval =\n config.flushInterval ??\n (isServerless ? PosthogExporter.SERVERLESS_FLUSH_INTERVAL : PosthogExporter.DEFAULT_FLUSH_INTERVAL);\n\n const host = config.host || process.env.POSTHOG_HOST || 'https://us.i.posthog.com';\n\n if (!config.host && !process.env.POSTHOG_HOST) {\n this.logger.info(\n 'No PostHog host specified, using US default (https://us.i.posthog.com). ' +\n 'For EU region, set `host: \"https://eu.i.posthog.com\"` in config or POSTHOG_HOST env var. ' +\n 'For self-hosted, provide your instance URL.',\n );\n }\n\n return {\n host,\n flushAt,\n flushInterval,\n privacyMode: config.enablePrivacyMode,\n };\n }\n\n protected override skipBuildRootTask = true;\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogRoot | undefined> {\n throw new Error('Method not implemented.');\n }\n\n protected override skipCachingEventSpans = true;\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogEvent> {\n const { span, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const distinctId = this.getDistinctId(span, traceData);\n const properties = this.buildEventProperties(span, 0);\n\n this.#client?.capture(\n this.withGroups({\n distinctId,\n event: eventName,\n properties,\n timestamp: span.endTime ? new Date(span.endTime) : new Date(),\n }),\n );\n\n return true;\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogSpan | undefined> {\n const { span, traceData } = args;\n if (!traceData.hasExtraValue(DISTINCT_ID)) {\n const userId = span.metadata?.userId;\n if (userId) {\n traceData.setExtraValue(DISTINCT_ID, String(userId));\n }\n }\n\n return span;\n }\n\n protected override skipSpanUpdateEvents = true;\n protected override _updateSpan(_args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n throw new Error('Method not implemented.');\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n // Merge input from cached span (SPAN_STARTED) if not present on end span\n // This handles the case where input is only sent at start\n const cachedSpan = traceData.getSpan({ spanId: span.id });\n const mergedSpan = !span.input && cachedSpan?.input ? { ...span, input: cachedSpan.input } : span;\n\n const eventMessage = this.buildEventMessage({ span: mergedSpan, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n protected override async _abortSpan(args: {\n span: PosthogSpan;\n reason: SpanErrorInfo;\n traceData: PosthogTraceData;\n }): Promise<void> {\n const { span, reason, traceData } = args;\n\n // update span with the abort reason\n span.errorInfo = reason;\n\n const eventMessage = this.buildEventMessage({ span, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n /**\n * Forward feedback recorded via `addFeedback()` to PostHog as a native\n * `$ai_feedback` event, shown as \"User feedback\" on the linked trace in\n * PostHog's AI observability UI.\n */\n async onFeedbackEvent(event: FeedbackEvent): Promise<void> {\n if (!this.#client) return;\n\n const { feedback } = event;\n if (!feedback.traceId) {\n this.logger.debug('PostHog exporter: dropping feedback with no traceId; PostHog requires $ai_trace_id', {\n feedbackId: feedback.feedbackId,\n });\n return;\n }\n\n const properties: Record<string, any> = {\n // Custom metadata goes first so it cannot overwrite the natively mapped fields below\n ...this.extractCustomMetadata(feedback.metadata),\n $ai_trace_id: feedback.traceId,\n // PostHog's trace UI only displays feedback events that carry $ai_feedback_text\n $ai_feedback_text: feedback.comment ?? String(feedback.value),\n feedback_id: feedback.feedbackId,\n feedback_type: feedback.feedbackType,\n feedback_value: feedback.value,\n };\n\n const feedbackSource = feedback.feedbackSource ?? feedback.source;\n if (feedbackSource) properties.feedback_source = feedbackSource;\n if (feedback.spanId) properties.span_id = feedback.spanId;\n if (feedback.sourceId) properties.source_id = feedback.sourceId;\n if (feedback.metadata?.sessionId) properties.$ai_session_id = feedback.metadata.sessionId;\n\n try {\n this.#client.capture(\n this.withGroups({\n distinctId: this.getFeedbackDistinctId(feedback),\n event: '$ai_feedback',\n properties,\n timestamp: new Date(feedback.timestamp),\n }),\n );\n } catch (err) {\n this.logger.error('PostHog exporter: failed to submit feedback', {\n error: err,\n traceId: feedback.traceId,\n feedbackId: feedback.feedbackId,\n });\n }\n }\n\n private getFeedbackDistinctId(feedback: ExportedFeedback): string {\n const userId = feedback.feedbackUserId ?? feedback.userId ?? feedback.metadata?.userId;\n if (userId) {\n return String(userId);\n }\n return this.config.defaultDistinctId ?? 'anonymous';\n }\n\n /**\n * PostHog group analytics are keyed off the top-level `groups` field on the\n * capture call. The Node SDK derives the event's `$groups` from that field and\n * overwrites any property-level `$groups`, so group metadata carried in\n * properties is dropped unless it is mirrored here.\n */\n private withGroups(message: EventMessage): EventMessage {\n const groups = message.properties?.$groups;\n if (groups && typeof groups === 'object' && !Array.isArray(groups)) {\n return { ...message, groups };\n }\n return message;\n }\n\n private buildEventMessage(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): EventMessage {\n const { span, traceData } = args;\n\n const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();\n\n const distinctId = this.getDistinctId(span, traceData);\n\n if (span.isRootSpan) {\n return this.buildRootEventMessage({ span, distinctId, endTime });\n } else {\n return this.buildChildEventMessage({ span, distinctId, endTime, traceData });\n }\n }\n\n /**\n * Capture an explicit $ai_trace event for root spans.\n * This gives us control over trace-level metadata like name and tags,\n * rather than relying on PostHog's pseudo-trace auto-creation.\n */\n private buildRootEventMessage(args: { span: AnyExportedSpan; distinctId: string; endTime: number }): EventMessage {\n const { span, distinctId, endTime } = args;\n\n // Note: We don't set $ai_latency on $ai_trace events because PostHog\n // aggregates latency from child events. Setting it here causes double-counting.\n const traceProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_span_name: span.name,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.metadata?.sessionId) {\n traceProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n if (span.input) {\n traceProperties.$ai_input_state = span.input;\n }\n\n if (span.output) {\n traceProperties.$ai_output_state = span.output;\n }\n\n if (span.errorInfo) {\n traceProperties.$ai_error = {\n message: span.errorInfo.message,\n ...(span.errorInfo.id && { id: span.errorInfo.id }),\n ...(span.errorInfo.category && { category: span.errorInfo.category }),\n };\n }\n\n // Add tags as custom properties (PostHog doesn't have native tag support on traces)\n if (span.tags?.length) {\n for (const tag of span.tags) {\n traceProperties[tag] = true;\n }\n }\n\n // Add custom metadata (excluding userId and sessionId which are handled separately)\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n Object.assign(traceProperties, customMetadata);\n\n return {\n distinctId,\n event: '$ai_trace',\n properties: traceProperties,\n timestamp: new Date(endTime),\n };\n }\n\n private buildChildEventMessage(args: {\n span: AnyExportedSpan;\n distinctId: string;\n endTime: number;\n traceData: PosthogTraceData;\n }): EventMessage {\n const { span, distinctId, endTime, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const startTime = span.startTime.getTime();\n const latency = (endTime - startTime) / 1000;\n\n // Check if parent is the root span - if so, use traceId as parent_id\n // since we don't create an $ai_span for root spans\n const parentIsRootSpan = this.isParentRootSpan(span, traceData);\n const properties = this.buildEventProperties(span, latency, parentIsRootSpan);\n\n return {\n distinctId,\n event: eventName,\n properties,\n timestamp: new Date(endTime),\n };\n }\n\n private toDate(timestamp: Date | number): Date {\n return timestamp instanceof Date ? timestamp : new Date(timestamp);\n }\n\n private mapToPostHogEvent(spanType: SpanType): string {\n if (spanType == SpanType.MODEL_GENERATION) {\n return '$ai_generation';\n }\n return '$ai_span';\n }\n\n private getDistinctId(span: AnyExportedSpan, traceData?: PosthogTraceData): string {\n if (span.metadata?.userId) {\n return String(span.metadata.userId);\n }\n\n if (traceData?.hasExtraValue(DISTINCT_ID)) {\n return String(traceData.getExtraValue(DISTINCT_ID));\n }\n\n if (this.config.defaultDistinctId) {\n return this.config.defaultDistinctId;\n }\n\n return 'anonymous';\n }\n\n /**\n * Check if the parent of this span is the root span.\n * We need this because we don't create $ai_span for root spans,\n * so children of root spans should use $ai_trace_id as their $ai_parent_id.\n */\n private isParentRootSpan(span: AnyExportedSpan, traceData: PosthogTraceData): boolean {\n if (!span.parentSpanId) {\n return false;\n }\n\n // Look up the parent span in our cache to check if it's a root span\n const parentCache = traceData.getSpan({ spanId: span.parentSpanId });\n if (parentCache) {\n return parentCache.isRootSpan;\n }\n\n // Parent not found in cache - shouldn't happen normally, but default to false\n return false;\n }\n\n private buildEventProperties(\n span: AnyExportedSpan,\n latency: number,\n parentIsRootSpan: boolean = false,\n ): Record<string, any> {\n const baseProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_latency: latency,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.parentSpanId) {\n // If parent is the root span, use trace_id as parent_id since we don't\n // create an $ai_span for root spans (only $ai_trace)\n baseProperties.$ai_parent_id = parentIsRootSpan ? span.traceId : span.parentSpanId;\n }\n\n if (span.metadata?.sessionId) {\n baseProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n // Include tags for root spans (tags are only set on root spans by design)\n // PostHog doesn't allow setting tags directly, so we iterate through each tag\n // and set it as a property with value true\n if (span.isRootSpan && span.tags?.length) {\n for (const tag of span.tags) {\n baseProperties[tag] = true;\n }\n }\n\n if (span.type === SpanType.MODEL_GENERATION) {\n baseProperties.$ai_generation_id = span.id;\n return { ...baseProperties, ...this.buildGenerationProperties(span) };\n } else {\n baseProperties.$ai_span_id = span.id;\n baseProperties.$ai_span_name = span.name;\n return { ...baseProperties, ...this.buildSpanProperties(span) };\n }\n }\n\n private extractErrorProperties(errorInfo?: SpanErrorInfo): Record<string, any> {\n if (!errorInfo) {\n return {};\n }\n\n const props: Record<string, string> = {\n error_message: errorInfo.message,\n };\n\n if (errorInfo.id) {\n props.error_id = errorInfo.id;\n }\n\n if (errorInfo.category) {\n props.error_category = errorInfo.category;\n }\n\n return props;\n }\n\n private extractCustomMetadata(metadata?: Record<string, unknown>): Record<string, any> {\n const { userId, sessionId, ...customMetadata } = metadata ?? {};\n return customMetadata;\n }\n\n private buildGenerationProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n const attrs = (span.attributes ?? {}) as ModelGenerationAttributes;\n\n props.$ai_model = attrs.model || 'unknown-model';\n props.$ai_provider = attrs.provider || 'unknown-provider';\n\n if (span.input) props.$ai_input = this.formatMessages(span.input, 'user');\n if (span.output) props.$ai_output_choices = this.formatMessages(span.output, 'assistant');\n\n // Extract usage properties using the shared utility\n Object.assign(props, formatUsageMetrics(attrs.usage));\n\n if (attrs.parameters) {\n if (attrs.parameters.temperature !== undefined) props.$ai_temperature = attrs.parameters.temperature;\n if (attrs.parameters.maxOutputTokens !== undefined) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;\n }\n if (attrs.streaming !== undefined) props.$ai_stream = attrs.streaming;\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private buildSpanProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n\n if (span.input) props.$ai_input_state = span.input;\n if (span.output) props.$ai_output_state = span.output;\n\n if (span.type === SpanType.MODEL_CHUNK) {\n const attrs = span.attributes as any;\n if (attrs?.chunkType) props.chunk_type = attrs.chunkType;\n if (attrs?.sequenceNumber !== undefined) props.chunk_sequence_number = attrs.sequenceNumber;\n }\n\n if (span.attributes) {\n Object.assign(props, span.attributes);\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private formatMessages(data: SpanData, defaultRole: 'user' | 'assistant' = 'user'): PostHogMessage[] {\n // Unwrap {messages: [...]} wrapper produced by generation span inputs\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'messages' in data) {\n const wrapped = (data as Record<string, unknown>).messages;\n if (this.isMessageArray(wrapped)) {\n return wrapped.map(msg => this.normalizeMessage(msg));\n }\n }\n\n if (this.isMessageArray(data)) {\n return data.map(msg => this.normalizeMessage(msg));\n }\n\n if (typeof data === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text: data }] }];\n }\n\n if (this.isSpanOutputWithToolCalls(data)) {\n const content: PostHogContent[] = [];\n if (data.text) {\n content.push({ type: 'text', text: data.text });\n }\n for (const tc of data.toolCalls) {\n content.push({\n type: 'tool-call',\n id: tc.toolCallId,\n function: { name: tc.toolName, arguments: tc.args },\n });\n }\n return [{ role: 'assistant', content }];\n }\n\n // Extract text from output objects (e.g. generation outputs with text but no tool calls)\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'text' in data) {\n const text = (data as Record<string, unknown>).text;\n if (typeof text === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text }] }];\n }\n }\n\n return [{ role: defaultRole, content: [{ type: 'text', text: this.safeStringify(data) }] }];\n }\n\n private isSpanOutputWithToolCalls(\n data: unknown,\n ): data is { text?: string; toolCalls: Array<{ toolCallId: string; toolName: string; args: unknown }> } {\n if (typeof data !== 'object' || data === null || !('toolCalls' in data)) return false;\n const { toolCalls } = data as Record<string, unknown>;\n return Array.isArray(toolCalls) && toolCalls.length > 0;\n }\n\n private isMessageArray(data: unknown): data is MastraMessage[] {\n if (!Array.isArray(data)) {\n return false;\n }\n\n return data.every(item => typeof item === 'object' && item !== null && 'role' in item && 'content' in item);\n }\n\n private normalizeMessage(msg: MastraMessage): PostHogMessage {\n if (typeof msg.content === 'string') {\n return {\n role: msg.role as PostHogMessage['role'],\n content: [{ type: 'text', text: msg.content }],\n };\n }\n\n return {\n role: msg.role as PostHogMessage['role'],\n content: msg.content as PostHogContent[],\n };\n }\n\n private safeStringify(data: unknown): string {\n try {\n return JSON.stringify(data);\n } catch {\n if (typeof data === 'object' && data !== null) {\n return `[Non-serializable ${data.constructor?.name || 'Object'}]`;\n }\n return String(data);\n }\n }\n\n /**\n * Force flush any buffered data to PostHog without shutting down.\n */\n protected override async _flush(): Promise<void> {\n if (this.#client) {\n await this.#client.flush();\n }\n }\n\n override async _postShutdown(): Promise<void> {\n if (this.#client) {\n await this.#client.shutdown();\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,OAAyC;CAC1E,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,QAA6B,CAAC;CAEpC,IAAI,MAAM,gBAAgB,KAAA,GACxB,MAAM,mBAAmB,MAAM;CAGjC,IAAI,MAAM,cAAc,cAAc,KAAA,GACpC,MAAM,8BAA8B,MAAM,aAAa;CAGzD,IAAI,MAAM,cAAc,eAAe,KAAA,GACrC,MAAM,kCAAkC,MAAM,aAAa;CAG7D,IAAI,MAAM,iBAAiB,KAAA,GACzB,MAAM,oBAAoB,MAAM;CAGlC,OAAO;AACT;AA0BA,MAAM,cAAc;AAsBpB,IAAa,kBAAb,MAAa,wBAAwBA,sBAAAA,iBAMnC;CACA,OAAO;CACP;CAEA,OAAwB,sBAAsB;CAC9C,OAAwB,4BAA4B;CACpD,OAAwB,mBAAmB;CAC3C,OAAwB,yBAAyB;CAEjD,YAAY,SAAgC,CAAC,GAAG;EAE9C,MAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;EAE5C,MAAM;GAAE,GAAG;GAAQ;EAAO,CAAC;EAE3B,IAAI,CAAC,QAAQ;GACX,KAAK,YAAY,8FAA8F;GAC/G;EACF;EAEA,MAAM,eAAe,KAAK,kBAAkB,KAAK,MAAM;EACvD,KAAKC,UAAU,IAAIC,aAAAA,QAAQ,QAAQ,YAAY;EAC/C,MAAM,UACH,OAAO,cAAc,QAAS,oDAAoD;EACrF,KAAK,OAAO,MAAM,SAAS,MAAM;CACnC;CAEA,kBAA0B,QAA+B;EACvD,MAAM,eAAe,OAAO,cAAc;EAC1C,MAAM,UACJ,OAAO,YAAY,eAAe,gBAAgB,sBAAsB,gBAAgB;EAC1F,MAAM,gBACJ,OAAO,kBACN,eAAe,gBAAgB,4BAA4B,gBAAgB;EAE9E,MAAM,OAAO,OAAO,QAAQ,QAAQ,IAAI,gBAAgB;EAExD,IAAI,CAAC,OAAO,QAAQ,CAAC,QAAQ,IAAI,cAC/B,KAAK,OAAO,KACV,gNAGF;EAGF,OAAO;GACL;GACA;GACA;GACA,aAAa,OAAO;EACtB;CACF;CAEA,oBAAuC;CACvC,MAAyB,WAAW,OAGC;EACnC,MAAM,IAAI,MAAM,yBAAyB;CAC3C;CAEA,wBAA2C;CAC3C,MAAyB,YAAY,MAGX;EACxB,MAAM,EAAE,MAAM,cAAc;EAE5B,MAAM,YAAY,KAAK,kBAAkB,KAAK,IAAI;EAClD,MAAM,aAAa,KAAK,cAAc,MAAM,SAAS;EACrD,MAAM,aAAa,KAAK,qBAAqB,MAAM,CAAC;EAEpD,KAAKD,SAAS,QACZ,KAAK,WAAW;GACd;GACA,OAAO;GACP;GACA,WAAW,KAAK,UAAU,IAAI,KAAK,KAAK,OAAO,oBAAI,IAAI,KAAK;EAC9D,CAAC,CACH;EAEA,OAAO;CACT;CAEA,MAAyB,WAAW,MAGC;EACnC,MAAM,EAAE,MAAM,cAAc;EAC5B,IAAI,CAAC,UAAU,cAAc,WAAW,GAAG;GACzC,MAAM,SAAS,KAAK,UAAU;GAC9B,IAAI,QACF,UAAU,cAAc,aAAa,OAAO,MAAM,CAAC;EAEvD;EAEA,OAAO;CACT;CAEA,uBAA0C;CAC1C,YAA+B,OAA8E;EAC3G,MAAM,IAAI,MAAM,yBAAyB;CAC3C;CAEA,MAAyB,YAAY,MAA6E;EAChH,MAAM,EAAE,MAAM,cAAc;EAI5B,MAAM,aAAa,UAAU,QAAQ,EAAE,QAAQ,KAAK,GAAG,CAAC;EACxD,MAAM,aAAa,CAAC,KAAK,SAAS,YAAY,QAAQ;GAAE,GAAG;GAAM,OAAO,WAAW;EAAM,IAAI;EAE7F,MAAM,eAAe,KAAK,kBAAkB;GAAE,MAAM;GAAY;EAAU,CAAC;EAC3E,KAAKA,SAAS,QAAQ,KAAK,WAAW,YAAY,CAAC;CACrD;CAEA,MAAyB,WAAW,MAIlB;EAChB,MAAM,EAAE,MAAM,QAAQ,cAAc;EAGpC,KAAK,YAAY;EAEjB,MAAM,eAAe,KAAK,kBAAkB;GAAE;GAAM;EAAU,CAAC;EAC/D,KAAKA,SAAS,QAAQ,KAAK,WAAW,YAAY,CAAC;CACrD;;;;;;CAOA,MAAM,gBAAgB,OAAqC;EACzD,IAAI,CAAC,KAAKA,SAAS;EAEnB,MAAM,EAAE,aAAa;EACrB,IAAI,CAAC,SAAS,SAAS;GACrB,KAAK,OAAO,MAAM,sFAAsF,EACtG,YAAY,SAAS,WACvB,CAAC;GACD;EACF;EAEA,MAAM,aAAkC;GAEtC,GAAG,KAAK,sBAAsB,SAAS,QAAQ;GAC/C,cAAc,SAAS;GAEvB,mBAAmB,SAAS,WAAW,OAAO,SAAS,KAAK;GAC5D,aAAa,SAAS;GACtB,eAAe,SAAS;GACxB,gBAAgB,SAAS;EAC3B;EAEA,MAAM,iBAAiB,SAAS,kBAAkB,SAAS;EAC3D,IAAI,gBAAgB,WAAW,kBAAkB;EACjD,IAAI,SAAS,QAAQ,WAAW,UAAU,SAAS;EACnD,IAAI,SAAS,UAAU,WAAW,YAAY,SAAS;EACvD,IAAI,SAAS,UAAU,WAAW,WAAW,iBAAiB,SAAS,SAAS;EAEhF,IAAI;GACF,KAAKA,QAAQ,QACX,KAAK,WAAW;IACd,YAAY,KAAK,sBAAsB,QAAQ;IAC/C,OAAO;IACP;IACA,WAAW,IAAI,KAAK,SAAS,SAAS;GACxC,CAAC,CACH;EACF,SAAS,KAAK;GACZ,KAAK,OAAO,MAAM,+CAA+C;IAC/D,OAAO;IACP,SAAS,SAAS;IAClB,YAAY,SAAS;GACvB,CAAC;EACH;CACF;CAEA,sBAA8B,UAAoC;EAChE,MAAM,SAAS,SAAS,kBAAkB,SAAS,UAAU,SAAS,UAAU;EAChF,IAAI,QACF,OAAO,OAAO,MAAM;EAEtB,OAAO,KAAK,OAAO,qBAAqB;CAC1C;;;;;;;CAQA,WAAmB,SAAqC;EACtD,MAAM,SAAS,QAAQ,YAAY;EACnC,IAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAC/D,OAAO;GAAE,GAAG;GAAS;EAAO;EAE9B,OAAO;CACT;CAEA,kBAA0B,MAA4E;EACpG,MAAM,EAAE,MAAM,cAAc;EAE5B,MAAM,UAAU,KAAK,UAAU,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI;EAE9E,MAAM,aAAa,KAAK,cAAc,MAAM,SAAS;EAErD,IAAI,KAAK,YACP,OAAO,KAAK,sBAAsB;GAAE;GAAM;GAAY;EAAQ,CAAC;OAE/D,OAAO,KAAK,uBAAuB;GAAE;GAAM;GAAY;GAAS;EAAU,CAAC;CAE/E;;;;;;CAOA,sBAA8B,MAAoF;EAChH,MAAM,EAAE,MAAM,YAAY,YAAY;EAItC,MAAM,kBAAuC;GAC3C,cAAc,KAAK;GACnB,eAAe,KAAK;GACpB,cAAc,CAAC,CAAC,KAAK;EACvB;EAEA,IAAI,KAAK,UAAU,WACjB,gBAAgB,iBAAiB,KAAK,SAAS;EAGjD,IAAI,KAAK,OACP,gBAAgB,kBAAkB,KAAK;EAGzC,IAAI,KAAK,QACP,gBAAgB,mBAAmB,KAAK;EAG1C,IAAI,KAAK,WACP,gBAAgB,YAAY;GAC1B,SAAS,KAAK,UAAU;GACxB,GAAI,KAAK,UAAU,MAAM,EAAE,IAAI,KAAK,UAAU,GAAG;GACjD,GAAI,KAAK,UAAU,YAAY,EAAE,UAAU,KAAK,UAAU,SAAS;EACrE;EAIF,IAAI,KAAK,MAAM,QACb,KAAK,MAAM,OAAO,KAAK,MACrB,gBAAgB,OAAO;EAK3B,MAAM,EAAE,QAAQ,WAAW,GAAG,mBAAmB,KAAK,YAAY,CAAC;EACnE,OAAO,OAAO,iBAAiB,cAAc;EAE7C,OAAO;GACL;GACA,OAAO;GACP,YAAY;GACZ,WAAW,IAAI,KAAK,OAAO;EAC7B;CACF;CAEA,uBAA+B,MAKd;EACf,MAAM,EAAE,MAAM,YAAY,SAAS,cAAc;EAEjD,MAAM,YAAY,KAAK,kBAAkB,KAAK,IAAI;EAElD,MAAM,WAAW,UADC,KAAK,UAAU,QACE,KAAK;EAIxC,MAAM,mBAAmB,KAAK,iBAAiB,MAAM,SAAS;EAG9D,OAAO;GACL;GACA,OAAO;GACP,YALiB,KAAK,qBAAqB,MAAM,SAAS,gBAKjD;GACT,WAAW,IAAI,KAAK,OAAO;EAC7B;CACF;CAEA,OAAe,WAAgC;EAC7C,OAAO,qBAAqB,OAAO,YAAY,IAAI,KAAK,SAAS;CACnE;CAEA,kBAA0B,UAA4B;EACpD,IAAI,YAAYE,2BAAAA,SAAS,kBACvB,OAAO;EAET,OAAO;CACT;CAEA,cAAsB,MAAuB,WAAsC;EACjF,IAAI,KAAK,UAAU,QACjB,OAAO,OAAO,KAAK,SAAS,MAAM;EAGpC,IAAI,WAAW,cAAc,WAAW,GACtC,OAAO,OAAO,UAAU,cAAc,WAAW,CAAC;EAGpD,IAAI,KAAK,OAAO,mBACd,OAAO,KAAK,OAAO;EAGrB,OAAO;CACT;;;;;;CAOA,iBAAyB,MAAuB,WAAsC;EACpF,IAAI,CAAC,KAAK,cACR,OAAO;EAIT,MAAM,cAAc,UAAU,QAAQ,EAAE,QAAQ,KAAK,aAAa,CAAC;EACnE,IAAI,aACF,OAAO,YAAY;EAIrB,OAAO;CACT;CAEA,qBACE,MACA,SACA,mBAA4B,OACP;EACrB,MAAM,iBAAsC;GAC1C,cAAc,KAAK;GACnB,aAAa;GACb,cAAc,CAAC,CAAC,KAAK;EACvB;EAEA,IAAI,KAAK,cAGP,eAAe,gBAAgB,mBAAmB,KAAK,UAAU,KAAK;EAGxE,IAAI,KAAK,UAAU,WACjB,eAAe,iBAAiB,KAAK,SAAS;EAMhD,IAAI,KAAK,cAAc,KAAK,MAAM,QAChC,KAAK,MAAM,OAAO,KAAK,MACrB,eAAe,OAAO;EAI1B,IAAI,KAAK,SAASA,2BAAAA,SAAS,kBAAkB;GAC3C,eAAe,oBAAoB,KAAK;GACxC,OAAO;IAAE,GAAG;IAAgB,GAAG,KAAK,0BAA0B,IAAI;GAAE;EACtE,OAAO;GACL,eAAe,cAAc,KAAK;GAClC,eAAe,gBAAgB,KAAK;GACpC,OAAO;IAAE,GAAG;IAAgB,GAAG,KAAK,oBAAoB,IAAI;GAAE;EAChE;CACF;CAEA,uBAA+B,WAAgD;EAC7E,IAAI,CAAC,WACH,OAAO,CAAC;EAGV,MAAM,QAAgC,EACpC,eAAe,UAAU,QAC3B;EAEA,IAAI,UAAU,IACZ,MAAM,WAAW,UAAU;EAG7B,IAAI,UAAU,UACZ,MAAM,iBAAiB,UAAU;EAGnC,OAAO;CACT;CAEA,sBAA8B,UAAyD;EACrF,MAAM,EAAE,QAAQ,WAAW,GAAG,mBAAmB,YAAY,CAAC;EAC9D,OAAO;CACT;CAEA,0BAAkC,MAA4C;EAC5E,MAAM,QAA6B,CAAC;EACpC,MAAM,QAAS,KAAK,cAAc,CAAC;EAEnC,MAAM,YAAY,MAAM,SAAS;EACjC,MAAM,eAAe,MAAM,YAAY;EAEvC,IAAI,KAAK,OAAO,MAAM,YAAY,KAAK,eAAe,KAAK,OAAO,MAAM;EACxE,IAAI,KAAK,QAAQ,MAAM,qBAAqB,KAAK,eAAe,KAAK,QAAQ,WAAW;EAGxF,OAAO,OAAO,OAAO,mBAAmB,MAAM,KAAK,CAAC;EAEpD,IAAI,MAAM,YAAY;GACpB,IAAI,MAAM,WAAW,gBAAgB,KAAA,GAAW,MAAM,kBAAkB,MAAM,WAAW;GACzF,IAAI,MAAM,WAAW,oBAAoB,KAAA,GAAW,MAAM,iBAAiB,MAAM,WAAW;EAC9F;EACA,IAAI,MAAM,cAAc,KAAA,GAAW,MAAM,aAAa,MAAM;EAE5D,OAAO;GAAE,GAAG;GAAO,GAAG,KAAK,uBAAuB,KAAK,SAAS;GAAG,GAAG,KAAK,sBAAsB,KAAK,QAAQ;EAAE;CAClH;CAEA,oBAA4B,MAA4C;EACtE,MAAM,QAA6B,CAAC;EAEpC,IAAI,KAAK,OAAO,MAAM,kBAAkB,KAAK;EAC7C,IAAI,KAAK,QAAQ,MAAM,mBAAmB,KAAK;EAE/C,IAAI,KAAK,SAASA,2BAAAA,SAAS,aAAa;GACtC,MAAM,QAAQ,KAAK;GACnB,IAAI,OAAO,WAAW,MAAM,aAAa,MAAM;GAC/C,IAAI,OAAO,mBAAmB,KAAA,GAAW,MAAM,wBAAwB,MAAM;EAC/E;EAEA,IAAI,KAAK,YACP,OAAO,OAAO,OAAO,KAAK,UAAU;EAGtC,OAAO;GAAE,GAAG;GAAO,GAAG,KAAK,uBAAuB,KAAK,SAAS;GAAG,GAAG,KAAK,sBAAsB,KAAK,QAAQ;EAAE;CAClH;CAEA,eAAuB,MAAgB,cAAoC,QAA0B;EAEnG,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,KAAK,cAAc,MAAM;GAC3F,MAAM,UAAW,KAAiC;GAClD,IAAI,KAAK,eAAe,OAAO,GAC7B,OAAO,QAAQ,KAAI,QAAO,KAAK,iBAAiB,GAAG,CAAC;EAExD;EAEA,IAAI,KAAK,eAAe,IAAI,GAC1B,OAAO,KAAK,KAAI,QAAO,KAAK,iBAAiB,GAAG,CAAC;EAGnD,IAAI,OAAO,SAAS,UAClB,OAAO,CAAC;GAAE,MAAM;GAAa,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;GAAK,CAAC;EAAE,CAAC;EAGxE,IAAI,KAAK,0BAA0B,IAAI,GAAG;GACxC,MAAM,UAA4B,CAAC;GACnC,IAAI,KAAK,MACP,QAAQ,KAAK;IAAE,MAAM;IAAQ,MAAM,KAAK;GAAK,CAAC;GAEhD,KAAK,MAAM,MAAM,KAAK,WACpB,QAAQ,KAAK;IACX,MAAM;IACN,IAAI,GAAG;IACP,UAAU;KAAE,MAAM,GAAG;KAAU,WAAW,GAAG;IAAK;GACpD,CAAC;GAEH,OAAO,CAAC;IAAE,MAAM;IAAa;GAAQ,CAAC;EACxC;EAGA,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,KAAK,UAAU,MAAM;GACvF,MAAM,OAAQ,KAAiC;GAC/C,IAAI,OAAO,SAAS,UAClB,OAAO,CAAC;IAAE,MAAM;IAAa,SAAS,CAAC;KAAE,MAAM;KAAQ;IAAK,CAAC;GAAE,CAAC;EAEpE;EAEA,OAAO,CAAC;GAAE,MAAM;GAAa,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,KAAK,cAAc,IAAI;GAAE,CAAC;EAAE,CAAC;CAC5F;CAEA,0BACE,MACsG;EACtG,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,eAAe,OAAO,OAAO;EAChF,MAAM,EAAE,cAAc;EACtB,OAAO,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS;CACxD;CAEA,eAAuB,MAAwC;EAC7D,IAAI,CAAC,MAAM,QAAQ,IAAI,GACrB,OAAO;EAGT,OAAO,KAAK,OAAM,SAAQ,OAAO,SAAS,YAAY,SAAS,QAAQ,UAAU,QAAQ,aAAa,IAAI;CAC5G;CAEA,iBAAyB,KAAoC;EAC3D,IAAI,OAAO,IAAI,YAAY,UACzB,OAAO;GACL,MAAM,IAAI;GACV,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,IAAI;GAAQ,CAAC;EAC/C;EAGF,OAAO;GACL,MAAM,IAAI;GACV,SAAS,IAAI;EACf;CACF;CAEA,cAAsB,MAAuB;EAC3C,IAAI;GACF,OAAO,KAAK,UAAU,IAAI;EAC5B,QAAQ;GACN,IAAI,OAAO,SAAS,YAAY,SAAS,MACvC,OAAO,qBAAqB,KAAK,aAAa,QAAQ,SAAS;GAEjE,OAAO,OAAO,IAAI;EACpB;CACF;;;;CAKA,MAAyB,SAAwB;EAC/C,IAAI,KAAKF,SACP,MAAM,KAAKA,QAAQ,MAAM;CAE7B;CAEA,MAAe,gBAA+B;EAC5C,IAAI,KAAKA,SACP,MAAM,KAAKA,QAAQ,SAAS;CAEhC;AACF"}
1
+ {"version":3,"file":"index.cjs","names":["TrackingExporter","#client","PostHog","SpanType"],"sources":["../src/tracing.ts"],"sourcesContent":["import type {\n AnyExportedSpan,\n ExportedFeedback,\n FeedbackEvent,\n ModelGenerationAttributes,\n SpanErrorInfo,\n UsageStats,\n} from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { TrackingExporter } from '@mastra/observability';\nimport { PostHog } from 'posthog-node';\nimport type { EventMessage } from 'posthog-node';\n\n/**\n * Token usage format compatible with PostHog.\n * @see https://posthog.com/docs/llm-analytics/generations#event-properties\n */\nexport interface PostHogUsageMetrics {\n $ai_input_tokens?: number;\n $ai_output_tokens?: number;\n $ai_cache_read_input_tokens?: number;\n $ai_cache_creation_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to PostHog's expected property format.\n *\n * Pass through gross input token counts with cache fields as subsets.\n * PostHog subtracts cache tokens when computing costs for non-Anthropic\n * providers and detects Anthropic-style exclusive reporting on its own.\n *\n * @param usage - The UsageStats from span attributes\n * @returns PostHog-formatted usage properties\n */\nexport function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics {\n if (!usage) return {};\n\n const props: PostHogUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) {\n props.$ai_input_tokens = usage.inputTokens;\n }\n\n if (usage.inputDetails?.cacheRead !== undefined) {\n props.$ai_cache_read_input_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage.inputDetails?.cacheWrite !== undefined) {\n props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;\n }\n\n if (usage.outputTokens !== undefined) {\n props.$ai_output_tokens = usage.outputTokens;\n }\n\n return props;\n}\n\ninterface PostHogMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: PostHogContent[];\n}\n\ninterface PostHogContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface MastraMessage {\n role: string;\n content: string | MastraContent[];\n}\n\ninterface MastraContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ntype SpanData = string | MastraMessage[] | Record<string, unknown> | unknown;\n\nconst DISTINCT_ID = 'distinctId';\n\nexport interface PosthogExporterConfig extends TrackingExporterConfig {\n /** PostHog API key. Defaults to POSTHOG_API_KEY environment variable. */\n apiKey?: string;\n /** PostHog host URL. Defaults to POSTHOG_HOST environment variable or US region. */\n host?: string;\n flushAt?: number;\n flushInterval?: number;\n serverless?: boolean;\n defaultDistinctId?: string;\n enablePrivacyMode?: boolean;\n}\n\ntype PosthogRoot = unknown;\ntype PosthogSpan = AnyExportedSpan;\n// used as a placeholder for event data since we don't need to cache\n// event data for Posthog\ntype PosthogEvent = boolean;\ntype PosthogMetadata = unknown;\ntype PosthogTraceData = TraceData<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata>;\n\nexport class PosthogExporter extends TrackingExporter<\n PosthogRoot,\n PosthogSpan,\n PosthogEvent,\n PosthogMetadata,\n PosthogExporterConfig\n> {\n name = 'posthog';\n #client: PostHog | undefined;\n\n private static readonly SERVERLESS_FLUSH_AT = 10;\n private static readonly SERVERLESS_FLUSH_INTERVAL = 2000;\n private static readonly DEFAULT_FLUSH_AT = 20;\n private static readonly DEFAULT_FLUSH_INTERVAL = 10000;\n\n constructor(config: PosthogExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const apiKey = config.apiKey ?? process.env.POSTHOG_API_KEY;\n\n super({ ...config, apiKey });\n\n if (!apiKey) {\n this.setDisabled('Missing required API key. Set POSTHOG_API_KEY environment variable or pass apiKey in config.');\n return;\n }\n\n const clientConfig = this.buildClientConfig(this.config);\n this.#client = new PostHog(apiKey, clientConfig);\n const message =\n (config.serverless ?? false) ? 'PostHog exporter initialized in serverless mode' : 'PostHog exporter initialized';\n this.logger.debug(message, config);\n }\n\n private buildClientConfig(config: PosthogExporterConfig) {\n const isServerless = config.serverless ?? false;\n const flushAt =\n config.flushAt ?? (isServerless ? PosthogExporter.SERVERLESS_FLUSH_AT : PosthogExporter.DEFAULT_FLUSH_AT);\n const flushInterval =\n config.flushInterval ??\n (isServerless ? PosthogExporter.SERVERLESS_FLUSH_INTERVAL : PosthogExporter.DEFAULT_FLUSH_INTERVAL);\n\n const host = config.host || process.env.POSTHOG_HOST || 'https://us.i.posthog.com';\n\n if (!config.host && !process.env.POSTHOG_HOST) {\n this.logger.info(\n 'No PostHog host specified, using US default (https://us.i.posthog.com). ' +\n 'For EU region, set `host: \"https://eu.i.posthog.com\"` in config or POSTHOG_HOST env var. ' +\n 'For self-hosted, provide your instance URL.',\n );\n }\n\n return {\n host,\n flushAt,\n flushInterval,\n privacyMode: config.enablePrivacyMode,\n };\n }\n\n protected override skipBuildRootTask = true;\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogRoot | undefined> {\n throw new Error('Method not implemented.');\n }\n\n protected override skipCachingEventSpans = true;\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogEvent> {\n const { span, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const distinctId = this.getDistinctId(span, traceData);\n const properties = this.buildEventProperties(span, 0);\n\n this.#client?.capture(\n this.withGroups({\n distinctId,\n event: eventName,\n properties,\n timestamp: span.endTime ? new Date(span.endTime) : new Date(),\n }),\n );\n\n return true;\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogSpan | undefined> {\n const { span, traceData } = args;\n if (!traceData.hasExtraValue(DISTINCT_ID)) {\n const userId = span.metadata?.userId;\n if (userId) {\n traceData.setExtraValue(DISTINCT_ID, String(userId));\n }\n }\n\n return span;\n }\n\n protected override skipSpanUpdateEvents = true;\n protected override _updateSpan(_args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n throw new Error('Method not implemented.');\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n // Merge input from cached span (SPAN_STARTED) if not present on end span\n // This handles the case where input is only sent at start\n const cachedSpan = traceData.getSpan({ spanId: span.id });\n const mergedSpan = !span.input && cachedSpan?.input ? { ...span, input: cachedSpan.input } : span;\n\n const eventMessage = this.buildEventMessage({ span: mergedSpan, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n protected override async _abortSpan(args: {\n span: PosthogSpan;\n reason: SpanErrorInfo;\n traceData: PosthogTraceData;\n }): Promise<void> {\n const { span, reason, traceData } = args;\n\n // update span with the abort reason\n span.errorInfo = reason;\n\n const eventMessage = this.buildEventMessage({ span, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n /**\n * Forward feedback recorded via `addFeedback()` to PostHog as a native\n * `$ai_feedback` event, shown as \"User feedback\" on the linked trace in\n * PostHog's AI observability UI.\n */\n async onFeedbackEvent(event: FeedbackEvent): Promise<void> {\n if (!this.#client) return;\n\n const { feedback } = event;\n if (!feedback.traceId) {\n this.logger.debug('PostHog exporter: dropping feedback with no traceId; PostHog requires $ai_trace_id', {\n feedbackId: feedback.feedbackId,\n });\n return;\n }\n\n const properties: Record<string, any> = {\n // Custom metadata goes first so it cannot overwrite the natively mapped fields below\n ...this.extractCustomMetadata(feedback.metadata),\n $ai_trace_id: feedback.traceId,\n // PostHog's trace UI only displays feedback events that carry $ai_feedback_text\n $ai_feedback_text: feedback.comment ?? String(feedback.value),\n feedback_id: feedback.feedbackId,\n feedback_type: feedback.feedbackType,\n feedback_value: feedback.value,\n };\n\n const feedbackSource = feedback.feedbackSource ?? feedback.source;\n if (feedbackSource) properties.feedback_source = feedbackSource;\n if (feedback.spanId) properties.span_id = feedback.spanId;\n if (feedback.sourceId) properties.source_id = feedback.sourceId;\n if (feedback.metadata?.sessionId) properties.$ai_session_id = feedback.metadata.sessionId;\n\n try {\n this.#client.capture(\n this.withGroups({\n distinctId: this.getFeedbackDistinctId(feedback),\n event: '$ai_feedback',\n properties,\n timestamp: new Date(feedback.timestamp),\n }),\n );\n } catch (err) {\n this.logger.error('PostHog exporter: failed to submit feedback', {\n error: err,\n traceId: feedback.traceId,\n feedbackId: feedback.feedbackId,\n });\n }\n }\n\n private getFeedbackDistinctId(feedback: ExportedFeedback): string {\n const userId = feedback.feedbackUserId ?? feedback.userId ?? feedback.metadata?.userId;\n if (userId) {\n return String(userId);\n }\n return this.config.defaultDistinctId ?? 'anonymous';\n }\n\n /**\n * PostHog group analytics are keyed off the top-level `groups` field on the\n * capture call. The Node SDK derives the event's `$groups` from that field and\n * overwrites any property-level `$groups`, so group metadata carried in\n * properties is dropped unless it is mirrored here.\n */\n private withGroups(message: EventMessage): EventMessage {\n const groups = message.properties?.$groups;\n if (groups && typeof groups === 'object' && !Array.isArray(groups)) {\n return { ...message, groups };\n }\n return message;\n }\n\n private buildEventMessage(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): EventMessage {\n const { span, traceData } = args;\n\n const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();\n\n const distinctId = this.getDistinctId(span, traceData);\n\n if (span.isRootSpan) {\n return this.buildRootEventMessage({ span, distinctId, endTime });\n } else {\n return this.buildChildEventMessage({ span, distinctId, endTime, traceData });\n }\n }\n\n /**\n * Capture an explicit $ai_trace event for root spans.\n * This gives us control over trace-level metadata like name and tags,\n * rather than relying on PostHog's pseudo-trace auto-creation.\n */\n private buildRootEventMessage(args: { span: AnyExportedSpan; distinctId: string; endTime: number }): EventMessage {\n const { span, distinctId, endTime } = args;\n\n // Note: We don't set $ai_latency on $ai_trace events because PostHog\n // aggregates latency from child events. Setting it here causes double-counting.\n const traceProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_span_name: span.name,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.metadata?.sessionId) {\n traceProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n if (span.input) {\n traceProperties.$ai_input_state = span.input;\n }\n\n if (span.output) {\n traceProperties.$ai_output_state = span.output;\n }\n\n if (span.errorInfo) {\n traceProperties.$ai_error = {\n message: span.errorInfo.message,\n ...(span.errorInfo.id && { id: span.errorInfo.id }),\n ...(span.errorInfo.category && { category: span.errorInfo.category }),\n };\n }\n\n // Add tags as custom properties (PostHog doesn't have native tag support on traces)\n if (span.tags?.length) {\n for (const tag of span.tags) {\n traceProperties[tag] = true;\n }\n }\n\n // Add custom metadata (excluding userId and sessionId which are handled separately)\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n Object.assign(traceProperties, customMetadata);\n\n return {\n distinctId,\n event: '$ai_trace',\n properties: traceProperties,\n timestamp: new Date(endTime),\n };\n }\n\n private buildChildEventMessage(args: {\n span: AnyExportedSpan;\n distinctId: string;\n endTime: number;\n traceData: PosthogTraceData;\n }): EventMessage {\n const { span, distinctId, endTime, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const startTime = span.startTime.getTime();\n const latency = (endTime - startTime) / 1000;\n\n // Check if parent is the root span - if so, use traceId as parent_id\n // since we don't create an $ai_span for root spans\n const parentIsRootSpan = this.isParentRootSpan(span, traceData);\n const properties = this.buildEventProperties(span, latency, parentIsRootSpan);\n\n return {\n distinctId,\n event: eventName,\n properties,\n timestamp: new Date(endTime),\n };\n }\n\n private toDate(timestamp: Date | number): Date {\n return timestamp instanceof Date ? timestamp : new Date(timestamp);\n }\n\n private mapToPostHogEvent(spanType: SpanType): string {\n if (spanType == SpanType.MODEL_GENERATION) {\n return '$ai_generation';\n }\n return '$ai_span';\n }\n\n private getDistinctId(span: AnyExportedSpan, traceData?: PosthogTraceData): string {\n if (span.metadata?.userId) {\n return String(span.metadata.userId);\n }\n\n if (traceData?.hasExtraValue(DISTINCT_ID)) {\n return String(traceData.getExtraValue(DISTINCT_ID));\n }\n\n if (this.config.defaultDistinctId) {\n return this.config.defaultDistinctId;\n }\n\n return 'anonymous';\n }\n\n /**\n * Check if the parent of this span is the root span.\n * We need this because we don't create $ai_span for root spans,\n * so children of root spans should use $ai_trace_id as their $ai_parent_id.\n */\n private isParentRootSpan(span: AnyExportedSpan, traceData: PosthogTraceData): boolean {\n if (!span.parentSpanId) {\n return false;\n }\n\n // Look up the parent span in our cache to check if it's a root span\n const parentCache = traceData.getSpan({ spanId: span.parentSpanId });\n if (parentCache) {\n return parentCache.isRootSpan;\n }\n\n // Parent not found in cache - shouldn't happen normally, but default to false\n return false;\n }\n\n private buildEventProperties(\n span: AnyExportedSpan,\n latency: number,\n parentIsRootSpan: boolean = false,\n ): Record<string, any> {\n const baseProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_latency: latency,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.parentSpanId) {\n // If parent is the root span, use trace_id as parent_id since we don't\n // create an $ai_span for root spans (only $ai_trace)\n baseProperties.$ai_parent_id = parentIsRootSpan ? span.traceId : span.parentSpanId;\n }\n\n if (span.metadata?.sessionId) {\n baseProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n // Include tags for root spans (tags are only set on root spans by design)\n // PostHog doesn't allow setting tags directly, so we iterate through each tag\n // and set it as a property with value true\n if (span.isRootSpan && span.tags?.length) {\n for (const tag of span.tags) {\n baseProperties[tag] = true;\n }\n }\n\n if (span.type === SpanType.MODEL_GENERATION) {\n baseProperties.$ai_generation_id = span.id;\n return { ...baseProperties, ...this.buildGenerationProperties(span) };\n } else {\n baseProperties.$ai_span_id = span.id;\n baseProperties.$ai_span_name = span.name;\n return { ...baseProperties, ...this.buildSpanProperties(span) };\n }\n }\n\n private extractErrorProperties(errorInfo?: SpanErrorInfo): Record<string, any> {\n if (!errorInfo) {\n return {};\n }\n\n const props: Record<string, string> = {\n error_message: errorInfo.message,\n };\n\n if (errorInfo.id) {\n props.error_id = errorInfo.id;\n }\n\n if (errorInfo.category) {\n props.error_category = errorInfo.category;\n }\n\n return props;\n }\n\n private extractCustomMetadata(metadata?: Record<string, unknown>): Record<string, any> {\n const { userId, sessionId, ...customMetadata } = metadata ?? {};\n return customMetadata;\n }\n\n private buildGenerationProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n const attrs = (span.attributes ?? {}) as ModelGenerationAttributes;\n\n props.$ai_model = attrs.model || 'unknown-model';\n props.$ai_provider = attrs.provider || 'unknown-provider';\n\n if (span.input) props.$ai_input = this.formatMessages(span.input, 'user');\n if (span.output) props.$ai_output_choices = this.formatMessages(span.output, 'assistant');\n\n // Extract usage properties using the shared utility\n Object.assign(props, formatUsageMetrics(attrs.usage));\n\n if (attrs.parameters) {\n if (attrs.parameters.temperature !== undefined) props.$ai_temperature = attrs.parameters.temperature;\n if (attrs.parameters.maxOutputTokens !== undefined) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;\n }\n if (attrs.streaming !== undefined) props.$ai_stream = attrs.streaming;\n if (attrs.tools?.length) {\n // OpenAI-style shape — the format PostHog's own AI SDKs send and its\n // trace view renders. Provider-defined tools pass through as-is.\n props.$ai_tools = attrs.tools.map(tool =>\n tool.type === 'function'\n ? {\n type: 'function',\n function: { name: tool.name, description: tool.description, parameters: tool.parameters },\n }\n : tool,\n );\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private buildSpanProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n\n if (span.input) props.$ai_input_state = span.input;\n if (span.output) props.$ai_output_state = span.output;\n\n if (span.type === SpanType.MODEL_CHUNK) {\n const attrs = span.attributes as any;\n if (attrs?.chunkType) props.chunk_type = attrs.chunkType;\n if (attrs?.sequenceNumber !== undefined) props.chunk_sequence_number = attrs.sequenceNumber;\n }\n\n if (span.attributes) {\n Object.assign(props, span.attributes);\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private formatMessages(data: SpanData, defaultRole: 'user' | 'assistant' = 'user'): PostHogMessage[] {\n // Unwrap {messages: [...]} wrapper produced by generation span inputs\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'messages' in data) {\n const wrapped = (data as Record<string, unknown>).messages;\n if (this.isMessageArray(wrapped)) {\n return wrapped.map(msg => this.normalizeMessage(msg));\n }\n }\n\n if (this.isMessageArray(data)) {\n return data.map(msg => this.normalizeMessage(msg));\n }\n\n if (typeof data === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text: data }] }];\n }\n\n if (this.isSpanOutputWithToolCalls(data)) {\n const content: PostHogContent[] = [];\n if (data.text) {\n content.push({ type: 'text', text: data.text });\n }\n for (const tc of data.toolCalls) {\n content.push({\n type: 'tool-call',\n id: tc.toolCallId,\n function: { name: tc.toolName, arguments: tc.args },\n });\n }\n return [{ role: 'assistant', content }];\n }\n\n // Extract text from output objects (e.g. generation outputs with text but no tool calls)\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'text' in data) {\n const text = (data as Record<string, unknown>).text;\n if (typeof text === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text }] }];\n }\n }\n\n return [{ role: defaultRole, content: [{ type: 'text', text: this.safeStringify(data) }] }];\n }\n\n private isSpanOutputWithToolCalls(\n data: unknown,\n ): data is { text?: string; toolCalls: Array<{ toolCallId: string; toolName: string; args: unknown }> } {\n if (typeof data !== 'object' || data === null || !('toolCalls' in data)) return false;\n const { toolCalls } = data as Record<string, unknown>;\n return Array.isArray(toolCalls) && toolCalls.length > 0;\n }\n\n private isMessageArray(data: unknown): data is MastraMessage[] {\n if (!Array.isArray(data)) {\n return false;\n }\n\n return data.every(item => typeof item === 'object' && item !== null && 'role' in item && 'content' in item);\n }\n\n private normalizeMessage(msg: MastraMessage): PostHogMessage {\n if (typeof msg.content === 'string') {\n return {\n role: msg.role as PostHogMessage['role'],\n content: [{ type: 'text', text: msg.content }],\n };\n }\n\n return {\n role: msg.role as PostHogMessage['role'],\n content: msg.content as PostHogContent[],\n };\n }\n\n private safeStringify(data: unknown): string {\n try {\n return JSON.stringify(data);\n } catch {\n if (typeof data === 'object' && data !== null) {\n return `[Non-serializable ${data.constructor?.name || 'Object'}]`;\n }\n return String(data);\n }\n }\n\n /**\n * Force flush any buffered data to PostHog without shutting down.\n */\n protected override async _flush(): Promise<void> {\n if (this.#client) {\n await this.#client.flush();\n }\n }\n\n override async _postShutdown(): Promise<void> {\n if (this.#client) {\n await this.#client.shutdown();\n }\n }\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -298,6 +298,14 @@ var PosthogExporter = class PosthogExporter extends TrackingExporter {
298
298
  if (attrs.parameters.maxOutputTokens !== void 0) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;
299
299
  }
300
300
  if (attrs.streaming !== void 0) props.$ai_stream = attrs.streaming;
301
+ if (attrs.tools?.length) props.$ai_tools = attrs.tools.map((tool) => tool.type === "function" ? {
302
+ type: "function",
303
+ function: {
304
+ name: tool.name,
305
+ description: tool.description,
306
+ parameters: tool.parameters
307
+ }
308
+ } : tool);
301
309
  return {
302
310
  ...props,
303
311
  ...this.extractErrorProperties(span.errorInfo),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#client"],"sources":["../src/tracing.ts"],"sourcesContent":["import type {\n AnyExportedSpan,\n ExportedFeedback,\n FeedbackEvent,\n ModelGenerationAttributes,\n SpanErrorInfo,\n UsageStats,\n} from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { TrackingExporter } from '@mastra/observability';\nimport { PostHog } from 'posthog-node';\nimport type { EventMessage } from 'posthog-node';\n\n/**\n * Token usage format compatible with PostHog.\n * @see https://posthog.com/docs/llm-analytics/generations#event-properties\n */\nexport interface PostHogUsageMetrics {\n $ai_input_tokens?: number;\n $ai_output_tokens?: number;\n $ai_cache_read_input_tokens?: number;\n $ai_cache_creation_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to PostHog's expected property format.\n *\n * Pass through gross input token counts with cache fields as subsets.\n * PostHog subtracts cache tokens when computing costs for non-Anthropic\n * providers and detects Anthropic-style exclusive reporting on its own.\n *\n * @param usage - The UsageStats from span attributes\n * @returns PostHog-formatted usage properties\n */\nexport function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics {\n if (!usage) return {};\n\n const props: PostHogUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) {\n props.$ai_input_tokens = usage.inputTokens;\n }\n\n if (usage.inputDetails?.cacheRead !== undefined) {\n props.$ai_cache_read_input_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage.inputDetails?.cacheWrite !== undefined) {\n props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;\n }\n\n if (usage.outputTokens !== undefined) {\n props.$ai_output_tokens = usage.outputTokens;\n }\n\n return props;\n}\n\ninterface PostHogMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: PostHogContent[];\n}\n\ninterface PostHogContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface MastraMessage {\n role: string;\n content: string | MastraContent[];\n}\n\ninterface MastraContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ntype SpanData = string | MastraMessage[] | Record<string, unknown> | unknown;\n\nconst DISTINCT_ID = 'distinctId';\n\nexport interface PosthogExporterConfig extends TrackingExporterConfig {\n /** PostHog API key. Defaults to POSTHOG_API_KEY environment variable. */\n apiKey?: string;\n /** PostHog host URL. Defaults to POSTHOG_HOST environment variable or US region. */\n host?: string;\n flushAt?: number;\n flushInterval?: number;\n serverless?: boolean;\n defaultDistinctId?: string;\n enablePrivacyMode?: boolean;\n}\n\ntype PosthogRoot = unknown;\ntype PosthogSpan = AnyExportedSpan;\n// used as a placeholder for event data since we don't need to cache\n// event data for Posthog\ntype PosthogEvent = boolean;\ntype PosthogMetadata = unknown;\ntype PosthogTraceData = TraceData<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata>;\n\nexport class PosthogExporter extends TrackingExporter<\n PosthogRoot,\n PosthogSpan,\n PosthogEvent,\n PosthogMetadata,\n PosthogExporterConfig\n> {\n name = 'posthog';\n #client: PostHog | undefined;\n\n private static readonly SERVERLESS_FLUSH_AT = 10;\n private static readonly SERVERLESS_FLUSH_INTERVAL = 2000;\n private static readonly DEFAULT_FLUSH_AT = 20;\n private static readonly DEFAULT_FLUSH_INTERVAL = 10000;\n\n constructor(config: PosthogExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const apiKey = config.apiKey ?? process.env.POSTHOG_API_KEY;\n\n super({ ...config, apiKey });\n\n if (!apiKey) {\n this.setDisabled('Missing required API key. Set POSTHOG_API_KEY environment variable or pass apiKey in config.');\n return;\n }\n\n const clientConfig = this.buildClientConfig(this.config);\n this.#client = new PostHog(apiKey, clientConfig);\n const message =\n (config.serverless ?? false) ? 'PostHog exporter initialized in serverless mode' : 'PostHog exporter initialized';\n this.logger.debug(message, config);\n }\n\n private buildClientConfig(config: PosthogExporterConfig) {\n const isServerless = config.serverless ?? false;\n const flushAt =\n config.flushAt ?? (isServerless ? PosthogExporter.SERVERLESS_FLUSH_AT : PosthogExporter.DEFAULT_FLUSH_AT);\n const flushInterval =\n config.flushInterval ??\n (isServerless ? PosthogExporter.SERVERLESS_FLUSH_INTERVAL : PosthogExporter.DEFAULT_FLUSH_INTERVAL);\n\n const host = config.host || process.env.POSTHOG_HOST || 'https://us.i.posthog.com';\n\n if (!config.host && !process.env.POSTHOG_HOST) {\n this.logger.info(\n 'No PostHog host specified, using US default (https://us.i.posthog.com). ' +\n 'For EU region, set `host: \"https://eu.i.posthog.com\"` in config or POSTHOG_HOST env var. ' +\n 'For self-hosted, provide your instance URL.',\n );\n }\n\n return {\n host,\n flushAt,\n flushInterval,\n privacyMode: config.enablePrivacyMode,\n };\n }\n\n protected override skipBuildRootTask = true;\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogRoot | undefined> {\n throw new Error('Method not implemented.');\n }\n\n protected override skipCachingEventSpans = true;\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogEvent> {\n const { span, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const distinctId = this.getDistinctId(span, traceData);\n const properties = this.buildEventProperties(span, 0);\n\n this.#client?.capture(\n this.withGroups({\n distinctId,\n event: eventName,\n properties,\n timestamp: span.endTime ? new Date(span.endTime) : new Date(),\n }),\n );\n\n return true;\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogSpan | undefined> {\n const { span, traceData } = args;\n if (!traceData.hasExtraValue(DISTINCT_ID)) {\n const userId = span.metadata?.userId;\n if (userId) {\n traceData.setExtraValue(DISTINCT_ID, String(userId));\n }\n }\n\n return span;\n }\n\n protected override skipSpanUpdateEvents = true;\n protected override _updateSpan(_args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n throw new Error('Method not implemented.');\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n // Merge input from cached span (SPAN_STARTED) if not present on end span\n // This handles the case where input is only sent at start\n const cachedSpan = traceData.getSpan({ spanId: span.id });\n const mergedSpan = !span.input && cachedSpan?.input ? { ...span, input: cachedSpan.input } : span;\n\n const eventMessage = this.buildEventMessage({ span: mergedSpan, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n protected override async _abortSpan(args: {\n span: PosthogSpan;\n reason: SpanErrorInfo;\n traceData: PosthogTraceData;\n }): Promise<void> {\n const { span, reason, traceData } = args;\n\n // update span with the abort reason\n span.errorInfo = reason;\n\n const eventMessage = this.buildEventMessage({ span, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n /**\n * Forward feedback recorded via `addFeedback()` to PostHog as a native\n * `$ai_feedback` event, shown as \"User feedback\" on the linked trace in\n * PostHog's AI observability UI.\n */\n async onFeedbackEvent(event: FeedbackEvent): Promise<void> {\n if (!this.#client) return;\n\n const { feedback } = event;\n if (!feedback.traceId) {\n this.logger.debug('PostHog exporter: dropping feedback with no traceId; PostHog requires $ai_trace_id', {\n feedbackId: feedback.feedbackId,\n });\n return;\n }\n\n const properties: Record<string, any> = {\n // Custom metadata goes first so it cannot overwrite the natively mapped fields below\n ...this.extractCustomMetadata(feedback.metadata),\n $ai_trace_id: feedback.traceId,\n // PostHog's trace UI only displays feedback events that carry $ai_feedback_text\n $ai_feedback_text: feedback.comment ?? String(feedback.value),\n feedback_id: feedback.feedbackId,\n feedback_type: feedback.feedbackType,\n feedback_value: feedback.value,\n };\n\n const feedbackSource = feedback.feedbackSource ?? feedback.source;\n if (feedbackSource) properties.feedback_source = feedbackSource;\n if (feedback.spanId) properties.span_id = feedback.spanId;\n if (feedback.sourceId) properties.source_id = feedback.sourceId;\n if (feedback.metadata?.sessionId) properties.$ai_session_id = feedback.metadata.sessionId;\n\n try {\n this.#client.capture(\n this.withGroups({\n distinctId: this.getFeedbackDistinctId(feedback),\n event: '$ai_feedback',\n properties,\n timestamp: new Date(feedback.timestamp),\n }),\n );\n } catch (err) {\n this.logger.error('PostHog exporter: failed to submit feedback', {\n error: err,\n traceId: feedback.traceId,\n feedbackId: feedback.feedbackId,\n });\n }\n }\n\n private getFeedbackDistinctId(feedback: ExportedFeedback): string {\n const userId = feedback.feedbackUserId ?? feedback.userId ?? feedback.metadata?.userId;\n if (userId) {\n return String(userId);\n }\n return this.config.defaultDistinctId ?? 'anonymous';\n }\n\n /**\n * PostHog group analytics are keyed off the top-level `groups` field on the\n * capture call. The Node SDK derives the event's `$groups` from that field and\n * overwrites any property-level `$groups`, so group metadata carried in\n * properties is dropped unless it is mirrored here.\n */\n private withGroups(message: EventMessage): EventMessage {\n const groups = message.properties?.$groups;\n if (groups && typeof groups === 'object' && !Array.isArray(groups)) {\n return { ...message, groups };\n }\n return message;\n }\n\n private buildEventMessage(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): EventMessage {\n const { span, traceData } = args;\n\n const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();\n\n const distinctId = this.getDistinctId(span, traceData);\n\n if (span.isRootSpan) {\n return this.buildRootEventMessage({ span, distinctId, endTime });\n } else {\n return this.buildChildEventMessage({ span, distinctId, endTime, traceData });\n }\n }\n\n /**\n * Capture an explicit $ai_trace event for root spans.\n * This gives us control over trace-level metadata like name and tags,\n * rather than relying on PostHog's pseudo-trace auto-creation.\n */\n private buildRootEventMessage(args: { span: AnyExportedSpan; distinctId: string; endTime: number }): EventMessage {\n const { span, distinctId, endTime } = args;\n\n // Note: We don't set $ai_latency on $ai_trace events because PostHog\n // aggregates latency from child events. Setting it here causes double-counting.\n const traceProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_span_name: span.name,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.metadata?.sessionId) {\n traceProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n if (span.input) {\n traceProperties.$ai_input_state = span.input;\n }\n\n if (span.output) {\n traceProperties.$ai_output_state = span.output;\n }\n\n if (span.errorInfo) {\n traceProperties.$ai_error = {\n message: span.errorInfo.message,\n ...(span.errorInfo.id && { id: span.errorInfo.id }),\n ...(span.errorInfo.category && { category: span.errorInfo.category }),\n };\n }\n\n // Add tags as custom properties (PostHog doesn't have native tag support on traces)\n if (span.tags?.length) {\n for (const tag of span.tags) {\n traceProperties[tag] = true;\n }\n }\n\n // Add custom metadata (excluding userId and sessionId which are handled separately)\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n Object.assign(traceProperties, customMetadata);\n\n return {\n distinctId,\n event: '$ai_trace',\n properties: traceProperties,\n timestamp: new Date(endTime),\n };\n }\n\n private buildChildEventMessage(args: {\n span: AnyExportedSpan;\n distinctId: string;\n endTime: number;\n traceData: PosthogTraceData;\n }): EventMessage {\n const { span, distinctId, endTime, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const startTime = span.startTime.getTime();\n const latency = (endTime - startTime) / 1000;\n\n // Check if parent is the root span - if so, use traceId as parent_id\n // since we don't create an $ai_span for root spans\n const parentIsRootSpan = this.isParentRootSpan(span, traceData);\n const properties = this.buildEventProperties(span, latency, parentIsRootSpan);\n\n return {\n distinctId,\n event: eventName,\n properties,\n timestamp: new Date(endTime),\n };\n }\n\n private toDate(timestamp: Date | number): Date {\n return timestamp instanceof Date ? timestamp : new Date(timestamp);\n }\n\n private mapToPostHogEvent(spanType: SpanType): string {\n if (spanType == SpanType.MODEL_GENERATION) {\n return '$ai_generation';\n }\n return '$ai_span';\n }\n\n private getDistinctId(span: AnyExportedSpan, traceData?: PosthogTraceData): string {\n if (span.metadata?.userId) {\n return String(span.metadata.userId);\n }\n\n if (traceData?.hasExtraValue(DISTINCT_ID)) {\n return String(traceData.getExtraValue(DISTINCT_ID));\n }\n\n if (this.config.defaultDistinctId) {\n return this.config.defaultDistinctId;\n }\n\n return 'anonymous';\n }\n\n /**\n * Check if the parent of this span is the root span.\n * We need this because we don't create $ai_span for root spans,\n * so children of root spans should use $ai_trace_id as their $ai_parent_id.\n */\n private isParentRootSpan(span: AnyExportedSpan, traceData: PosthogTraceData): boolean {\n if (!span.parentSpanId) {\n return false;\n }\n\n // Look up the parent span in our cache to check if it's a root span\n const parentCache = traceData.getSpan({ spanId: span.parentSpanId });\n if (parentCache) {\n return parentCache.isRootSpan;\n }\n\n // Parent not found in cache - shouldn't happen normally, but default to false\n return false;\n }\n\n private buildEventProperties(\n span: AnyExportedSpan,\n latency: number,\n parentIsRootSpan: boolean = false,\n ): Record<string, any> {\n const baseProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_latency: latency,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.parentSpanId) {\n // If parent is the root span, use trace_id as parent_id since we don't\n // create an $ai_span for root spans (only $ai_trace)\n baseProperties.$ai_parent_id = parentIsRootSpan ? span.traceId : span.parentSpanId;\n }\n\n if (span.metadata?.sessionId) {\n baseProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n // Include tags for root spans (tags are only set on root spans by design)\n // PostHog doesn't allow setting tags directly, so we iterate through each tag\n // and set it as a property with value true\n if (span.isRootSpan && span.tags?.length) {\n for (const tag of span.tags) {\n baseProperties[tag] = true;\n }\n }\n\n if (span.type === SpanType.MODEL_GENERATION) {\n baseProperties.$ai_generation_id = span.id;\n return { ...baseProperties, ...this.buildGenerationProperties(span) };\n } else {\n baseProperties.$ai_span_id = span.id;\n baseProperties.$ai_span_name = span.name;\n return { ...baseProperties, ...this.buildSpanProperties(span) };\n }\n }\n\n private extractErrorProperties(errorInfo?: SpanErrorInfo): Record<string, any> {\n if (!errorInfo) {\n return {};\n }\n\n const props: Record<string, string> = {\n error_message: errorInfo.message,\n };\n\n if (errorInfo.id) {\n props.error_id = errorInfo.id;\n }\n\n if (errorInfo.category) {\n props.error_category = errorInfo.category;\n }\n\n return props;\n }\n\n private extractCustomMetadata(metadata?: Record<string, unknown>): Record<string, any> {\n const { userId, sessionId, ...customMetadata } = metadata ?? {};\n return customMetadata;\n }\n\n private buildGenerationProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n const attrs = (span.attributes ?? {}) as ModelGenerationAttributes;\n\n props.$ai_model = attrs.model || 'unknown-model';\n props.$ai_provider = attrs.provider || 'unknown-provider';\n\n if (span.input) props.$ai_input = this.formatMessages(span.input, 'user');\n if (span.output) props.$ai_output_choices = this.formatMessages(span.output, 'assistant');\n\n // Extract usage properties using the shared utility\n Object.assign(props, formatUsageMetrics(attrs.usage));\n\n if (attrs.parameters) {\n if (attrs.parameters.temperature !== undefined) props.$ai_temperature = attrs.parameters.temperature;\n if (attrs.parameters.maxOutputTokens !== undefined) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;\n }\n if (attrs.streaming !== undefined) props.$ai_stream = attrs.streaming;\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private buildSpanProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n\n if (span.input) props.$ai_input_state = span.input;\n if (span.output) props.$ai_output_state = span.output;\n\n if (span.type === SpanType.MODEL_CHUNK) {\n const attrs = span.attributes as any;\n if (attrs?.chunkType) props.chunk_type = attrs.chunkType;\n if (attrs?.sequenceNumber !== undefined) props.chunk_sequence_number = attrs.sequenceNumber;\n }\n\n if (span.attributes) {\n Object.assign(props, span.attributes);\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private formatMessages(data: SpanData, defaultRole: 'user' | 'assistant' = 'user'): PostHogMessage[] {\n // Unwrap {messages: [...]} wrapper produced by generation span inputs\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'messages' in data) {\n const wrapped = (data as Record<string, unknown>).messages;\n if (this.isMessageArray(wrapped)) {\n return wrapped.map(msg => this.normalizeMessage(msg));\n }\n }\n\n if (this.isMessageArray(data)) {\n return data.map(msg => this.normalizeMessage(msg));\n }\n\n if (typeof data === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text: data }] }];\n }\n\n if (this.isSpanOutputWithToolCalls(data)) {\n const content: PostHogContent[] = [];\n if (data.text) {\n content.push({ type: 'text', text: data.text });\n }\n for (const tc of data.toolCalls) {\n content.push({\n type: 'tool-call',\n id: tc.toolCallId,\n function: { name: tc.toolName, arguments: tc.args },\n });\n }\n return [{ role: 'assistant', content }];\n }\n\n // Extract text from output objects (e.g. generation outputs with text but no tool calls)\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'text' in data) {\n const text = (data as Record<string, unknown>).text;\n if (typeof text === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text }] }];\n }\n }\n\n return [{ role: defaultRole, content: [{ type: 'text', text: this.safeStringify(data) }] }];\n }\n\n private isSpanOutputWithToolCalls(\n data: unknown,\n ): data is { text?: string; toolCalls: Array<{ toolCallId: string; toolName: string; args: unknown }> } {\n if (typeof data !== 'object' || data === null || !('toolCalls' in data)) return false;\n const { toolCalls } = data as Record<string, unknown>;\n return Array.isArray(toolCalls) && toolCalls.length > 0;\n }\n\n private isMessageArray(data: unknown): data is MastraMessage[] {\n if (!Array.isArray(data)) {\n return false;\n }\n\n return data.every(item => typeof item === 'object' && item !== null && 'role' in item && 'content' in item);\n }\n\n private normalizeMessage(msg: MastraMessage): PostHogMessage {\n if (typeof msg.content === 'string') {\n return {\n role: msg.role as PostHogMessage['role'],\n content: [{ type: 'text', text: msg.content }],\n };\n }\n\n return {\n role: msg.role as PostHogMessage['role'],\n content: msg.content as PostHogContent[],\n };\n }\n\n private safeStringify(data: unknown): string {\n try {\n return JSON.stringify(data);\n } catch {\n if (typeof data === 'object' && data !== null) {\n return `[Non-serializable ${data.constructor?.name || 'Object'}]`;\n }\n return String(data);\n }\n }\n\n /**\n * Force flush any buffered data to PostHog without shutting down.\n */\n protected override async _flush(): Promise<void> {\n if (this.#client) {\n await this.#client.flush();\n }\n }\n\n override async _postShutdown(): Promise<void> {\n if (this.#client) {\n await this.#client.shutdown();\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#client"],"sources":["../src/tracing.ts"],"sourcesContent":["import type {\n AnyExportedSpan,\n ExportedFeedback,\n FeedbackEvent,\n ModelGenerationAttributes,\n SpanErrorInfo,\n UsageStats,\n} from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { TrackingExporter } from '@mastra/observability';\nimport { PostHog } from 'posthog-node';\nimport type { EventMessage } from 'posthog-node';\n\n/**\n * Token usage format compatible with PostHog.\n * @see https://posthog.com/docs/llm-analytics/generations#event-properties\n */\nexport interface PostHogUsageMetrics {\n $ai_input_tokens?: number;\n $ai_output_tokens?: number;\n $ai_cache_read_input_tokens?: number;\n $ai_cache_creation_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to PostHog's expected property format.\n *\n * Pass through gross input token counts with cache fields as subsets.\n * PostHog subtracts cache tokens when computing costs for non-Anthropic\n * providers and detects Anthropic-style exclusive reporting on its own.\n *\n * @param usage - The UsageStats from span attributes\n * @returns PostHog-formatted usage properties\n */\nexport function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics {\n if (!usage) return {};\n\n const props: PostHogUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) {\n props.$ai_input_tokens = usage.inputTokens;\n }\n\n if (usage.inputDetails?.cacheRead !== undefined) {\n props.$ai_cache_read_input_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage.inputDetails?.cacheWrite !== undefined) {\n props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;\n }\n\n if (usage.outputTokens !== undefined) {\n props.$ai_output_tokens = usage.outputTokens;\n }\n\n return props;\n}\n\ninterface PostHogMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: PostHogContent[];\n}\n\ninterface PostHogContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface MastraMessage {\n role: string;\n content: string | MastraContent[];\n}\n\ninterface MastraContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ntype SpanData = string | MastraMessage[] | Record<string, unknown> | unknown;\n\nconst DISTINCT_ID = 'distinctId';\n\nexport interface PosthogExporterConfig extends TrackingExporterConfig {\n /** PostHog API key. Defaults to POSTHOG_API_KEY environment variable. */\n apiKey?: string;\n /** PostHog host URL. Defaults to POSTHOG_HOST environment variable or US region. */\n host?: string;\n flushAt?: number;\n flushInterval?: number;\n serverless?: boolean;\n defaultDistinctId?: string;\n enablePrivacyMode?: boolean;\n}\n\ntype PosthogRoot = unknown;\ntype PosthogSpan = AnyExportedSpan;\n// used as a placeholder for event data since we don't need to cache\n// event data for Posthog\ntype PosthogEvent = boolean;\ntype PosthogMetadata = unknown;\ntype PosthogTraceData = TraceData<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata>;\n\nexport class PosthogExporter extends TrackingExporter<\n PosthogRoot,\n PosthogSpan,\n PosthogEvent,\n PosthogMetadata,\n PosthogExporterConfig\n> {\n name = 'posthog';\n #client: PostHog | undefined;\n\n private static readonly SERVERLESS_FLUSH_AT = 10;\n private static readonly SERVERLESS_FLUSH_INTERVAL = 2000;\n private static readonly DEFAULT_FLUSH_AT = 20;\n private static readonly DEFAULT_FLUSH_INTERVAL = 10000;\n\n constructor(config: PosthogExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const apiKey = config.apiKey ?? process.env.POSTHOG_API_KEY;\n\n super({ ...config, apiKey });\n\n if (!apiKey) {\n this.setDisabled('Missing required API key. Set POSTHOG_API_KEY environment variable or pass apiKey in config.');\n return;\n }\n\n const clientConfig = this.buildClientConfig(this.config);\n this.#client = new PostHog(apiKey, clientConfig);\n const message =\n (config.serverless ?? false) ? 'PostHog exporter initialized in serverless mode' : 'PostHog exporter initialized';\n this.logger.debug(message, config);\n }\n\n private buildClientConfig(config: PosthogExporterConfig) {\n const isServerless = config.serverless ?? false;\n const flushAt =\n config.flushAt ?? (isServerless ? PosthogExporter.SERVERLESS_FLUSH_AT : PosthogExporter.DEFAULT_FLUSH_AT);\n const flushInterval =\n config.flushInterval ??\n (isServerless ? PosthogExporter.SERVERLESS_FLUSH_INTERVAL : PosthogExporter.DEFAULT_FLUSH_INTERVAL);\n\n const host = config.host || process.env.POSTHOG_HOST || 'https://us.i.posthog.com';\n\n if (!config.host && !process.env.POSTHOG_HOST) {\n this.logger.info(\n 'No PostHog host specified, using US default (https://us.i.posthog.com). ' +\n 'For EU region, set `host: \"https://eu.i.posthog.com\"` in config or POSTHOG_HOST env var. ' +\n 'For self-hosted, provide your instance URL.',\n );\n }\n\n return {\n host,\n flushAt,\n flushInterval,\n privacyMode: config.enablePrivacyMode,\n };\n }\n\n protected override skipBuildRootTask = true;\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogRoot | undefined> {\n throw new Error('Method not implemented.');\n }\n\n protected override skipCachingEventSpans = true;\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogEvent> {\n const { span, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const distinctId = this.getDistinctId(span, traceData);\n const properties = this.buildEventProperties(span, 0);\n\n this.#client?.capture(\n this.withGroups({\n distinctId,\n event: eventName,\n properties,\n timestamp: span.endTime ? new Date(span.endTime) : new Date(),\n }),\n );\n\n return true;\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogSpan | undefined> {\n const { span, traceData } = args;\n if (!traceData.hasExtraValue(DISTINCT_ID)) {\n const userId = span.metadata?.userId;\n if (userId) {\n traceData.setExtraValue(DISTINCT_ID, String(userId));\n }\n }\n\n return span;\n }\n\n protected override skipSpanUpdateEvents = true;\n protected override _updateSpan(_args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n throw new Error('Method not implemented.');\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n // Merge input from cached span (SPAN_STARTED) if not present on end span\n // This handles the case where input is only sent at start\n const cachedSpan = traceData.getSpan({ spanId: span.id });\n const mergedSpan = !span.input && cachedSpan?.input ? { ...span, input: cachedSpan.input } : span;\n\n const eventMessage = this.buildEventMessage({ span: mergedSpan, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n protected override async _abortSpan(args: {\n span: PosthogSpan;\n reason: SpanErrorInfo;\n traceData: PosthogTraceData;\n }): Promise<void> {\n const { span, reason, traceData } = args;\n\n // update span with the abort reason\n span.errorInfo = reason;\n\n const eventMessage = this.buildEventMessage({ span, traceData });\n this.#client?.capture(this.withGroups(eventMessage));\n }\n\n /**\n * Forward feedback recorded via `addFeedback()` to PostHog as a native\n * `$ai_feedback` event, shown as \"User feedback\" on the linked trace in\n * PostHog's AI observability UI.\n */\n async onFeedbackEvent(event: FeedbackEvent): Promise<void> {\n if (!this.#client) return;\n\n const { feedback } = event;\n if (!feedback.traceId) {\n this.logger.debug('PostHog exporter: dropping feedback with no traceId; PostHog requires $ai_trace_id', {\n feedbackId: feedback.feedbackId,\n });\n return;\n }\n\n const properties: Record<string, any> = {\n // Custom metadata goes first so it cannot overwrite the natively mapped fields below\n ...this.extractCustomMetadata(feedback.metadata),\n $ai_trace_id: feedback.traceId,\n // PostHog's trace UI only displays feedback events that carry $ai_feedback_text\n $ai_feedback_text: feedback.comment ?? String(feedback.value),\n feedback_id: feedback.feedbackId,\n feedback_type: feedback.feedbackType,\n feedback_value: feedback.value,\n };\n\n const feedbackSource = feedback.feedbackSource ?? feedback.source;\n if (feedbackSource) properties.feedback_source = feedbackSource;\n if (feedback.spanId) properties.span_id = feedback.spanId;\n if (feedback.sourceId) properties.source_id = feedback.sourceId;\n if (feedback.metadata?.sessionId) properties.$ai_session_id = feedback.metadata.sessionId;\n\n try {\n this.#client.capture(\n this.withGroups({\n distinctId: this.getFeedbackDistinctId(feedback),\n event: '$ai_feedback',\n properties,\n timestamp: new Date(feedback.timestamp),\n }),\n );\n } catch (err) {\n this.logger.error('PostHog exporter: failed to submit feedback', {\n error: err,\n traceId: feedback.traceId,\n feedbackId: feedback.feedbackId,\n });\n }\n }\n\n private getFeedbackDistinctId(feedback: ExportedFeedback): string {\n const userId = feedback.feedbackUserId ?? feedback.userId ?? feedback.metadata?.userId;\n if (userId) {\n return String(userId);\n }\n return this.config.defaultDistinctId ?? 'anonymous';\n }\n\n /**\n * PostHog group analytics are keyed off the top-level `groups` field on the\n * capture call. The Node SDK derives the event's `$groups` from that field and\n * overwrites any property-level `$groups`, so group metadata carried in\n * properties is dropped unless it is mirrored here.\n */\n private withGroups(message: EventMessage): EventMessage {\n const groups = message.properties?.$groups;\n if (groups && typeof groups === 'object' && !Array.isArray(groups)) {\n return { ...message, groups };\n }\n return message;\n }\n\n private buildEventMessage(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): EventMessage {\n const { span, traceData } = args;\n\n const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();\n\n const distinctId = this.getDistinctId(span, traceData);\n\n if (span.isRootSpan) {\n return this.buildRootEventMessage({ span, distinctId, endTime });\n } else {\n return this.buildChildEventMessage({ span, distinctId, endTime, traceData });\n }\n }\n\n /**\n * Capture an explicit $ai_trace event for root spans.\n * This gives us control over trace-level metadata like name and tags,\n * rather than relying on PostHog's pseudo-trace auto-creation.\n */\n private buildRootEventMessage(args: { span: AnyExportedSpan; distinctId: string; endTime: number }): EventMessage {\n const { span, distinctId, endTime } = args;\n\n // Note: We don't set $ai_latency on $ai_trace events because PostHog\n // aggregates latency from child events. Setting it here causes double-counting.\n const traceProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_span_name: span.name,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.metadata?.sessionId) {\n traceProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n if (span.input) {\n traceProperties.$ai_input_state = span.input;\n }\n\n if (span.output) {\n traceProperties.$ai_output_state = span.output;\n }\n\n if (span.errorInfo) {\n traceProperties.$ai_error = {\n message: span.errorInfo.message,\n ...(span.errorInfo.id && { id: span.errorInfo.id }),\n ...(span.errorInfo.category && { category: span.errorInfo.category }),\n };\n }\n\n // Add tags as custom properties (PostHog doesn't have native tag support on traces)\n if (span.tags?.length) {\n for (const tag of span.tags) {\n traceProperties[tag] = true;\n }\n }\n\n // Add custom metadata (excluding userId and sessionId which are handled separately)\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n Object.assign(traceProperties, customMetadata);\n\n return {\n distinctId,\n event: '$ai_trace',\n properties: traceProperties,\n timestamp: new Date(endTime),\n };\n }\n\n private buildChildEventMessage(args: {\n span: AnyExportedSpan;\n distinctId: string;\n endTime: number;\n traceData: PosthogTraceData;\n }): EventMessage {\n const { span, distinctId, endTime, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const startTime = span.startTime.getTime();\n const latency = (endTime - startTime) / 1000;\n\n // Check if parent is the root span - if so, use traceId as parent_id\n // since we don't create an $ai_span for root spans\n const parentIsRootSpan = this.isParentRootSpan(span, traceData);\n const properties = this.buildEventProperties(span, latency, parentIsRootSpan);\n\n return {\n distinctId,\n event: eventName,\n properties,\n timestamp: new Date(endTime),\n };\n }\n\n private toDate(timestamp: Date | number): Date {\n return timestamp instanceof Date ? timestamp : new Date(timestamp);\n }\n\n private mapToPostHogEvent(spanType: SpanType): string {\n if (spanType == SpanType.MODEL_GENERATION) {\n return '$ai_generation';\n }\n return '$ai_span';\n }\n\n private getDistinctId(span: AnyExportedSpan, traceData?: PosthogTraceData): string {\n if (span.metadata?.userId) {\n return String(span.metadata.userId);\n }\n\n if (traceData?.hasExtraValue(DISTINCT_ID)) {\n return String(traceData.getExtraValue(DISTINCT_ID));\n }\n\n if (this.config.defaultDistinctId) {\n return this.config.defaultDistinctId;\n }\n\n return 'anonymous';\n }\n\n /**\n * Check if the parent of this span is the root span.\n * We need this because we don't create $ai_span for root spans,\n * so children of root spans should use $ai_trace_id as their $ai_parent_id.\n */\n private isParentRootSpan(span: AnyExportedSpan, traceData: PosthogTraceData): boolean {\n if (!span.parentSpanId) {\n return false;\n }\n\n // Look up the parent span in our cache to check if it's a root span\n const parentCache = traceData.getSpan({ spanId: span.parentSpanId });\n if (parentCache) {\n return parentCache.isRootSpan;\n }\n\n // Parent not found in cache - shouldn't happen normally, but default to false\n return false;\n }\n\n private buildEventProperties(\n span: AnyExportedSpan,\n latency: number,\n parentIsRootSpan: boolean = false,\n ): Record<string, any> {\n const baseProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_latency: latency,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.parentSpanId) {\n // If parent is the root span, use trace_id as parent_id since we don't\n // create an $ai_span for root spans (only $ai_trace)\n baseProperties.$ai_parent_id = parentIsRootSpan ? span.traceId : span.parentSpanId;\n }\n\n if (span.metadata?.sessionId) {\n baseProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n // Include tags for root spans (tags are only set on root spans by design)\n // PostHog doesn't allow setting tags directly, so we iterate through each tag\n // and set it as a property with value true\n if (span.isRootSpan && span.tags?.length) {\n for (const tag of span.tags) {\n baseProperties[tag] = true;\n }\n }\n\n if (span.type === SpanType.MODEL_GENERATION) {\n baseProperties.$ai_generation_id = span.id;\n return { ...baseProperties, ...this.buildGenerationProperties(span) };\n } else {\n baseProperties.$ai_span_id = span.id;\n baseProperties.$ai_span_name = span.name;\n return { ...baseProperties, ...this.buildSpanProperties(span) };\n }\n }\n\n private extractErrorProperties(errorInfo?: SpanErrorInfo): Record<string, any> {\n if (!errorInfo) {\n return {};\n }\n\n const props: Record<string, string> = {\n error_message: errorInfo.message,\n };\n\n if (errorInfo.id) {\n props.error_id = errorInfo.id;\n }\n\n if (errorInfo.category) {\n props.error_category = errorInfo.category;\n }\n\n return props;\n }\n\n private extractCustomMetadata(metadata?: Record<string, unknown>): Record<string, any> {\n const { userId, sessionId, ...customMetadata } = metadata ?? {};\n return customMetadata;\n }\n\n private buildGenerationProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n const attrs = (span.attributes ?? {}) as ModelGenerationAttributes;\n\n props.$ai_model = attrs.model || 'unknown-model';\n props.$ai_provider = attrs.provider || 'unknown-provider';\n\n if (span.input) props.$ai_input = this.formatMessages(span.input, 'user');\n if (span.output) props.$ai_output_choices = this.formatMessages(span.output, 'assistant');\n\n // Extract usage properties using the shared utility\n Object.assign(props, formatUsageMetrics(attrs.usage));\n\n if (attrs.parameters) {\n if (attrs.parameters.temperature !== undefined) props.$ai_temperature = attrs.parameters.temperature;\n if (attrs.parameters.maxOutputTokens !== undefined) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;\n }\n if (attrs.streaming !== undefined) props.$ai_stream = attrs.streaming;\n if (attrs.tools?.length) {\n // OpenAI-style shape — the format PostHog's own AI SDKs send and its\n // trace view renders. Provider-defined tools pass through as-is.\n props.$ai_tools = attrs.tools.map(tool =>\n tool.type === 'function'\n ? {\n type: 'function',\n function: { name: tool.name, description: tool.description, parameters: tool.parameters },\n }\n : tool,\n );\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private buildSpanProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n\n if (span.input) props.$ai_input_state = span.input;\n if (span.output) props.$ai_output_state = span.output;\n\n if (span.type === SpanType.MODEL_CHUNK) {\n const attrs = span.attributes as any;\n if (attrs?.chunkType) props.chunk_type = attrs.chunkType;\n if (attrs?.sequenceNumber !== undefined) props.chunk_sequence_number = attrs.sequenceNumber;\n }\n\n if (span.attributes) {\n Object.assign(props, span.attributes);\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span.metadata) };\n }\n\n private formatMessages(data: SpanData, defaultRole: 'user' | 'assistant' = 'user'): PostHogMessage[] {\n // Unwrap {messages: [...]} wrapper produced by generation span inputs\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'messages' in data) {\n const wrapped = (data as Record<string, unknown>).messages;\n if (this.isMessageArray(wrapped)) {\n return wrapped.map(msg => this.normalizeMessage(msg));\n }\n }\n\n if (this.isMessageArray(data)) {\n return data.map(msg => this.normalizeMessage(msg));\n }\n\n if (typeof data === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text: data }] }];\n }\n\n if (this.isSpanOutputWithToolCalls(data)) {\n const content: PostHogContent[] = [];\n if (data.text) {\n content.push({ type: 'text', text: data.text });\n }\n for (const tc of data.toolCalls) {\n content.push({\n type: 'tool-call',\n id: tc.toolCallId,\n function: { name: tc.toolName, arguments: tc.args },\n });\n }\n return [{ role: 'assistant', content }];\n }\n\n // Extract text from output objects (e.g. generation outputs with text but no tool calls)\n if (typeof data === 'object' && data !== null && !Array.isArray(data) && 'text' in data) {\n const text = (data as Record<string, unknown>).text;\n if (typeof text === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text }] }];\n }\n }\n\n return [{ role: defaultRole, content: [{ type: 'text', text: this.safeStringify(data) }] }];\n }\n\n private isSpanOutputWithToolCalls(\n data: unknown,\n ): data is { text?: string; toolCalls: Array<{ toolCallId: string; toolName: string; args: unknown }> } {\n if (typeof data !== 'object' || data === null || !('toolCalls' in data)) return false;\n const { toolCalls } = data as Record<string, unknown>;\n return Array.isArray(toolCalls) && toolCalls.length > 0;\n }\n\n private isMessageArray(data: unknown): data is MastraMessage[] {\n if (!Array.isArray(data)) {\n return false;\n }\n\n return data.every(item => typeof item === 'object' && item !== null && 'role' in item && 'content' in item);\n }\n\n private normalizeMessage(msg: MastraMessage): PostHogMessage {\n if (typeof msg.content === 'string') {\n return {\n role: msg.role as PostHogMessage['role'],\n content: [{ type: 'text', text: msg.content }],\n };\n }\n\n return {\n role: msg.role as PostHogMessage['role'],\n content: msg.content as PostHogContent[],\n };\n }\n\n private safeStringify(data: unknown): string {\n try {\n return JSON.stringify(data);\n } catch {\n if (typeof data === 'object' && data !== null) {\n return `[Non-serializable ${data.constructor?.name || 'Object'}]`;\n }\n return String(data);\n }\n }\n\n /**\n * Force flush any buffered data to PostHog without shutting down.\n */\n protected override async _flush(): Promise<void> {\n if (this.#client) {\n await this.#client.flush();\n }\n }\n\n override async _postShutdown(): Promise<void> {\n if (this.#client) {\n await this.#client.shutdown();\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAEf,aAAa,EAEb,aAAa,EACb,UAAU,EACX,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,+BAA+B,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,mBAAmB,CAsB1E;AA4BD,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,KAAK,WAAW,GAAG,OAAO,CAAC;AAC3B,KAAK,WAAW,GAAG,eAAe,CAAC;AAGnC,KAAK,YAAY,GAAG,OAAO,CAAC;AAC5B,KAAK,eAAe,GAAG,OAAO,CAAC;AAC/B,KAAK,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;AAE3F,qBAAa,eAAgB,SAAQ,gBAAgB,CACnD,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,EACf,qBAAqB,CACtB;;IACC,IAAI,SAAa;IAGjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAM;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAQ;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAE3C,MAAM,GAAE,qBAA0B;IAkB9C,OAAO,CAAC,iBAAiB;IA0BzB,UAAmB,iBAAiB,UAAQ;cACnB,UAAU,CAAC,KAAK,EAAE;QACzC,IAAI,EAAE,eAAe,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAIpC,UAAmB,qBAAqB,UAAQ;cACvB,WAAW,CAAC,IAAI,EAAE;QACzC,IAAI,EAAE,eAAe,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,YAAY,CAAC;cAmBA,UAAU,CAAC,IAAI,EAAE;QACxC,IAAI,EAAE,eAAe,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAYpC,UAAmB,oBAAoB,UAAQ;cAC5B,WAAW,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,SAAS,EAAE,gBAAgB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAInF,WAAW,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,SAAS,EAAE,gBAAgB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAYxF,UAAU,CAAC,IAAI,EAAE;QACxC,IAAI,EAAE,WAAW,CAAC;QAClB,MAAM,EAAE,aAAa,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjB;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C1D,OAAO,CAAC,qBAAqB;IAQ7B;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,iBAAiB;IAczB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAkD7B,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,aAAa;IAgBrB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,oBAAoB;IAwC5B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,yBAAyB;IAsBjC,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,cAAc;IA2CtB,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,aAAa;IAWrB;;OAEG;cACsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9C"}
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAEf,aAAa,EAEb,aAAa,EACb,UAAU,EACX,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,+BAA+B,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,mBAAmB,CAsB1E;AA4BD,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,KAAK,WAAW,GAAG,OAAO,CAAC;AAC3B,KAAK,WAAW,GAAG,eAAe,CAAC;AAGnC,KAAK,YAAY,GAAG,OAAO,CAAC;AAC5B,KAAK,eAAe,GAAG,OAAO,CAAC;AAC/B,KAAK,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;AAE3F,qBAAa,eAAgB,SAAQ,gBAAgB,CACnD,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,EACf,qBAAqB,CACtB;;IACC,IAAI,SAAa;IAGjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAM;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAQ;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAE3C,MAAM,GAAE,qBAA0B;IAkB9C,OAAO,CAAC,iBAAiB;IA0BzB,UAAmB,iBAAiB,UAAQ;cACnB,UAAU,CAAC,KAAK,EAAE;QACzC,IAAI,EAAE,eAAe,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAIpC,UAAmB,qBAAqB,UAAQ;cACvB,WAAW,CAAC,IAAI,EAAE;QACzC,IAAI,EAAE,eAAe,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,YAAY,CAAC;cAmBA,UAAU,CAAC,IAAI,EAAE;QACxC,IAAI,EAAE,eAAe,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAYpC,UAAmB,oBAAoB,UAAQ;cAC5B,WAAW,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,SAAS,EAAE,gBAAgB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAInF,WAAW,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,SAAS,EAAE,gBAAgB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAYxF,UAAU,CAAC,IAAI,EAAE;QACxC,IAAI,EAAE,WAAW,CAAC;QAClB,MAAM,EAAE,aAAa,CAAC;QACtB,SAAS,EAAE,gBAAgB,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjB;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C1D,OAAO,CAAC,qBAAqB;IAQ7B;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,iBAAiB;IAczB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAkD7B,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,aAAa;IAgBrB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,oBAAoB;IAwC5B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,yBAAyB;IAkCjC,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,cAAc;IA2CtB,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,aAAa;IAWrB;;OAEG;cACsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/posthog",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "PostHog observability provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
27
  "posthog-node": "^5.37.0",
28
- "@mastra/observability": "1.16.4"
28
+ "@mastra/observability": "1.16.5"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "22.20.1",
@@ -33,10 +33,10 @@
33
33
  "tsdown": "0.22.9",
34
34
  "typescript": "^6.0.3",
35
35
  "vitest": "4.1.10",
36
- "@internal/types-builder": "0.0.95",
37
- "@observability/test-utils": "0.1.0",
38
- "@internal/lint": "0.0.120",
39
- "@mastra/core": "1.56.0"
36
+ "@internal/lint": "0.0.121",
37
+ "@mastra/core": "1.57.0",
38
+ "@internal/types-builder": "0.0.96",
39
+ "@observability/test-utils": "0.1.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@mastra/core": ">=1.16.0-0 <2.0.0-0"