@dxos/observability 0.13.0 → 0.14.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.
Files changed (33) hide show
  1. package/dist/lib/Observability.mjs +7 -2
  2. package/dist/lib/Observability.mjs.map +1 -1
  3. package/dist/lib/chunk-ObservabilityExtension.mjs +12 -10
  4. package/dist/lib/chunk-ObservabilityExtension.mjs.map +1 -1
  5. package/dist/lib/chunk-OtelSpanSink.mjs +5 -1
  6. package/dist/lib/chunk-OtelSpanSink.mjs.map +1 -1
  7. package/dist/types/src/Observability.d.ts.map +1 -1
  8. package/dist/types/src/Observability.js +7 -0
  9. package/dist/types/src/Observability.js.map +1 -1
  10. package/dist/types/src/Observability.test.js +13 -1
  11. package/dist/types/src/Observability.test.js.map +1 -1
  12. package/dist/types/src/ObservabilityExtension.d.ts +4 -0
  13. package/dist/types/src/ObservabilityExtension.d.ts.map +1 -1
  14. package/dist/types/src/extensions/otel/tail-sampling.d.ts +2 -0
  15. package/dist/types/src/extensions/otel/tail-sampling.d.ts.map +1 -1
  16. package/dist/types/src/extensions/otel/tail-sampling.js +5 -0
  17. package/dist/types/src/extensions/otel/tail-sampling.js.map +1 -1
  18. package/dist/types/src/extensions/otel/tail-sampling.test.js +5 -0
  19. package/dist/types/src/extensions/otel/tail-sampling.test.js.map +1 -1
  20. package/dist/types/src/extensions/posthog/extension.d.ts.map +1 -1
  21. package/dist/types/src/extensions/posthog/extension.js +5 -2
  22. package/dist/types/src/extensions/posthog/extension.js.map +1 -1
  23. package/dist/types/src/version.d.ts +1 -1
  24. package/dist/types/src/version.js +1 -1
  25. package/dist/types/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +19 -19
  27. package/src/Observability.test.ts +15 -1
  28. package/src/Observability.ts +7 -0
  29. package/src/ObservabilityExtension.ts +5 -0
  30. package/src/extensions/otel/tail-sampling.test.ts +6 -0
  31. package/src/extensions/otel/tail-sampling.ts +7 -0
  32. package/src/extensions/posthog/extension.ts +10 -2
  33. package/src/version.ts +1 -1
@@ -165,6 +165,11 @@ var ObservabilityImpl = class {
165
165
  for (const extension of this._getExtensions("support")) key = await extension.uploadLogs() ?? key;
166
166
  return key;
167
167
  },
168
+ uploadNdjson: async (ndjson, kind) => {
169
+ let key;
170
+ for (const extension of this._getExtensions("support")) key = await extension.uploadNdjson(ndjson, kind) ?? key;
171
+ return key;
172
+ },
168
173
  sessionContext: () => {
169
174
  for (const extension of this._getExtensions("support")) {
170
175
  const context = extension.sessionContext();
@@ -214,7 +219,7 @@ var addExtension = (_extension) => Effect.fn(function* (_observability) {
214
219
  invariant("_addExtension" in observability && typeof observability._addExtension === "function", void 0, {
215
220
  "~LogMeta": "~LogMeta",
216
221
  F: __dxlog_file,
217
- L: 360,
222
+ L: 367,
218
223
  S: this,
219
224
  A: ["'_addExtension' in observability && typeof observability._addExtension === 'function'", ""]
220
225
  });
@@ -226,7 +231,7 @@ var addDataProvider = (dataProvider) => Effect.fn(function* (_observability) {
226
231
  invariant("_addDataProvider" in observability && typeof observability._addDataProvider === "function", void 0, {
227
232
  "~LogMeta": "~LogMeta",
228
233
  F: __dxlog_file,
229
- L: 368,
234
+ L: 375,
230
235
  S: this,
231
236
  A: ["'_addDataProvider' in observability && typeof observability._addDataProvider === 'function'", ""]
232
237
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Observability.mjs","names":[],"sources":["../../src/Observability.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Array from 'effect/Array';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\n\nimport { type CleanupFn, SubscriptionList } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport * as ObservabilityExtension from './ObservabilityExtension.ts';\n\nexport * from './storage/index.ts';\n\nconst attachAiCapture = async (observability: Observability): Promise<CleanupFn> => {\n const { AiSpanProcessor, contentCaptureAllowed } = await import('./ai/AiObservability.ts');\n const { Otel } = await import('./extensions/index.ts');\n return Otel.addSpanProcessor(\n new AiSpanProcessor({\n captureInference: (inference) => observability.ai.captureInference(inference),\n captureTurn: (turn) => observability.ai.captureTurn(turn),\n captureToolCall: (toolCall) => observability.ai.captureToolCall(toolCall),\n captureEnabled: () => observability.enabled,\n allowContent: contentCaptureAllowed,\n }),\n );\n};\n\n// TODO(wittjosiah): Figure out how to handle when telemetry is disabled.\n// In theory the setting should be both persisted and synchronized.\n// Initialize probably should still run for the cases where data is emitted manually (e.g., support tickets).\n\n/**\n * Provider of observability data.\n */\nexport type DataProvider = (observability: Observability) => Effect.Effect<CleanupFn | void, Error>;\n\n/**\n * Extensible observability provider.\n */\n// TODO(wittjosiah): Add pipe method.\nexport interface Observability {\n initialize(): Effect.Effect<void>;\n close(): Effect.Effect<void>;\n enable(): Effect.Effect<void>;\n disable(): Effect.Effect<void>;\n flush(): Effect.Effect<void>;\n addDataProvider(dataProvider: DataProvider): Effect.Effect<void, Error>;\n identify(\n distinctId: string,\n attributes?: ObservabilityExtension.Attributes,\n setOnceAttributes?: ObservabilityExtension.Attributes,\n ): void;\n alias(distinctId: string, previousId?: string): void;\n setTags(tags: ObservabilityExtension.Attributes, kind?: ObservabilityExtension.Kind): void;\n enabled: boolean;\n errors: ObservabilityExtension.Errors;\n events: ObservabilityExtension.Events;\n support: ObservabilityExtension.Support;\n ai: ObservabilityExtension.Ai;\n mcp: ObservabilityExtension.Mcp;\n /** True if at least one extension of the given kind reports as available. */\n isAvailable(kind: ObservabilityExtension.Kind): Effect.Effect<boolean>;\n metrics: ObservabilityExtension.Metrics;\n}\n\nclass ObservabilityImpl implements Observability {\n private _initialized = false;\n private readonly _extensions: ObservabilityExtension.Extension[] = [];\n private readonly _dataProviders: DataProvider[] = [];\n private readonly _subscriptions = new SubscriptionList();\n\n initialize(): Effect.Effect<void> {\n if (this._initialized) {\n return Effect.succeed(undefined);\n }\n\n const initializedExtensions: ObservabilityExtension.Extension[] = [];\n\n return Effect.gen({ self: this }, function* () {\n const context: ObservabilityExtension.ExtensionContext = {\n setTags: (tags, kind) => this.setTags(tags, kind),\n };\n for (const extension of this._extensions) {\n if (extension.initialize) {\n yield* extension.initialize(context);\n }\n initializedExtensions.push(extension);\n }\n\n const cleanups = yield* Effect.all(this._dataProviders.map((provider) => provider(this)));\n this._subscriptions.add(...cleanups.filter((cleanup) => cleanup !== undefined));\n this._subscriptions.add(yield* Effect.promise(() => attachAiCapture(this)));\n this._initialized = true;\n }).pipe(\n Effect.catch((error) =>\n Effect.gen({ self: this }, function* () {\n log.catch(error);\n // Roll back already-initialized extensions.\n for (const extension of initializedExtensions) {\n if (extension.close) {\n yield* extension.close().pipe(Effect.catch(() => Effect.succeed(undefined)));\n }\n }\n this._subscriptions.clear();\n }),\n ),\n );\n }\n\n close(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n this._subscriptions.clear();\n this._dataProviders.length = 0;\n for (const extension of this._extensions) {\n if (extension.close) {\n yield* extension.close();\n }\n }\n this._initialized = false;\n });\n }\n\n enable(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n for (const extension of this._extensions) {\n if (extension.enable) {\n yield* extension.enable();\n }\n }\n });\n }\n\n disable(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n for (const extension of this._extensions) {\n if (extension.disable) {\n yield* extension.disable();\n }\n }\n });\n }\n\n flush(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n for (const extension of this._extensions) {\n if (extension.flush) {\n yield* extension.flush();\n }\n }\n });\n }\n\n _addExtension(extension: ObservabilityExtension.Extension): void {\n invariant(!this._initialized, 'Observability is already initialized');\n this._extensions.push(extension);\n }\n\n _addDataProvider(dataProvider: DataProvider): void {\n invariant(!this._initialized, 'Observability is already initialized');\n this._dataProviders.push(dataProvider);\n }\n\n /**\n * Adds a data provider and initializes it.\n */\n addDataProvider(dataProvider: DataProvider): Effect.Effect<void, Error> {\n return Effect.gen({ self: this }, function* () {\n this._dataProviders.push(dataProvider);\n const cleanup = yield* dataProvider(this);\n if (cleanup) {\n this._subscriptions.add(cleanup);\n }\n });\n }\n\n identify(\n distinctId: string,\n attributes?: ObservabilityExtension.Attributes,\n setOnceAttributes?: ObservabilityExtension.Attributes,\n ): void {\n for (const extension of this._extensions) {\n extension.identify?.(distinctId, attributes, setOnceAttributes);\n }\n }\n\n alias(distinctId: string, previousId?: string): void {\n for (const extension of this._extensions) {\n extension.alias?.(distinctId, previousId);\n }\n }\n\n setTags(tags: ObservabilityExtension.Attributes, kind?: ObservabilityExtension.Kind): void {\n for (const extension of this._extensions) {\n if (kind && !extension.apis.some((api) => api.kind === kind)) {\n continue;\n }\n\n const processedTags = Object.fromEntries(\n Object.entries(tags)\n .filter((entry): entry is [string, string | number | boolean] => entry[1] !== undefined)\n .map(([key, value]) => [key, value.toString()]),\n );\n extension.setTags?.(processedTags, kind);\n }\n }\n\n get enabled(): boolean {\n return this._extensions.every((extension) => extension.enabled);\n }\n\n get errors(): ObservabilityExtension.Errors {\n return {\n captureException: (error, attributes) => {\n for (const extension of this._getExtensions('errors')) {\n extension.captureException(error, attributes);\n }\n },\n };\n }\n\n get events(): ObservabilityExtension.Events {\n return {\n captureEvent: (event, attributes) => {\n for (const extension of this._getExtensions('events')) {\n extension.captureEvent(event, attributes);\n }\n },\n };\n }\n\n get mcp(): ObservabilityExtension.Mcp {\n return {\n captureInitialize: (client) => {\n for (const extension of this._getExtensions('mcp')) {\n extension.captureInitialize(client);\n }\n },\n captureToolCall: (call) => {\n for (const extension of this._getExtensions('mcp')) {\n extension.captureToolCall(call);\n }\n },\n };\n }\n\n get ai(): ObservabilityExtension.Ai {\n return {\n captureInference: (inference) => {\n for (const extension of this._getExtensions('ai')) {\n extension.captureInference(inference);\n }\n },\n captureTurn: (turn) => {\n for (const extension of this._getExtensions('ai')) {\n extension.captureTurn(turn);\n }\n },\n captureToolCall: (toolCall) => {\n for (const extension of this._getExtensions('ai')) {\n extension.captureToolCall(toolCall);\n }\n },\n };\n }\n\n get support(): ObservabilityExtension.Support {\n return {\n uploadLogs: async () => {\n let key: string | undefined;\n for (const extension of this._getExtensions('support')) {\n key = (await extension.uploadLogs()) ?? key;\n }\n return key;\n },\n sessionContext: () => {\n for (const extension of this._getExtensions('support')) {\n const context = extension.sessionContext();\n if (context) {\n return context;\n }\n }\n return undefined;\n },\n flushLogs: async (attributes) => {\n for (const extension of this._getExtensions('support')) {\n await extension.flushLogs(attributes);\n }\n },\n };\n }\n\n isAvailable(kind: ObservabilityExtension.Kind): Effect.Effect<boolean> {\n const apis = this._getExtensions(kind);\n if (apis.length === 0) {\n return Effect.succeed(false);\n }\n return Effect.gen({ self: this }, function* () {\n for (const api of apis) {\n const available = yield* api.isAvailable();\n if (available) {\n return true;\n }\n }\n return false;\n });\n }\n\n get metrics(): ObservabilityExtension.Metrics {\n return {\n gauge: (name, value, attributes, meta) => {\n for (const extension of this._getExtensions('metrics')) {\n extension.gauge(name, value, attributes, meta);\n }\n },\n increment: (name, value, attributes, meta) => {\n for (const extension of this._getExtensions('metrics')) {\n extension.increment(name, value, attributes, meta);\n }\n },\n distribution: (name, value, attributes, meta) => {\n for (const extension of this._getExtensions('metrics')) {\n extension.distribution(name, value, attributes, meta);\n }\n },\n observe: (name, callback, attributes, meta) => {\n const cleanups = this._getExtensions('metrics').map((extension) =>\n extension.observe(name, callback, attributes, meta),\n );\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n },\n };\n }\n\n private _getExtensions<T extends ObservabilityExtension.Kind>(\n kind: T,\n ): Extract<ObservabilityExtension.ExtensionApi, { kind: T }>[] {\n return Function.pipe(\n this._extensions,\n Array.flatMap((extension) => extension.apis),\n Array.filter((api): api is Extract<ObservabilityExtension.ExtensionApi, { kind: T }> => api.kind === kind),\n );\n }\n}\n\nexport const make = (): Effect.Effect<Observability> => Effect.succeed(new ObservabilityImpl());\n\nexport const addExtension = (_extension: Effect.Effect<ObservabilityExtension.Extension>) =>\n Effect.fn(function* (_observability: Effect.Effect<Observability>) {\n const observability = yield* _observability;\n const extension = yield* _extension;\n invariant('_addExtension' in observability && typeof observability._addExtension === 'function');\n observability._addExtension(extension);\n return observability;\n });\n\nexport const addDataProvider = (dataProvider: DataProvider) =>\n Effect.fn(function* (_observability: Effect.Effect<Observability>) {\n const observability = yield* _observability;\n invariant('_addDataProvider' in observability && typeof observability._addDataProvider === 'function');\n observability._addDataProvider(dataProvider);\n return observability;\n });\n\nexport const initialize = Effect.fn(function* (_observability: Effect.Effect<Observability>) {\n const observability = yield* _observability;\n yield* observability.initialize();\n return observability;\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"Observability.mjs","names":[],"sources":["../../src/Observability.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Array from 'effect/Array';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\n\nimport { type CleanupFn, SubscriptionList } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport * as ObservabilityExtension from './ObservabilityExtension.ts';\n\nexport * from './storage/index.ts';\n\nconst attachAiCapture = async (observability: Observability): Promise<CleanupFn> => {\n const { AiSpanProcessor, contentCaptureAllowed } = await import('./ai/AiObservability.ts');\n const { Otel } = await import('./extensions/index.ts');\n return Otel.addSpanProcessor(\n new AiSpanProcessor({\n captureInference: (inference) => observability.ai.captureInference(inference),\n captureTurn: (turn) => observability.ai.captureTurn(turn),\n captureToolCall: (toolCall) => observability.ai.captureToolCall(toolCall),\n captureEnabled: () => observability.enabled,\n allowContent: contentCaptureAllowed,\n }),\n );\n};\n\n// TODO(wittjosiah): Figure out how to handle when telemetry is disabled.\n// In theory the setting should be both persisted and synchronized.\n// Initialize probably should still run for the cases where data is emitted manually (e.g., support tickets).\n\n/**\n * Provider of observability data.\n */\nexport type DataProvider = (observability: Observability) => Effect.Effect<CleanupFn | void, Error>;\n\n/**\n * Extensible observability provider.\n */\n// TODO(wittjosiah): Add pipe method.\nexport interface Observability {\n initialize(): Effect.Effect<void>;\n close(): Effect.Effect<void>;\n enable(): Effect.Effect<void>;\n disable(): Effect.Effect<void>;\n flush(): Effect.Effect<void>;\n addDataProvider(dataProvider: DataProvider): Effect.Effect<void, Error>;\n identify(\n distinctId: string,\n attributes?: ObservabilityExtension.Attributes,\n setOnceAttributes?: ObservabilityExtension.Attributes,\n ): void;\n alias(distinctId: string, previousId?: string): void;\n setTags(tags: ObservabilityExtension.Attributes, kind?: ObservabilityExtension.Kind): void;\n enabled: boolean;\n errors: ObservabilityExtension.Errors;\n events: ObservabilityExtension.Events;\n support: ObservabilityExtension.Support;\n ai: ObservabilityExtension.Ai;\n mcp: ObservabilityExtension.Mcp;\n /** True if at least one extension of the given kind reports as available. */\n isAvailable(kind: ObservabilityExtension.Kind): Effect.Effect<boolean>;\n metrics: ObservabilityExtension.Metrics;\n}\n\nclass ObservabilityImpl implements Observability {\n private _initialized = false;\n private readonly _extensions: ObservabilityExtension.Extension[] = [];\n private readonly _dataProviders: DataProvider[] = [];\n private readonly _subscriptions = new SubscriptionList();\n\n initialize(): Effect.Effect<void> {\n if (this._initialized) {\n return Effect.succeed(undefined);\n }\n\n const initializedExtensions: ObservabilityExtension.Extension[] = [];\n\n return Effect.gen({ self: this }, function* () {\n const context: ObservabilityExtension.ExtensionContext = {\n setTags: (tags, kind) => this.setTags(tags, kind),\n };\n for (const extension of this._extensions) {\n if (extension.initialize) {\n yield* extension.initialize(context);\n }\n initializedExtensions.push(extension);\n }\n\n const cleanups = yield* Effect.all(this._dataProviders.map((provider) => provider(this)));\n this._subscriptions.add(...cleanups.filter((cleanup) => cleanup !== undefined));\n this._subscriptions.add(yield* Effect.promise(() => attachAiCapture(this)));\n this._initialized = true;\n }).pipe(\n Effect.catch((error) =>\n Effect.gen({ self: this }, function* () {\n log.catch(error);\n // Roll back already-initialized extensions.\n for (const extension of initializedExtensions) {\n if (extension.close) {\n yield* extension.close().pipe(Effect.catch(() => Effect.succeed(undefined)));\n }\n }\n this._subscriptions.clear();\n }),\n ),\n );\n }\n\n close(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n this._subscriptions.clear();\n this._dataProviders.length = 0;\n for (const extension of this._extensions) {\n if (extension.close) {\n yield* extension.close();\n }\n }\n this._initialized = false;\n });\n }\n\n enable(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n for (const extension of this._extensions) {\n if (extension.enable) {\n yield* extension.enable();\n }\n }\n });\n }\n\n disable(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n for (const extension of this._extensions) {\n if (extension.disable) {\n yield* extension.disable();\n }\n }\n });\n }\n\n flush(): Effect.Effect<void> {\n return Effect.gen({ self: this }, function* () {\n for (const extension of this._extensions) {\n if (extension.flush) {\n yield* extension.flush();\n }\n }\n });\n }\n\n _addExtension(extension: ObservabilityExtension.Extension): void {\n invariant(!this._initialized, 'Observability is already initialized');\n this._extensions.push(extension);\n }\n\n _addDataProvider(dataProvider: DataProvider): void {\n invariant(!this._initialized, 'Observability is already initialized');\n this._dataProviders.push(dataProvider);\n }\n\n /**\n * Adds a data provider and initializes it.\n */\n addDataProvider(dataProvider: DataProvider): Effect.Effect<void, Error> {\n return Effect.gen({ self: this }, function* () {\n this._dataProviders.push(dataProvider);\n const cleanup = yield* dataProvider(this);\n if (cleanup) {\n this._subscriptions.add(cleanup);\n }\n });\n }\n\n identify(\n distinctId: string,\n attributes?: ObservabilityExtension.Attributes,\n setOnceAttributes?: ObservabilityExtension.Attributes,\n ): void {\n for (const extension of this._extensions) {\n extension.identify?.(distinctId, attributes, setOnceAttributes);\n }\n }\n\n alias(distinctId: string, previousId?: string): void {\n for (const extension of this._extensions) {\n extension.alias?.(distinctId, previousId);\n }\n }\n\n setTags(tags: ObservabilityExtension.Attributes, kind?: ObservabilityExtension.Kind): void {\n for (const extension of this._extensions) {\n if (kind && !extension.apis.some((api) => api.kind === kind)) {\n continue;\n }\n\n const processedTags = Object.fromEntries(\n Object.entries(tags)\n .filter((entry): entry is [string, string | number | boolean] => entry[1] !== undefined)\n .map(([key, value]) => [key, value.toString()]),\n );\n extension.setTags?.(processedTags, kind);\n }\n }\n\n get enabled(): boolean {\n return this._extensions.every((extension) => extension.enabled);\n }\n\n get errors(): ObservabilityExtension.Errors {\n return {\n captureException: (error, attributes) => {\n for (const extension of this._getExtensions('errors')) {\n extension.captureException(error, attributes);\n }\n },\n };\n }\n\n get events(): ObservabilityExtension.Events {\n return {\n captureEvent: (event, attributes) => {\n for (const extension of this._getExtensions('events')) {\n extension.captureEvent(event, attributes);\n }\n },\n };\n }\n\n get mcp(): ObservabilityExtension.Mcp {\n return {\n captureInitialize: (client) => {\n for (const extension of this._getExtensions('mcp')) {\n extension.captureInitialize(client);\n }\n },\n captureToolCall: (call) => {\n for (const extension of this._getExtensions('mcp')) {\n extension.captureToolCall(call);\n }\n },\n };\n }\n\n get ai(): ObservabilityExtension.Ai {\n return {\n captureInference: (inference) => {\n for (const extension of this._getExtensions('ai')) {\n extension.captureInference(inference);\n }\n },\n captureTurn: (turn) => {\n for (const extension of this._getExtensions('ai')) {\n extension.captureTurn(turn);\n }\n },\n captureToolCall: (toolCall) => {\n for (const extension of this._getExtensions('ai')) {\n extension.captureToolCall(toolCall);\n }\n },\n };\n }\n\n get support(): ObservabilityExtension.Support {\n return {\n uploadLogs: async () => {\n let key: string | undefined;\n for (const extension of this._getExtensions('support')) {\n key = (await extension.uploadLogs()) ?? key;\n }\n return key;\n },\n uploadNdjson: async (ndjson, kind) => {\n let key: string | undefined;\n for (const extension of this._getExtensions('support')) {\n key = (await extension.uploadNdjson(ndjson, kind)) ?? key;\n }\n return key;\n },\n sessionContext: () => {\n for (const extension of this._getExtensions('support')) {\n const context = extension.sessionContext();\n if (context) {\n return context;\n }\n }\n return undefined;\n },\n flushLogs: async (attributes) => {\n for (const extension of this._getExtensions('support')) {\n await extension.flushLogs(attributes);\n }\n },\n };\n }\n\n isAvailable(kind: ObservabilityExtension.Kind): Effect.Effect<boolean> {\n const apis = this._getExtensions(kind);\n if (apis.length === 0) {\n return Effect.succeed(false);\n }\n return Effect.gen({ self: this }, function* () {\n for (const api of apis) {\n const available = yield* api.isAvailable();\n if (available) {\n return true;\n }\n }\n return false;\n });\n }\n\n get metrics(): ObservabilityExtension.Metrics {\n return {\n gauge: (name, value, attributes, meta) => {\n for (const extension of this._getExtensions('metrics')) {\n extension.gauge(name, value, attributes, meta);\n }\n },\n increment: (name, value, attributes, meta) => {\n for (const extension of this._getExtensions('metrics')) {\n extension.increment(name, value, attributes, meta);\n }\n },\n distribution: (name, value, attributes, meta) => {\n for (const extension of this._getExtensions('metrics')) {\n extension.distribution(name, value, attributes, meta);\n }\n },\n observe: (name, callback, attributes, meta) => {\n const cleanups = this._getExtensions('metrics').map((extension) =>\n extension.observe(name, callback, attributes, meta),\n );\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n },\n };\n }\n\n private _getExtensions<T extends ObservabilityExtension.Kind>(\n kind: T,\n ): Extract<ObservabilityExtension.ExtensionApi, { kind: T }>[] {\n return Function.pipe(\n this._extensions,\n Array.flatMap((extension) => extension.apis),\n Array.filter((api): api is Extract<ObservabilityExtension.ExtensionApi, { kind: T }> => api.kind === kind),\n );\n }\n}\n\nexport const make = (): Effect.Effect<Observability> => Effect.succeed(new ObservabilityImpl());\n\nexport const addExtension = (_extension: Effect.Effect<ObservabilityExtension.Extension>) =>\n Effect.fn(function* (_observability: Effect.Effect<Observability>) {\n const observability = yield* _observability;\n const extension = yield* _extension;\n invariant('_addExtension' in observability && typeof observability._addExtension === 'function');\n observability._addExtension(extension);\n return observability;\n });\n\nexport const addDataProvider = (dataProvider: DataProvider) =>\n Effect.fn(function* (_observability: Effect.Effect<Observability>) {\n const observability = yield* _observability;\n invariant('_addDataProvider' in observability && typeof observability._addDataProvider === 'function');\n observability._addDataProvider(dataProvider);\n return observability;\n });\n\nexport const initialize = Effect.fn(function* (_observability: Effect.Effect<Observability>) {\n const observability = yield* _observability;\n yield* observability.initialize();\n return observability;\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,IAAM,kBAAkB,OAAO,kBAAqD;CAClF,MAAM,EAAE,iBAAiB,0BAA0B,MAAM,OAAO;CAChE,MAAM,EAAE,SAAS,MAAM,OAAO,yBAAA,CAAA,MAAA,MAAA,EAAA,CAAA;CAC9B,OAAO,KAAK,iBACV,IAAI,gBAAgB;EAClB,mBAAmB,cAAc,cAAc,GAAG,iBAAiB,SAAS;EAC5E,cAAc,SAAS,cAAc,GAAG,YAAY,IAAI;EACxD,kBAAkB,aAAa,cAAc,GAAG,gBAAgB,QAAQ;EACxE,sBAAsB,cAAc;EACpC,cAAc;CAChB,CAAC,CACH;AACF;AAwCA,IAAM,oBAAN,MAAiD;CAC/C,eAAuB;CACvB,cAAmE,CAAC;CACpE,iBAAkD,CAAC;CACnD,iBAAkC,IAAI,iBAAiB;CAEvD,aAAkC;EAChC,IAAI,KAAK,cACP,OAAO,OAAO,QAAQ,KAAA,CAAS;EAGjC,MAAM,wBAA4D,CAAC;EAEnE,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,MAAM,UAAmD,EACvD,UAAU,MAAM,SAAS,KAAK,QAAQ,MAAM,IAAI,EAClD;GACA,KAAK,MAAM,aAAa,KAAK,aAAa;IACxC,IAAI,UAAU,YACZ,OAAO,UAAU,WAAW,OAAO;IAErC,sBAAsB,KAAK,SAAS;GACtC;GAEA,MAAM,WAAW,OAAO,OAAO,IAAI,KAAK,eAAe,KAAK,aAAa,SAAS,IAAI,CAAC,CAAC;GACxF,KAAK,eAAe,IAAI,GAAG,SAAS,QAAQ,YAAY,YAAY,KAAA,CAAS,CAAC;GAC9E,KAAK,eAAe,IAAI,OAAO,OAAO,cAAc,gBAAgB,IAAI,CAAC,CAAC;GAC1E,KAAK,eAAe;EACtB,CAAC,CAAC,CAAC,KACD,OAAO,OAAO,UACZ,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GACtC,IAAI,MAAM,OAAI,KAAA,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;GAAA,CAAC;GAEf,KAAK,MAAM,aAAa,uBACtB,IAAI,UAAU,OACZ,OAAO,UAAU,MAAM,CAAC,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAA,CAAS,CAAC,CAAC;GAG/E,KAAK,eAAe,MAAM;EAC5B,CAAC,CACH,CACF;CACF;CAEA,QAA6B;EAC3B,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,KAAK,eAAe,MAAM;GAC1B,KAAK,eAAe,SAAS;GAC7B,KAAK,MAAM,aAAa,KAAK,aAC3B,IAAI,UAAU,OACZ,OAAO,UAAU,MAAM;GAG3B,KAAK,eAAe;EACtB,CAAC;CACH;CAEA,SAA8B;EAC5B,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,KAAK,MAAM,aAAa,KAAK,aAC3B,IAAI,UAAU,QACZ,OAAO,UAAU,OAAO;EAG9B,CAAC;CACH;CAEA,UAA+B;EAC7B,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,KAAK,MAAM,aAAa,KAAK,aAC3B,IAAI,UAAU,SACZ,OAAO,UAAU,QAAQ;EAG/B,CAAC;CACH;CAEA,QAA6B;EAC3B,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,KAAK,MAAM,aAAa,KAAK,aAC3B,IAAI,UAAU,OACZ,OAAO,UAAU,MAAM;EAG7B,CAAC;CACH;CAEA,cAAc,WAAmD;EAC/D,UAAU,CAAC,KAAK,cAAc,wCAAqC;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,CAAA,sBAAA,wCAAA;EAAA,CAAC;EACpE,KAAK,YAAY,KAAK,SAAS;CACjC;CAEA,iBAAiB,cAAkC;EACjD,UAAU,CAAC,KAAK,cAAc,wCAAqC;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,CAAA,sBAAA,wCAAA;EAAA,CAAC;EACpE,KAAK,eAAe,KAAK,YAAY;CACvC;;;;CAKA,gBAAgB,cAAwD;EACtE,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,KAAK,eAAe,KAAK,YAAY;GACrC,MAAM,UAAU,OAAO,aAAa,IAAI;GACxC,IAAI,SACF,KAAK,eAAe,IAAI,OAAO;EAEnC,CAAC;CACH;CAEA,SACE,YACA,YACA,mBACM;EACN,KAAK,MAAM,aAAa,KAAK,aAC3B,UAAU,WAAW,YAAY,YAAY,iBAAiB;CAElE;CAEA,MAAM,YAAoB,YAA2B;EACnD,KAAK,MAAM,aAAa,KAAK,aAC3B,UAAU,QAAQ,YAAY,UAAU;CAE5C;CAEA,QAAQ,MAAyC,MAA0C;EACzF,KAAK,MAAM,aAAa,KAAK,aAAa;GACxC,IAAI,QAAQ,CAAC,UAAU,KAAK,MAAM,QAAQ,IAAI,SAAS,IAAI,GACzD;GAGF,MAAM,gBAAgB,OAAO,YAC3B,OAAO,QAAQ,IAAI,CAAA,CAChB,QAAQ,UAAwD,MAAM,OAAO,KAAA,CAAS,CAAA,CACtF,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,MAAM,SAAS,CAAC,CAAC,CAClD;GACA,UAAU,UAAU,eAAe,IAAI;EACzC;CACF;CAEA,IAAI,UAAmB;EACrB,OAAO,KAAK,YAAY,OAAO,cAAc,UAAU,OAAO;CAChE;CAEA,IAAI,SAAwC;EAC1C,OAAO,EACL,mBAAmB,OAAO,eAAe;GACvC,KAAK,MAAM,aAAa,KAAK,eAAe,QAAQ,GAClD,UAAU,iBAAiB,OAAO,UAAU;EAEhD,EACF;CACF;CAEA,IAAI,SAAwC;EAC1C,OAAO,EACL,eAAe,OAAO,eAAe;GACnC,KAAK,MAAM,aAAa,KAAK,eAAe,QAAQ,GAClD,UAAU,aAAa,OAAO,UAAU;EAE5C,EACF;CACF;CAEA,IAAI,MAAkC;EACpC,OAAO;GACL,oBAAoB,WAAW;IAC7B,KAAK,MAAM,aAAa,KAAK,eAAe,KAAK,GAC/C,UAAU,kBAAkB,MAAM;GAEtC;GACA,kBAAkB,SAAS;IACzB,KAAK,MAAM,aAAa,KAAK,eAAe,KAAK,GAC/C,UAAU,gBAAgB,IAAI;GAElC;EACF;CACF;CAEA,IAAI,KAAgC;EAClC,OAAO;GACL,mBAAmB,cAAc;IAC/B,KAAK,MAAM,aAAa,KAAK,eAAe,IAAI,GAC9C,UAAU,iBAAiB,SAAS;GAExC;GACA,cAAc,SAAS;IACrB,KAAK,MAAM,aAAa,KAAK,eAAe,IAAI,GAC9C,UAAU,YAAY,IAAI;GAE9B;GACA,kBAAkB,aAAa;IAC7B,KAAK,MAAM,aAAa,KAAK,eAAe,IAAI,GAC9C,UAAU,gBAAgB,QAAQ;GAEtC;EACF;CACF;CAEA,IAAI,UAA0C;EAC5C,OAAO;GACL,YAAY,YAAY;IACtB,IAAI;IACJ,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GACnD,MAAO,MAAM,UAAU,WAAW,KAAM;IAE1C,OAAO;GACT;GACA,cAAc,OAAO,QAAQ,SAAS;IACpC,IAAI;IACJ,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GACnD,MAAO,MAAM,UAAU,aAAa,QAAQ,IAAI,KAAM;IAExD,OAAO;GACT;GACA,sBAAsB;IACpB,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GAAG;KACtD,MAAM,UAAU,UAAU,eAAe;KACzC,IAAI,SACF,OAAO;IAEX;GAEF;GACA,WAAW,OAAO,eAAe;IAC/B,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GACnD,MAAM,UAAU,UAAU,UAAU;GAExC;EACF;CACF;CAEA,YAAY,MAA2D;EACrE,MAAM,OAAO,KAAK,eAAe,IAAI;EACrC,IAAI,KAAK,WAAW,GAClB,OAAO,OAAO,QAAQ,KAAK;EAE7B,OAAO,OAAO,IAAI,EAAE,MAAM,KAAK,GAAG,aAAa;GAC7C,KAAK,MAAM,OAAO,MAEhB,IAAI,OADqB,IAAI,YAAY,GAEvC,OAAO;GAGX,OAAO;EACT,CAAC;CACH;CAEA,IAAI,UAA0C;EAC5C,OAAO;GACL,QAAQ,MAAM,OAAO,YAAY,SAAS;IACxC,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GACnD,UAAU,MAAM,MAAM,OAAO,YAAY,IAAI;GAEjD;GACA,YAAY,MAAM,OAAO,YAAY,SAAS;IAC5C,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GACnD,UAAU,UAAU,MAAM,OAAO,YAAY,IAAI;GAErD;GACA,eAAe,MAAM,OAAO,YAAY,SAAS;IAC/C,KAAK,MAAM,aAAa,KAAK,eAAe,SAAS,GACnD,UAAU,aAAa,MAAM,OAAO,YAAY,IAAI;GAExD;GACA,UAAU,MAAM,UAAU,YAAY,SAAS;IAC7C,MAAM,WAAW,KAAK,eAAe,SAAS,CAAC,CAAC,KAAK,cACnD,UAAU,QAAQ,MAAM,UAAU,YAAY,IAAI,CACpD;IACA,aAAa;KACX,KAAK,MAAM,WAAW,UACpB,QAAQ;IAEZ;GACF;EACF;CACF;CAEA,eACE,MAC6D;EAC7D,OAAO,SAAS,KACd,KAAK,aACL,MAAM,SAAS,cAAc,UAAU,IAAI,GAC3C,MAAM,QAAQ,QAA0E,IAAI,SAAS,IAAI,CAC3G;CACF;AACF;AAEA,IAAa,aAA2C,OAAO,QAAQ,IAAI,kBAAkB,CAAC;AAE9F,IAAa,gBAAgB,eAC3B,OAAO,GAAG,WAAW,gBAA8C;CACjE,MAAM,gBAAgB,OAAO;CAC7B,MAAM,YAAY,OAAO;CACzB,UAAU,mBAAmB,iBAAiB,OAAO,cAAc,kBAAkB,YAAS,KAAA,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA,CAAA,yFAAA,EAAA;CAAA,CAAC;CAC/F,cAAc,cAAc,SAAS;CACrC,OAAO;AACT,CAAC;AAEH,IAAa,mBAAmB,iBAC9B,OAAO,GAAG,WAAW,gBAA8C;CACjE,MAAM,gBAAgB,OAAO;CAC7B,UAAU,sBAAsB,iBAAiB,OAAO,cAAc,qBAAqB,YAAS,KAAA,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA,CAAA,+FAAA,EAAA;CAAA,CAAC;CACrG,cAAc,iBAAiB,YAAY;CAC3C,OAAO;AACT,CAAC;AAEH,IAAa,aAAa,OAAO,GAAG,WAAW,gBAA8C;CAC3F,MAAM,gBAAgB,OAAO;CAC7B,OAAO,cAAc,WAAW;CAChC,OAAO;AACT,CAAC"}
@@ -63,7 +63,7 @@ var toAiGenerationProperties = (inference) => stripUndefined({
63
63
  var stripUndefined = (properties) => Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
64
64
  //#endregion
65
65
  //#region src/version.ts
66
- var DXOS_VERSION = "0.13.0";
66
+ var DXOS_VERSION = "0.14.0";
67
67
  //#endregion
68
68
  //#region src/extensions/posthog/otel-destination.ts
69
69
  var __dxlog_file$1 = "/__w/dxos/dxos/packages/sdk/observability/src/extensions/posthog/otel-destination.ts";
@@ -109,9 +109,10 @@ var widgetSessionId = () => {
109
109
  /** Same-origin route of the web deployment, which proxies the upload to object storage. */
110
110
  var DEFAULT_FEEDBACK_LOGS_ENDPOINT = "/api/feedback-logs";
111
111
  /** Upload serialized logs, gzipped, to the feedback-logs endpoint. Returns the R2 key on success. */
112
- var uploadLogs = async (endpoint, ndjson) => {
112
+ var uploadLogs = async (endpoint, ndjson, kind = "logs") => {
113
113
  try {
114
- const response = await fetch(endpoint, {
114
+ const url = kind === "logs" ? endpoint : `${endpoint}${endpoint.includes("?") ? "&" : "?"}kind=${kind}`;
115
+ const response = await fetch(url, {
115
116
  method: "POST",
116
117
  headers: { "Content-Type": "application/gzip" },
117
118
  body: await gzip(ndjson)
@@ -123,7 +124,7 @@ var uploadLogs = async (endpoint, ndjson) => {
123
124
  }, {
124
125
  "~LogMeta": "~LogMeta",
125
126
  F: __dxlog_file,
126
- L: 95,
127
+ L: 101,
127
128
  S: void 0
128
129
  });
129
130
  return;
@@ -137,7 +138,7 @@ var uploadLogs = async (endpoint, ndjson) => {
137
138
  }, {
138
139
  "~LogMeta": "~LogMeta",
139
140
  F: __dxlog_file,
140
- L: 101,
141
+ L: 107,
141
142
  S: void 0
142
143
  });
143
144
  return;
@@ -158,7 +159,7 @@ var extensions = Effect.fn(function* ({ config, release, environment, serviceNam
158
159
  log("PostHog is being stubbed because it is running in a worker.", void 0, {
159
160
  "~LogMeta": "~LogMeta",
160
161
  F: __dxlog_file,
161
- L: 124,
162
+ L: 130,
162
163
  S: this
163
164
  });
164
165
  return stubExtension;
@@ -169,7 +170,7 @@ var extensions = Effect.fn(function* ({ config, release, environment, serviceNam
169
170
  log.info("Missing POSTHOG_API_KEY or POSTHOG_API_HOST", void 0, {
170
171
  "~LogMeta": "~LogMeta",
171
172
  F: __dxlog_file,
172
- L: 131,
173
+ L: 137,
173
174
  S: this
174
175
  });
175
176
  return stubExtension;
@@ -195,7 +196,7 @@ var extensions = Effect.fn(function* ({ config, release, environment, serviceNam
195
196
  const removePosthogLog = log.addProcessor(logProcessor, void 0, {
196
197
  "~LogMeta": "~LogMeta",
197
198
  F: __dxlog_file,
198
- L: 157,
199
+ L: 163,
199
200
  S: this
200
201
  });
201
202
  const tagSession = () => context.setTags({ sessionId: posthog.get_session_id() }, "logs");
@@ -261,6 +262,7 @@ var extensions = Effect.fn(function* ({ config, release, environment, serviceNam
261
262
  if (ndjson.length === 0) return;
262
263
  return await uploadLogs(feedbackLogsEndpoint, ndjson);
263
264
  },
265
+ uploadNdjson: async (ndjson, kind) => ndjson.length === 0 ? void 0 : await uploadLogs(feedbackLogsEndpoint, ndjson, kind),
264
266
  sessionContext: () => {
265
267
  if (!posthog.__loaded) return;
266
268
  try {
@@ -278,7 +280,7 @@ var extensions = Effect.fn(function* ({ config, release, environment, serviceNam
278
280
  log.warn("PostHog session context unavailable", { err }, {
279
281
  "~LogMeta": "~LogMeta",
280
282
  F: __dxlog_file,
281
- L: 241,
283
+ L: 249,
282
284
  S: this
283
285
  });
284
286
  return;
@@ -305,7 +307,7 @@ var extensions = Effect.fn(function* ({ config, release, environment, serviceNam
305
307
  }, {
306
308
  "~LogMeta": "~LogMeta",
307
309
  F: __dxlog_file,
308
- L: 264,
310
+ L: 272,
309
311
  S: this
310
312
  });
311
313
  }
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-ObservabilityExtension.mjs","names":[],"sources":["../../src/extensions/posthog/llm-analytics.ts","../../src/version.ts","../../src/extensions/posthog/otel-destination.ts","../../src/extensions/posthog/extension.ts","../../src/extensions/posthog/index.ts","../../src/ObservabilityExtension.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as ObservabilityExtension from '../../ObservabilityExtension.ts';\n\n/**\n * PostHog's LLM analytics schema. Three events with `$ai_*` properties are what the product reads:\n * `$ai_trace` is the top-level unit (a conversation turn here), `$ai_span` a step inside it (a tool\n * call), `$ai_generation` a model call. They are linked by `$ai_trace_id`, and a span or generation\n * names its parent with `$ai_parent_id`.\n *\n * `$ai_input_tokens` is the uncached count, which is what PostHog expects alongside the two cache\n * figures — together they price the call, and their ratio is the prompt-cache hit rate.\n */\nexport const AI_GENERATION_EVENT = '$ai_generation';\nexport const AI_TRACE_EVENT = '$ai_trace';\nexport const AI_SPAN_EVENT = '$ai_span';\n\n/** `$ai_trace`: the turn, carrying the prompt and the messages it produced as its states. */\nexport const toAiTraceProperties = (turn: ObservabilityExtension.Turn): Record<string, unknown> =>\n stripUndefined({\n ...spanProperties(turn),\n });\n\n/** `$ai_span` of type `tool`: one tool call, carrying its arguments and result as its states. */\nexport const toAiSpanProperties = (toolCall: ObservabilityExtension.ToolCall): Record<string, unknown> =>\n stripUndefined({\n ...spanProperties(toolCall),\n $ai_span_type: 'tool',\n });\n\nconst spanProperties = (span: ObservabilityExtension.AiSpanBase): Record<string, unknown> => ({\n $ai_trace_id: span.traceId,\n $ai_span_id: span.spanId,\n $ai_parent_id: span.parentSpanId,\n $ai_span_name: span.spanName,\n $ai_session_id: span.sessionId,\n $ai_latency: span.latency,\n $ai_input_state: span.content?.input,\n $ai_output_state: span.content?.output,\n $ai_content_truncated: span.content?.truncated,\n $ai_is_error: span.errorClass ? true : undefined,\n $ai_error: span.errorClass,\n});\n\nexport const toAiGenerationProperties = (inference: ObservabilityExtension.Inference): Record<string, unknown> =>\n stripUndefined({\n $ai_trace_id: inference.traceId,\n $ai_span_id: inference.spanId,\n $ai_parent_id: inference.parentSpanId,\n $ai_span_name: inference.spanName,\n $ai_provider: inference.provider,\n $ai_model: inference.model,\n $ai_session_id: inference.sessionId,\n $ai_model_parameters: inference.parameters,\n $ai_input_tokens: inference.inputTokens,\n $ai_output_tokens: inference.outputTokens,\n $ai_cache_read_input_tokens: inference.cacheReadTokens,\n $ai_cache_creation_input_tokens: inference.cacheWriteTokens,\n $ai_latency: inference.latency,\n $ai_stream: inference.streaming,\n $ai_input: inference.content?.input,\n $ai_output_choices: inference.content?.output,\n $ai_tools: inference.content?.tools,\n $ai_content_truncated: inference.content?.truncated,\n $ai_is_error: inference.errorClass ? true : undefined,\n $ai_error: inference.errorClass,\n });\n\nconst stripUndefined = (properties: Record<string, unknown>): Record<string, unknown> =>\n Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== undefined));\n","//\n// Copyright 2026 DXOS.org\n//\n\nexport const DXOS_VERSION = '0.13.0';\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Config, getEnvString } from '@dxos/config';\nimport { log } from '@dxos/log';\n\nimport { type OtelDestination } from '../otel/otel.ts';\n\n/** PostHog serves OTLP under `/i` of the same host it ingests product analytics on, not at the root. */\nconst OTLP_PATH_PREFIX = '/i';\n\n/**\n * PostHog as an OTLP backend, authenticated with the public `phc_` project token rather than a\n * secret, so the browser can post to it directly.\n */\nexport const otelDestination = (config: Config): OtelDestination | undefined => {\n const host = getEnvString(config, 'DX_POSTHOG_API_HOST');\n const apiKey = getEnvString(config, 'DX_POSTHOG_API_KEY');\n if (!host) {\n return undefined;\n }\n if (!apiKey) {\n log.info('DX_POSTHOG_API_HOST is set without DX_POSTHOG_API_KEY; skipping PostHog OTLP export');\n return undefined;\n }\n\n return {\n endpoint: `${host.replace(/\\/$/, '')}${OTLP_PATH_PREFIX}`,\n headers: { Authorization: `Bearer ${apiKey}` },\n };\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport { type PostHogConfig } from 'posthog-js';\n\nimport { type Config, getEnvString } from '@dxos/config';\nimport { log } from '@dxos/log';\nimport { type IdbLogStore } from '@dxos/log-store-idb';\nimport { gzip, isNode } from '@dxos/util';\n\nimport * as ObservabilityExtension from '../../ObservabilityExtension.ts';\nimport { DXOS_VERSION } from '../../version.ts';\nimport { stubExtension } from '../stub.ts';\nimport {\n AI_GENERATION_EVENT,\n AI_SPAN_EVENT,\n AI_TRACE_EVENT,\n toAiGenerationProperties,\n toAiSpanProperties,\n toAiTraceProperties,\n} from './llm-analytics.ts';\nimport { otelDestination } from './otel-destination.ts';\n\nconst WIDGET_SESSION_STORAGE_KEY = 'dxos.support.widgetSessionId';\n\nconst widgetSessionId = (): string => {\n try {\n const existing = localStorage.getItem(WIDGET_SESSION_STORAGE_KEY);\n if (existing) {\n return existing;\n }\n const minted = crypto.randomUUID();\n localStorage.setItem(WIDGET_SESSION_STORAGE_KEY, minted);\n return minted;\n } catch {\n return crypto.randomUUID();\n }\n};\n\nexport type ExtensionsOptions = {\n config: Config;\n /** Release identifier, e.g. `composer@0.8.3`. */\n release?: string;\n /** Deployment environment, e.g. `production` or `staging`. */\n environment?: string;\n serviceName?: string;\n posthog?: Partial<PostHogConfig>;\n /**\n * Shared persistent log store for debug log dumps.\n * The owning app is expected to register `logStore.processor` with `log` itself —\n * this extension only consumes the buffered logs (via `export()`).\n */\n logStore?: IdbLogStore;\n /**\n * Maximum byte size passed to `logStore.export()` when uploading feedback logs.\n * Should match the upload limit enforced by the server receiving the logs.\n * When omitted the full store is exported without trimming.\n */\n feedbackLogMaxSize?: number;\n /**\n * Where to POST feedback logs, defaulting to {@link DEFAULT_FEEDBACK_LOGS_ENDPOINT}.\n * A native build serves its frontend from its own origin and has no such route there, so it must\n * pass the absolute URL of a deployment that does.\n */\n feedbackLogsEndpoint?: string;\n /** What the `posthog-node` transport needs; a browser host has posthog-js and reads none of it. */\n node?: NodeOptions;\n};\n\nexport type NodeOptions = {\n /** Pins the project instead of reading `DX_POSTHOG_API_KEY`. */\n apiKey?: string;\n /** Ingestion host — a region, or a proxy on your own domain. */\n host?: string;\n /** Attribution for events captured before `identify`, since there is no ambient person. */\n distinctId?: string;\n /** Which MCP server this host is, stamped on every `$mcp_*` event. */\n mcpServer?: { name: string; version: string };\n};\n\n/** Same-origin route of the web deployment, which proxies the upload to object storage. */\nconst DEFAULT_FEEDBACK_LOGS_ENDPOINT = '/api/feedback-logs';\n\n/** Upload serialized logs, gzipped, to the feedback-logs endpoint. Returns the R2 key on success. */\nconst uploadLogs = async (endpoint: string, ndjson: string): Promise<string | undefined> => {\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/gzip' },\n body: await gzip(ndjson),\n });\n if (!response.ok) {\n log.warn('feedback log upload failed', { endpoint, status: response.status });\n return undefined;\n }\n const { key } = await response.json();\n return key;\n } catch (err) {\n log.warn('feedback log upload error', { endpoint, error: err });\n return undefined;\n }\n};\n\n/** Create a PostHog-backed observability extension for events, errors, and support tickets. */\nexport const extensions: (options: ExtensionsOptions) => Effect.Effect<ObservabilityExtension.Extension> = Effect.fn(\n function* ({\n config,\n release,\n environment,\n serviceName = 'app',\n posthog: posthogConfig,\n logStore,\n feedbackLogMaxSize,\n feedbackLogsEndpoint = DEFAULT_FEEDBACK_LOGS_ENDPOINT,\n node,\n }) {\n if (isNode()) {\n const { extensions: nodeExtensions } = yield* Effect.promise(() => import('#posthog-transport'));\n return yield* nodeExtensions({ config, release, environment, node });\n }\n if (typeof window === 'undefined') {\n log('PostHog is being stubbed because it is running in a worker.');\n return stubExtension;\n }\n\n const apiKey = getEnvString(config, 'DX_POSTHOG_API_KEY');\n const api_host = getEnvString(config, 'DX_POSTHOG_API_HOST');\n if (!apiKey || !api_host) {\n log.info('Missing POSTHOG_API_KEY or POSTHOG_API_HOST');\n return stubExtension;\n }\n\n const { default: posthog } = yield* Effect.promise(() => import('posthog-js'));\n const { logProcessor } = yield* Effect.promise(() => import('./log-processor.ts'));\n let unregisterPosthogProcessors: (() => void) | undefined;\n\n return {\n initialize: (context) =>\n Effect.sync(() => {\n // https://posthog.com/docs/libraries/js/config\n posthog.init(apiKey, {\n api_host,\n mask_all_text: true,\n capture_exceptions: true,\n // Cookies stay scoped to the exact host; the cross-subdomain dmn_chk_* probe fails on public-suffix hosts (e.g. *.pages.dev) and spams the console.\n cross_subdomain_cookie: false,\n ...posthogConfig,\n });\n posthog.register({\n sdkVersion: DXOS_VERSION,\n ...(release ? { release } : {}),\n ...(environment ? { environment } : {}),\n });\n unregisterPosthogProcessors?.();\n const removePosthogLog = log.addProcessor(logProcessor);\n // PostHog joins a log record to its session replay on a log attribute literally named\n // `sessionId` — not the OTel `session.id` resource attribute — and rotates that id.\n const tagSession = () => context.setTags({ sessionId: posthog.get_session_id() }, 'logs');\n const removeSessionListener = posthog.onSessionId(tagSession);\n tagSession();\n unregisterPosthogProcessors = () => {\n removePosthogLog();\n removeSessionListener();\n };\n }),\n close: () =>\n Effect.sync(() => {\n unregisterPosthogProcessors?.();\n unregisterPosthogProcessors = undefined;\n }),\n enable: () => Effect.sync(() => posthog.opt_in_capturing()),\n disable: () => Effect.sync(() => posthog.opt_out_capturing()),\n identify: (distinctId, attributes, setOnceAttributes) => {\n posthog.identify(distinctId, attributes, setOnceAttributes);\n },\n alias: (distinctId, previousId) => {\n posthog.alias(distinctId, previousId);\n },\n setTags: (tags) => {\n posthog.register_for_session(tags);\n },\n get enabled(): boolean {\n return posthog.is_capturing();\n },\n apis: [\n {\n kind: 'events',\n isAvailable: () => Effect.succeed(true),\n captureEvent: (event, attributes) => {\n posthog.capture(event, attributes);\n },\n },\n {\n kind: 'errors',\n isAvailable: () => Effect.succeed(true),\n captureException: (error, attributes) => {\n posthog.captureException(error, attributes);\n },\n },\n {\n kind: 'ai',\n isAvailable: () => Effect.succeed(true),\n captureInference: (inference) => {\n posthog.capture(AI_GENERATION_EVENT, toAiGenerationProperties(inference));\n },\n captureTurn: (turn) => {\n posthog.capture(AI_TRACE_EVENT, toAiTraceProperties(turn));\n },\n captureToolCall: (toolCall) => {\n posthog.capture(AI_SPAN_EVENT, toAiSpanProperties(toolCall));\n },\n },\n {\n kind: 'support',\n isAvailable: () => Effect.succeed(logStore !== undefined),\n uploadLogs: async () => {\n if (logStore === undefined) {\n return undefined;\n }\n const ndjson = await logStore.export({ maxSize: feedbackLogMaxSize });\n if (ndjson.length === 0) {\n return undefined;\n }\n return await uploadLogs(feedbackLogsEndpoint, ndjson);\n },\n sessionContext: () => {\n if (!posthog.__loaded) {\n return undefined;\n }\n try {\n return {\n distinctId: posthog.get_distinct_id(),\n widgetSessionId: widgetSessionId(),\n sessionId: posthog.get_session_id(),\n replayUrl: posthog.get_session_replay_url({ withTimestamp: true, timestampLookBack: 30 }),\n currentUrl: `${window.location.origin}${window.location.pathname}`,\n };\n } catch (err) {\n log.warn('PostHog session context unavailable', { err });\n return undefined;\n }\n },\n flushLogs: async (attributes) => {\n const destination = otelDestination(config);\n if (!destination) {\n return;\n }\n const ndjson = await logStore?.export({ maxSize: feedbackLogMaxSize });\n if (!ndjson || ndjson.length === 0) {\n return;\n }\n const { flushSupportLogs } = await import('./support-logs.ts');\n const count = await flushSupportLogs(ndjson, {\n destination,\n resourceAttributes: {\n 'service.name': serviceName,\n ...(release ? { 'service.version': release } : {}),\n ...(environment ? { 'deployment.environment': environment } : {}),\n },\n attributes,\n });\n log.info('support logs flushed to PostHog', { ...attributes, count });\n },\n },\n ],\n };\n },\n);\n","//\n// Copyright 2025 DXOS.org\n//\n\nexport * from './llm-analytics.ts';\nexport * from './extension.ts';\nexport * from './otel-destination.ts';\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Effect from 'effect/Effect';\n\nimport { type CleanupFn } from '@dxos/async';\nimport { type MetricObserver } from '@dxos/tracing';\n\nexport * from './extensions/index.ts';\n\n/**\n * Kind of observability extension.\n *\n * - errors: Error tracking (e.g., PostHog)\n * - events: Product usage event tracking (e.g., PostHog)\n * - support: Support tickets anchoring user reports and telemetry (e.g., PostHog)\n * - ai: Model inferences, tool calls, and turns (e.g., PostHog LLM analytics, OTel gen_ai)\n * - logs: Structured logging (e.g., OTEL)\n * - mcp: MCP server sessions and tool calls (e.g., PostHog)\n * - metrics: Metric data (e.g., OTEL)\n * - traces: Distributed tracing (e.g., OTEL)\n */\nexport type Kind = 'ai' | 'errors' | 'events' | 'support' | 'logs' | 'mcp' | 'metrics' | 'traces';\n\n/**\n * Base for every extension API variant. All kinds implement availability the same way.\n */\nexport type ExtensionApiBase<K extends Kind = Kind> = {\n kind: K;\n isAvailable(): Effect.Effect<boolean>;\n};\n\n/**\n * Instrument-level metadata, declared once by the first caller for a given metric name.\n * Units must be UCUM as OTel requires — `s`, `By`, `{thing}`.\n */\nexport type MetricMeta = { unit?: string; description?: string };\n\n/**\n * Metrics extension API (kind-specific methods only).\n */\nexport type Metrics = {\n gauge(name: string, value: number, tags?: Attributes, meta?: MetricMeta): void;\n increment(name: string, value?: number, tags?: Attributes, meta?: MetricMeta): void;\n distribution(name: string, value: number, tags?: Attributes, meta?: MetricMeta): void;\n /**\n * Registers a callback read once per export interval.\n * Prefer this over {@link Metrics.gauge} for any \"current value\" metric, since a pushed gauge\n * only lands in the export windows its producer happens to tick in.\n */\n observe(name: string, callback: MetricObserver, tags?: Attributes, meta?: MetricMeta): CleanupFn;\n};\n\n/**\n * Errors extension API (kind-specific methods only).\n */\nexport type Errors = {\n captureException(error: Error, attributes?: Attributes): void;\n};\n\n/**\n * Wider than {@link Attributes}: event properties carry structured values (message arrays,\n * nested objects), not just scalars.\n */\nexport type EventAttributes = Record<string, unknown>;\n\n/**\n * Events extension API (kind-specific methods only).\n */\nexport type Events = {\n captureEvent(event: string, attributes?: EventAttributes): void;\n};\n\n/**\n * One model call, already filtered by the capture policy and scrubbed (see `AiObservability`). Shaped\n * after the OTel GenAI conventions rather than any vendor's schema — an extension maps it onto\n * whatever its backend calls these things.\n */\nexport type Inference = {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n /** Provider span name, e.g. `LanguageModel.streamText`. */\n spanName: string;\n /** `gen_ai.system` — the serving product, not the wire dialect. */\n provider?: string;\n model?: string;\n /** Conversation the call belongs to, when the call site named one. */\n sessionId?: string;\n /** Request parameters the provider reported (temperature, max_tokens, …). */\n parameters?: Record<string, unknown>;\n /** Excludes tokens served from the prompt cache; add {@link cacheReadTokens} for the total. */\n inputTokens?: number;\n outputTokens?: number;\n /** Absent rather than zero when the provider does not report prompt caching. */\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n /** Wall-clock duration, in seconds. */\n latency: number;\n streaming: boolean;\n /** Absent entirely when the capture policy denied it. */\n content?: GenerationContent;\n /** Exception class name only — a provider's message can embed request or response fragments. */\n errorClass?: string;\n};\n\nexport type GenerationContent = {\n /** Parsed prompt messages, or the raw string when {@link truncated} left it unparseable. */\n input?: unknown;\n output?: unknown;\n tools?: unknown;\n /** Some field above was cut to fit, so it is a fragment rather than the whole. */\n truncated?: boolean;\n};\n\n/** Fields a turn and a tool call share with an inference: identity, timing, and gated content. */\nexport type AiSpanBase = {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n /** For a tool call, the tool's name rather than the span's. */\n spanName: string;\n /** Conversation the span belongs to, when the call site named one. */\n sessionId?: string;\n /** Wall-clock duration, in seconds. */\n latency: number;\n /** Absent entirely when the capture policy denied it. */\n content?: AiSpanContent;\n /** Exception class name only, as on {@link Inference}. */\n errorClass?: string;\n};\n\nexport type AiSpanContent = {\n input?: unknown;\n output?: unknown;\n /** Some field above was cut to fit, so it is a fragment rather than the whole. */\n truncated?: boolean;\n};\n\n/**\n * One conversation turn: the unit a backend groups a turn's model calls and tool calls under (what\n * PostHog calls a trace). Its input is the user prompt and its output the messages the turn produced.\n */\nexport type Turn = AiSpanBase;\n\n/** One tool call inside a turn (what PostHog calls a span), with the arguments and the result. */\nexport type ToolCall = AiSpanBase;\n\n/**\n * AI extension API (kind-specific methods only).\n */\nexport type Ai = {\n captureInference(inference: Inference): void;\n captureTurn(turn: Turn): void;\n captureToolCall(toolCall: ToolCall): void;\n};\n\n/** The client name an MCP event carries when no request named its client; `@posthog/mcp` drops a falsy one. */\nexport const UNKNOWN_MCP_CLIENT = 'unknown';\n\n/** What every MCP event carries: the session it groups under and the client it came from. */\nexport type McpSession = {\n /** Groups the session's events; one server process is one session over a stdio transport. */\n sessionId: string;\n clientName?: string;\n clientVersion?: string;\n protocolVersion?: string;\n};\n\n/** MCP extension API (kind-specific methods only). */\nexport type Mcp = {\n /** A client connected successfully. */\n captureInitialize(session: McpSession): void;\n captureToolCall(\n call: McpSession & { toolName: string; parameters?: unknown; durationMs: number; isError: boolean },\n ): void;\n};\n\n/**\n * Support extension API (kind-specific methods only).\n */\nexport type SupportLogTags = { ticketId: string } | { reportId: string };\n\nexport type Support = {\n uploadLogs(): Promise<string | undefined>;\n sessionContext(): SupportSessionContext | undefined;\n flushLogs(attributes: SupportLogTags): Promise<void>;\n};\n\nexport type ExtensionApi =\n | (ExtensionApiBase<'errors'> & Errors)\n | (ExtensionApiBase<'events'> & Events)\n | (ExtensionApiBase<'support'> & Support)\n | (ExtensionApiBase<'ai'> & Ai)\n // TODO(wittjosiah): Direct logs api?\n | (ExtensionApiBase<'mcp'> & Mcp)\n | ExtensionApiBase<'logs'>\n | (ExtensionApiBase<'metrics'> & Metrics)\n // TODO(wittjosiah): Direct traces api?\n | ExtensionApiBase<'traces'>;\n\n/**\n * What a browser knows about its own telemetry session. Shaped after what posthog-js's own widget\n * sends.\n */\nexport type SupportSessionContext = {\n distinctId: string;\n /** Browser-minted id the widget API uses for access control on anonymous tickets. */\n widgetSessionId: string;\n sessionId?: string;\n replayUrl?: string;\n currentUrl?: string;\n};\n\n/**\n * Attributes to be attached to observability events.\n */\nexport type Attributes = Record<string, string | number | boolean | undefined>;\n\nexport type ExtensionContext = {\n setTags(tags: Attributes, kind?: Kind): void;\n};\n\n/**\n * Implementation of an observability extension API.\n */\nexport type Extension = {\n initialize?(context: ExtensionContext): Effect.Effect<void, Error>;\n close?(): Effect.Effect<void>;\n enable?(): Effect.Effect<void>;\n disable?(): Effect.Effect<void>;\n flush?(): Effect.Effect<void>;\n identify?(distinctId: string, attributes?: Attributes, setOnceAttributes?: Attributes): void;\n alias?(distinctId: string, previousId?: string): void;\n setTags?(tags: Record<string, string>, kind?: Kind): void;\n enabled: boolean;\n apis: ExtensionApi[];\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"chunk-ObservabilityExtension.mjs","names":[],"sources":["../../src/extensions/posthog/llm-analytics.ts","../../src/version.ts","../../src/extensions/posthog/otel-destination.ts","../../src/extensions/posthog/extension.ts","../../src/extensions/posthog/index.ts","../../src/ObservabilityExtension.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as ObservabilityExtension from '../../ObservabilityExtension.ts';\n\n/**\n * PostHog's LLM analytics schema. Three events with `$ai_*` properties are what the product reads:\n * `$ai_trace` is the top-level unit (a conversation turn here), `$ai_span` a step inside it (a tool\n * call), `$ai_generation` a model call. They are linked by `$ai_trace_id`, and a span or generation\n * names its parent with `$ai_parent_id`.\n *\n * `$ai_input_tokens` is the uncached count, which is what PostHog expects alongside the two cache\n * figures — together they price the call, and their ratio is the prompt-cache hit rate.\n */\nexport const AI_GENERATION_EVENT = '$ai_generation';\nexport const AI_TRACE_EVENT = '$ai_trace';\nexport const AI_SPAN_EVENT = '$ai_span';\n\n/** `$ai_trace`: the turn, carrying the prompt and the messages it produced as its states. */\nexport const toAiTraceProperties = (turn: ObservabilityExtension.Turn): Record<string, unknown> =>\n stripUndefined({\n ...spanProperties(turn),\n });\n\n/** `$ai_span` of type `tool`: one tool call, carrying its arguments and result as its states. */\nexport const toAiSpanProperties = (toolCall: ObservabilityExtension.ToolCall): Record<string, unknown> =>\n stripUndefined({\n ...spanProperties(toolCall),\n $ai_span_type: 'tool',\n });\n\nconst spanProperties = (span: ObservabilityExtension.AiSpanBase): Record<string, unknown> => ({\n $ai_trace_id: span.traceId,\n $ai_span_id: span.spanId,\n $ai_parent_id: span.parentSpanId,\n $ai_span_name: span.spanName,\n $ai_session_id: span.sessionId,\n $ai_latency: span.latency,\n $ai_input_state: span.content?.input,\n $ai_output_state: span.content?.output,\n $ai_content_truncated: span.content?.truncated,\n $ai_is_error: span.errorClass ? true : undefined,\n $ai_error: span.errorClass,\n});\n\nexport const toAiGenerationProperties = (inference: ObservabilityExtension.Inference): Record<string, unknown> =>\n stripUndefined({\n $ai_trace_id: inference.traceId,\n $ai_span_id: inference.spanId,\n $ai_parent_id: inference.parentSpanId,\n $ai_span_name: inference.spanName,\n $ai_provider: inference.provider,\n $ai_model: inference.model,\n $ai_session_id: inference.sessionId,\n $ai_model_parameters: inference.parameters,\n $ai_input_tokens: inference.inputTokens,\n $ai_output_tokens: inference.outputTokens,\n $ai_cache_read_input_tokens: inference.cacheReadTokens,\n $ai_cache_creation_input_tokens: inference.cacheWriteTokens,\n $ai_latency: inference.latency,\n $ai_stream: inference.streaming,\n $ai_input: inference.content?.input,\n $ai_output_choices: inference.content?.output,\n $ai_tools: inference.content?.tools,\n $ai_content_truncated: inference.content?.truncated,\n $ai_is_error: inference.errorClass ? true : undefined,\n $ai_error: inference.errorClass,\n });\n\nconst stripUndefined = (properties: Record<string, unknown>): Record<string, unknown> =>\n Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== undefined));\n","//\n// Copyright 2026 DXOS.org\n//\n\nexport const DXOS_VERSION = '0.14.0';\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Config, getEnvString } from '@dxos/config';\nimport { log } from '@dxos/log';\n\nimport { type OtelDestination } from '../otel/otel.ts';\n\n/** PostHog serves OTLP under `/i` of the same host it ingests product analytics on, not at the root. */\nconst OTLP_PATH_PREFIX = '/i';\n\n/**\n * PostHog as an OTLP backend, authenticated with the public `phc_` project token rather than a\n * secret, so the browser can post to it directly.\n */\nexport const otelDestination = (config: Config): OtelDestination | undefined => {\n const host = getEnvString(config, 'DX_POSTHOG_API_HOST');\n const apiKey = getEnvString(config, 'DX_POSTHOG_API_KEY');\n if (!host) {\n return undefined;\n }\n if (!apiKey) {\n log.info('DX_POSTHOG_API_HOST is set without DX_POSTHOG_API_KEY; skipping PostHog OTLP export');\n return undefined;\n }\n\n return {\n endpoint: `${host.replace(/\\/$/, '')}${OTLP_PATH_PREFIX}`,\n headers: { Authorization: `Bearer ${apiKey}` },\n };\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport { type PostHogConfig } from 'posthog-js';\n\nimport { type Config, getEnvString } from '@dxos/config';\nimport { log } from '@dxos/log';\nimport { type IdbLogStore } from '@dxos/log-store-idb';\nimport { gzip, isNode } from '@dxos/util';\n\nimport * as ObservabilityExtension from '../../ObservabilityExtension.ts';\nimport { DXOS_VERSION } from '../../version.ts';\nimport { stubExtension } from '../stub.ts';\nimport {\n AI_GENERATION_EVENT,\n AI_SPAN_EVENT,\n AI_TRACE_EVENT,\n toAiGenerationProperties,\n toAiSpanProperties,\n toAiTraceProperties,\n} from './llm-analytics.ts';\nimport { otelDestination } from './otel-destination.ts';\n\nconst WIDGET_SESSION_STORAGE_KEY = 'dxos.support.widgetSessionId';\n\nconst widgetSessionId = (): string => {\n try {\n const existing = localStorage.getItem(WIDGET_SESSION_STORAGE_KEY);\n if (existing) {\n return existing;\n }\n const minted = crypto.randomUUID();\n localStorage.setItem(WIDGET_SESSION_STORAGE_KEY, minted);\n return minted;\n } catch {\n return crypto.randomUUID();\n }\n};\n\nexport type ExtensionsOptions = {\n config: Config;\n /** Release identifier, e.g. `composer@0.8.3`. */\n release?: string;\n /** Deployment environment, e.g. `production` or `staging`. */\n environment?: string;\n serviceName?: string;\n posthog?: Partial<PostHogConfig>;\n /**\n * Shared persistent log store for debug log dumps.\n * The owning app is expected to register `logStore.processor` with `log` itself —\n * this extension only consumes the buffered logs (via `export()`).\n */\n logStore?: IdbLogStore;\n /**\n * Maximum byte size passed to `logStore.export()` when uploading feedback logs.\n * Should match the upload limit enforced by the server receiving the logs.\n * When omitted the full store is exported without trimming.\n */\n feedbackLogMaxSize?: number;\n /**\n * Where to POST feedback logs, defaulting to {@link DEFAULT_FEEDBACK_LOGS_ENDPOINT}.\n * A native build serves its frontend from its own origin and has no such route there, so it must\n * pass the absolute URL of a deployment that does.\n */\n feedbackLogsEndpoint?: string;\n /** What the `posthog-node` transport needs; a browser host has posthog-js and reads none of it. */\n node?: NodeOptions;\n};\n\nexport type NodeOptions = {\n /** Pins the project instead of reading `DX_POSTHOG_API_KEY`. */\n apiKey?: string;\n /** Ingestion host — a region, or a proxy on your own domain. */\n host?: string;\n /** Attribution for events captured before `identify`, since there is no ambient person. */\n distinctId?: string;\n /** Which MCP server this host is, stamped on every `$mcp_*` event. */\n mcpServer?: { name: string; version: string };\n};\n\n/** Same-origin route of the web deployment, which proxies the upload to object storage. */\nconst DEFAULT_FEEDBACK_LOGS_ENDPOINT = '/api/feedback-logs';\n\n/** Upload serialized logs, gzipped, to the feedback-logs endpoint. Returns the R2 key on success. */\nconst uploadLogs = async (\n endpoint: string,\n ndjson: string,\n kind: ObservabilityExtension.SupportUploadKind = 'logs',\n): Promise<string | undefined> => {\n try {\n // The endpoint may be relative (same-origin) or absolute (native builds), so append rather than parse.\n const url = kind === 'logs' ? endpoint : `${endpoint}${endpoint.includes('?') ? '&' : '?'}kind=${kind}`;\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/gzip' },\n body: await gzip(ndjson),\n });\n if (!response.ok) {\n log.warn('feedback log upload failed', { endpoint, status: response.status });\n return undefined;\n }\n const { key } = await response.json();\n return key;\n } catch (err) {\n log.warn('feedback log upload error', { endpoint, error: err });\n return undefined;\n }\n};\n\n/** Create a PostHog-backed observability extension for events, errors, and support tickets. */\nexport const extensions: (options: ExtensionsOptions) => Effect.Effect<ObservabilityExtension.Extension> = Effect.fn(\n function* ({\n config,\n release,\n environment,\n serviceName = 'app',\n posthog: posthogConfig,\n logStore,\n feedbackLogMaxSize,\n feedbackLogsEndpoint = DEFAULT_FEEDBACK_LOGS_ENDPOINT,\n node,\n }) {\n if (isNode()) {\n const { extensions: nodeExtensions } = yield* Effect.promise(() => import('#posthog-transport'));\n return yield* nodeExtensions({ config, release, environment, node });\n }\n if (typeof window === 'undefined') {\n log('PostHog is being stubbed because it is running in a worker.');\n return stubExtension;\n }\n\n const apiKey = getEnvString(config, 'DX_POSTHOG_API_KEY');\n const api_host = getEnvString(config, 'DX_POSTHOG_API_HOST');\n if (!apiKey || !api_host) {\n log.info('Missing POSTHOG_API_KEY or POSTHOG_API_HOST');\n return stubExtension;\n }\n\n const { default: posthog } = yield* Effect.promise(() => import('posthog-js'));\n const { logProcessor } = yield* Effect.promise(() => import('./log-processor.ts'));\n let unregisterPosthogProcessors: (() => void) | undefined;\n\n return {\n initialize: (context) =>\n Effect.sync(() => {\n // https://posthog.com/docs/libraries/js/config\n posthog.init(apiKey, {\n api_host,\n mask_all_text: true,\n capture_exceptions: true,\n // Cookies stay scoped to the exact host; the cross-subdomain dmn_chk_* probe fails on public-suffix hosts (e.g. *.pages.dev) and spams the console.\n cross_subdomain_cookie: false,\n ...posthogConfig,\n });\n posthog.register({\n sdkVersion: DXOS_VERSION,\n ...(release ? { release } : {}),\n ...(environment ? { environment } : {}),\n });\n unregisterPosthogProcessors?.();\n const removePosthogLog = log.addProcessor(logProcessor);\n // PostHog joins a log record to its session replay on a log attribute literally named\n // `sessionId` — not the OTel `session.id` resource attribute — and rotates that id.\n const tagSession = () => context.setTags({ sessionId: posthog.get_session_id() }, 'logs');\n const removeSessionListener = posthog.onSessionId(tagSession);\n tagSession();\n unregisterPosthogProcessors = () => {\n removePosthogLog();\n removeSessionListener();\n };\n }),\n close: () =>\n Effect.sync(() => {\n unregisterPosthogProcessors?.();\n unregisterPosthogProcessors = undefined;\n }),\n enable: () => Effect.sync(() => posthog.opt_in_capturing()),\n disable: () => Effect.sync(() => posthog.opt_out_capturing()),\n identify: (distinctId, attributes, setOnceAttributes) => {\n posthog.identify(distinctId, attributes, setOnceAttributes);\n },\n alias: (distinctId, previousId) => {\n posthog.alias(distinctId, previousId);\n },\n setTags: (tags) => {\n posthog.register_for_session(tags);\n },\n get enabled(): boolean {\n return posthog.is_capturing();\n },\n apis: [\n {\n kind: 'events',\n isAvailable: () => Effect.succeed(true),\n captureEvent: (event, attributes) => {\n posthog.capture(event, attributes);\n },\n },\n {\n kind: 'errors',\n isAvailable: () => Effect.succeed(true),\n captureException: (error, attributes) => {\n posthog.captureException(error, attributes);\n },\n },\n {\n kind: 'ai',\n isAvailable: () => Effect.succeed(true),\n captureInference: (inference) => {\n posthog.capture(AI_GENERATION_EVENT, toAiGenerationProperties(inference));\n },\n captureTurn: (turn) => {\n posthog.capture(AI_TRACE_EVENT, toAiTraceProperties(turn));\n },\n captureToolCall: (toolCall) => {\n posthog.capture(AI_SPAN_EVENT, toAiSpanProperties(toolCall));\n },\n },\n {\n kind: 'support',\n isAvailable: () => Effect.succeed(logStore !== undefined),\n uploadLogs: async () => {\n if (logStore === undefined) {\n return undefined;\n }\n const ndjson = await logStore.export({ maxSize: feedbackLogMaxSize });\n if (ndjson.length === 0) {\n return undefined;\n }\n return await uploadLogs(feedbackLogsEndpoint, ndjson);\n },\n uploadNdjson: async (ndjson, kind) =>\n ndjson.length === 0 ? undefined : await uploadLogs(feedbackLogsEndpoint, ndjson, kind),\n sessionContext: () => {\n if (!posthog.__loaded) {\n return undefined;\n }\n try {\n return {\n distinctId: posthog.get_distinct_id(),\n widgetSessionId: widgetSessionId(),\n sessionId: posthog.get_session_id(),\n replayUrl: posthog.get_session_replay_url({ withTimestamp: true, timestampLookBack: 30 }),\n currentUrl: `${window.location.origin}${window.location.pathname}`,\n };\n } catch (err) {\n log.warn('PostHog session context unavailable', { err });\n return undefined;\n }\n },\n flushLogs: async (attributes) => {\n const destination = otelDestination(config);\n if (!destination) {\n return;\n }\n const ndjson = await logStore?.export({ maxSize: feedbackLogMaxSize });\n if (!ndjson || ndjson.length === 0) {\n return;\n }\n const { flushSupportLogs } = await import('./support-logs.ts');\n const count = await flushSupportLogs(ndjson, {\n destination,\n resourceAttributes: {\n 'service.name': serviceName,\n ...(release ? { 'service.version': release } : {}),\n ...(environment ? { 'deployment.environment': environment } : {}),\n },\n attributes,\n });\n log.info('support logs flushed to PostHog', { ...attributes, count });\n },\n },\n ],\n };\n },\n);\n","//\n// Copyright 2025 DXOS.org\n//\n\nexport * from './llm-analytics.ts';\nexport * from './extension.ts';\nexport * from './otel-destination.ts';\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Effect from 'effect/Effect';\n\nimport { type CleanupFn } from '@dxos/async';\nimport { type MetricObserver } from '@dxos/tracing';\n\nexport * from './extensions/index.ts';\n\n/**\n * Kind of observability extension.\n *\n * - errors: Error tracking (e.g., PostHog)\n * - events: Product usage event tracking (e.g., PostHog)\n * - support: Support tickets anchoring user reports and telemetry (e.g., PostHog)\n * - ai: Model inferences, tool calls, and turns (e.g., PostHog LLM analytics, OTel gen_ai)\n * - logs: Structured logging (e.g., OTEL)\n * - mcp: MCP server sessions and tool calls (e.g., PostHog)\n * - metrics: Metric data (e.g., OTEL)\n * - traces: Distributed tracing (e.g., OTEL)\n */\nexport type Kind = 'ai' | 'errors' | 'events' | 'support' | 'logs' | 'mcp' | 'metrics' | 'traces';\n\n/**\n * Base for every extension API variant. All kinds implement availability the same way.\n */\nexport type ExtensionApiBase<K extends Kind = Kind> = {\n kind: K;\n isAvailable(): Effect.Effect<boolean>;\n};\n\n/**\n * Instrument-level metadata, declared once by the first caller for a given metric name.\n * Units must be UCUM as OTel requires — `s`, `By`, `{thing}`.\n */\nexport type MetricMeta = { unit?: string; description?: string };\n\n/**\n * Metrics extension API (kind-specific methods only).\n */\nexport type Metrics = {\n gauge(name: string, value: number, tags?: Attributes, meta?: MetricMeta): void;\n increment(name: string, value?: number, tags?: Attributes, meta?: MetricMeta): void;\n distribution(name: string, value: number, tags?: Attributes, meta?: MetricMeta): void;\n /**\n * Registers a callback read once per export interval.\n * Prefer this over {@link Metrics.gauge} for any \"current value\" metric, since a pushed gauge\n * only lands in the export windows its producer happens to tick in.\n */\n observe(name: string, callback: MetricObserver, tags?: Attributes, meta?: MetricMeta): CleanupFn;\n};\n\n/**\n * Errors extension API (kind-specific methods only).\n */\nexport type Errors = {\n captureException(error: Error, attributes?: Attributes): void;\n};\n\n/**\n * Wider than {@link Attributes}: event properties carry structured values (message arrays,\n * nested objects), not just scalars.\n */\nexport type EventAttributes = Record<string, unknown>;\n\n/**\n * Events extension API (kind-specific methods only).\n */\nexport type Events = {\n captureEvent(event: string, attributes?: EventAttributes): void;\n};\n\n/**\n * One model call, already filtered by the capture policy and scrubbed (see `AiObservability`). Shaped\n * after the OTel GenAI conventions rather than any vendor's schema — an extension maps it onto\n * whatever its backend calls these things.\n */\nexport type Inference = {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n /** Provider span name, e.g. `LanguageModel.streamText`. */\n spanName: string;\n /** `gen_ai.system` — the serving product, not the wire dialect. */\n provider?: string;\n model?: string;\n /** Conversation the call belongs to, when the call site named one. */\n sessionId?: string;\n /** Request parameters the provider reported (temperature, max_tokens, …). */\n parameters?: Record<string, unknown>;\n /** Excludes tokens served from the prompt cache; add {@link cacheReadTokens} for the total. */\n inputTokens?: number;\n outputTokens?: number;\n /** Absent rather than zero when the provider does not report prompt caching. */\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n /** Wall-clock duration, in seconds. */\n latency: number;\n streaming: boolean;\n /** Absent entirely when the capture policy denied it. */\n content?: GenerationContent;\n /** Exception class name only — a provider's message can embed request or response fragments. */\n errorClass?: string;\n};\n\nexport type GenerationContent = {\n /** Parsed prompt messages, or the raw string when {@link truncated} left it unparseable. */\n input?: unknown;\n output?: unknown;\n tools?: unknown;\n /** Some field above was cut to fit, so it is a fragment rather than the whole. */\n truncated?: boolean;\n};\n\n/** Fields a turn and a tool call share with an inference: identity, timing, and gated content. */\nexport type AiSpanBase = {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n /** For a tool call, the tool's name rather than the span's. */\n spanName: string;\n /** Conversation the span belongs to, when the call site named one. */\n sessionId?: string;\n /** Wall-clock duration, in seconds. */\n latency: number;\n /** Absent entirely when the capture policy denied it. */\n content?: AiSpanContent;\n /** Exception class name only, as on {@link Inference}. */\n errorClass?: string;\n};\n\nexport type AiSpanContent = {\n input?: unknown;\n output?: unknown;\n /** Some field above was cut to fit, so it is a fragment rather than the whole. */\n truncated?: boolean;\n};\n\n/**\n * One conversation turn: the unit a backend groups a turn's model calls and tool calls under (what\n * PostHog calls a trace). Its input is the user prompt and its output the messages the turn produced.\n */\nexport type Turn = AiSpanBase;\n\n/** One tool call inside a turn (what PostHog calls a span), with the arguments and the result. */\nexport type ToolCall = AiSpanBase;\n\n/**\n * AI extension API (kind-specific methods only).\n */\nexport type Ai = {\n captureInference(inference: Inference): void;\n captureTurn(turn: Turn): void;\n captureToolCall(toolCall: ToolCall): void;\n};\n\n/** The client name an MCP event carries when no request named its client; `@posthog/mcp` drops a falsy one. */\nexport const UNKNOWN_MCP_CLIENT = 'unknown';\n\n/** What every MCP event carries: the session it groups under and the client it came from. */\nexport type McpSession = {\n /** Groups the session's events; one server process is one session over a stdio transport. */\n sessionId: string;\n clientName?: string;\n clientVersion?: string;\n protocolVersion?: string;\n};\n\n/** MCP extension API (kind-specific methods only). */\nexport type Mcp = {\n /** A client connected successfully. */\n captureInitialize(session: McpSession): void;\n captureToolCall(\n call: McpSession & { toolName: string; parameters?: unknown; durationMs: number; isError: boolean },\n ): void;\n};\n\n/**\n * Support extension API (kind-specific methods only).\n */\nexport type SupportLogTags = { ticketId: string } | { reportId: string };\n\n/** What an uploaded NDJSON bundle is, which decides where it is filed in storage. */\nexport type SupportUploadKind = 'logs' | 'trajectory';\n\nexport type Support = {\n uploadLogs(): Promise<string | undefined>;\n /** Uploads an NDJSON bundle the caller built (e.g. an agent trajectory), returning its storage key. */\n uploadNdjson(ndjson: string, kind: SupportUploadKind): Promise<string | undefined>;\n sessionContext(): SupportSessionContext | undefined;\n flushLogs(attributes: SupportLogTags): Promise<void>;\n};\n\nexport type ExtensionApi =\n | (ExtensionApiBase<'errors'> & Errors)\n | (ExtensionApiBase<'events'> & Events)\n | (ExtensionApiBase<'support'> & Support)\n | (ExtensionApiBase<'ai'> & Ai)\n // TODO(wittjosiah): Direct logs api?\n | (ExtensionApiBase<'mcp'> & Mcp)\n | ExtensionApiBase<'logs'>\n | (ExtensionApiBase<'metrics'> & Metrics)\n // TODO(wittjosiah): Direct traces api?\n | ExtensionApiBase<'traces'>;\n\n/**\n * What a browser knows about its own telemetry session. Shaped after what posthog-js's own widget\n * sends.\n */\nexport type SupportSessionContext = {\n distinctId: string;\n /** Browser-minted id the widget API uses for access control on anonymous tickets. */\n widgetSessionId: string;\n sessionId?: string;\n replayUrl?: string;\n currentUrl?: string;\n};\n\n/**\n * Attributes to be attached to observability events.\n */\nexport type Attributes = Record<string, string | number | boolean | undefined>;\n\nexport type ExtensionContext = {\n setTags(tags: Attributes, kind?: Kind): void;\n};\n\n/**\n * Implementation of an observability extension API.\n */\nexport type Extension = {\n initialize?(context: ExtensionContext): Effect.Effect<void, Error>;\n close?(): Effect.Effect<void>;\n enable?(): Effect.Effect<void>;\n disable?(): Effect.Effect<void>;\n flush?(): Effect.Effect<void>;\n identify?(distinctId: string, attributes?: Attributes, setOnceAttributes?: Attributes): void;\n alias?(distinctId: string, previousId?: string): void;\n setTags?(tags: Record<string, string>, kind?: Kind): void;\n enabled: boolean;\n apis: ExtensionApi[];\n};\n"],"mappings":"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"}
@@ -67,6 +67,8 @@ var AiContentStrippingSpanProcessor = class {
67
67
  * In order:
68
68
  * - a span that **errored** is kept, and its trace is promoted;
69
69
  * - a span slower than {@link DEFAULT_SLOW_MS} is kept, and its trace is promoted;
70
+ * - a span marked `dxos.sampling.keep` — a rare flow such as an invitation, which a dashboard counts
71
+ * one by one and a 30% sample would mostly hide — is kept, and its trace is promoted;
70
72
  * - a span carrying `gen_ai.*` or `dxos.ai.kind` — a model call, or the turn and tool calls
71
73
  * around it that the AI analytics sink reports — is kept, and its trace is promoted, so a model call is never a
72
74
  * fraction of the calls that happened. This one is ours rather than canonical: the AI events are
@@ -118,7 +120,7 @@ var TailSampler = class {
118
120
  return this._promoted.has(span.traceId) || sampledByTraceId(span.traceId, this._ratio);
119
121
  }
120
122
  _isPromotable(span) {
121
- return span.status.code === SpanStatusCode.ERROR || span.durationMs > this._slowMs || Object.keys(span.attributes).some(isAiAttribute);
123
+ return span.status.code === SpanStatusCode.ERROR || span.durationMs > this._slowMs || isKeepMarked(span.attributes) || Object.keys(span.attributes).some(isAiAttribute);
122
124
  }
123
125
  /**
124
126
  * Keeps every later span of the trace. Called for what the sampler cannot see itself: a log at
@@ -137,6 +139,8 @@ var TailSampler = class {
137
139
  var GEN_AI_PREFIX = "gen_ai.";
138
140
  /** `gen_ai.*` marks a model call; `dxos.ai.kind` the turn around it and the tool calls inside it. */
139
141
  var isAiAttribute = (key) => key.startsWith(GEN_AI_PREFIX) || key === SpanAttributes.AI.kind;
142
+ /** Manual `@dxos/tracing` spans namespace their attributes under `ctx.`, so the marker arrives either way. */
143
+ var isKeepMarked = (attributes) => attributes[SpanAttributes.SAMPLING.keep] === true || attributes[`ctx.${SpanAttributes.SAMPLING.keep}`] === true;
140
144
  /**
141
145
  * Deterministic per trace, so every span of a trace decides the same way. Reads the low 8 hex digits
142
146
  * of the 32-hex-digit trace id — the id is random, so any window is uniform, and 32 bits is well
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-OtelSpanSink.mjs","names":[],"sources":["../../src/extensions/otel/ai-content.ts","../../src/extensions/otel/tail-sampling.ts","../../src/extensions/otel/OtelSpanSink.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Attributes, type Context } from '@opentelemetry/api';\nimport { type ReadableSpan, type Span, type SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport * as SpanAttributes from '@dxos/effect/SpanAttributes';\n\n/**\n * The prompt, the response, and the tool names. They stay on the span for the AI analytics sink,\n * which applies the content policy, and never leave the process with it: a trace backend gets the\n * model call's metadata.\n */\nexport const AI_CONTENT_ATTRIBUTES: ReadonlySet<string> = new Set([\n SpanAttributes.AI.input,\n SpanAttributes.AI.output,\n SpanAttributes.AI.tools,\n]);\n\nexport const withoutAiContent = (attributes: Attributes): Attributes =>\n Object.fromEntries(Object.entries(attributes).filter(([key]) => !AI_CONTENT_ATTRIBUTES.has(key)));\n\n/** A view of the span without its content attributes; the span itself is what other processors read. */\nexport const stripAiContent = (span: ReadableSpan): ReadableSpan =>\n Object.create(span, { attributes: { value: withoutAiContent(span.attributes), enumerable: true } });\n\n/** Strips AI content from every span it forwards. Sits directly in front of an exporter. */\nexport class AiContentStrippingSpanProcessor implements SpanProcessor {\n constructor(private readonly _delegate: SpanProcessor) {}\n\n onStart(span: Span, parentContext: Context): void {\n this._delegate.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n this._delegate.onEnd(stripAiContent(span));\n }\n\n forceFlush(): Promise<void> {\n return this._delegate.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this._delegate.shutdown();\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Attributes, type Context, SpanStatusCode } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport * as SpanAttributes from '@dxos/effect/SpanAttributes';\n\n/**\n * Tail sampling for spans arriving at the observability worker.\n *\n * Head sampling cannot express either rule we want. A sampler runs when a span is *created*, so it\n * cannot know that the span will fail, and it sees the span name before the provider has annotated\n * it. Both decisions are only available once the span has ended — which is what \"tail\" means. The\n * canonical implementation of this is the OpenTelemetry Collector's tail-sampling processor, which\n * buffers whole traces server-side; there is no collector between Composer and SigNoz, so the same\n * decisions are made here instead, on the spans this realm produced.\n *\n * The rules are the canonical tail-sampling set — keep what errored, keep what was slow, sample the\n * rest — as described in OpenTelemetry's own writeup and implemented by the Collector's\n * `tailsamplingprocessor` policies (`status_code`, `latency`, `probabilistic`):\n *\n * - https://opentelemetry.io/blog/2022/tail-sampling/\n * - https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/tailsamplingprocessor\n *\n * Expressing them as a processor that filters in `onEnd` is the documented way to drop spans from a\n * JS SDK, since a `Sampler` cannot: https://github.com/open-telemetry/opentelemetry-js/discussions/2817\n *\n * In order:\n * - a span that **errored** is kept, and its trace is promoted;\n * - a span slower than {@link DEFAULT_SLOW_MS} is kept, and its trace is promoted;\n * - a span carrying `gen_ai.*` or `dxos.ai.kind` — a model call, or the turn and tool calls\n * around it that the AI analytics sink reports — is kept, and its trace is promoted, so a model call is never a\n * fraction of the calls that happened. This one is ours rather than canonical: the AI events are\n * what price the product, and a sampled fraction of them reports a fraction of the spend;\n * - any other span of a **promoted** trace is kept;\n * - everything else is kept at {@link DEFAULT_RATIO}, keyed on the trace id so the decision is the\n * same for every span of a trace.\n *\n * Promotion is remembered rather than buffered: holding span payloads until a trace settles would\n * mean an unbounded buffer and a decision delay, and a conversation's trace can stay open for\n * minutes. Remembering only the trace id costs nothing and recovers the case that matters, since a\n * parent ends *after* its children — so promoting on the first error keeps the ancestors that\n * explain it. Siblings that already ended and were dropped are not recovered; that is the price of\n * not buffering, and it is why an errored trace can arrive with gaps rather than whole.\n */\n\n/** Kept fraction for a trace that nothing promoted. Matches the head ratio this replaced. */\nexport const DEFAULT_RATIO = 0.3;\n\n/**\n * Above this, a span is kept as slow.\n *\n * Far above the 5s the Collector's examples use, because this app's slowest spans are model calls\n * and a conversation turn legitimately runs for tens of seconds — a 5s threshold would promote\n * nearly every AI trace and quietly turn the ratio off for exactly the traffic there is most of.\n * At 30s what is left is a hang rather than a slow answer.\n */\nexport const DEFAULT_SLOW_MS = 30_000;\n\n/**\n * Bound on remembered trace ids. Reached only under a burst far above normal span rates, and a trace\n * evicted early degrades to the ratio rather than to a dropped span.\n */\nconst DEFAULT_MAX_TRACKED_TRACES = 10_000;\n\nexport type Options = {\n ratio?: number;\n slowMs?: number;\n maxTrackedTraces?: number;\n};\n\n/** The parts of an ended span the rules read, whether it arrived over a port or ended in-process. */\nexport type Decidable = {\n readonly traceId: string;\n readonly status: { readonly code: SpanStatusCode };\n readonly attributes: Attributes;\n readonly durationMs: number;\n};\n\nexport class TailSampler {\n private readonly _ratio: number;\n private readonly _slowMs: number;\n private readonly _maxTrackedTraces: number;\n /** Insertion-ordered, so the oldest entry is the first key — an LRU without the bookkeeping. */\n private readonly _promoted = new Set<string>();\n\n constructor({\n ratio = DEFAULT_RATIO,\n slowMs = DEFAULT_SLOW_MS,\n maxTrackedTraces = DEFAULT_MAX_TRACKED_TRACES,\n }: Options = {}) {\n this._ratio = ratio;\n this._slowMs = slowMs;\n this._maxTrackedTraces = maxTrackedTraces;\n }\n\n /** Whether the span should be forwarded to the exporter. */\n keep(span: Decidable): boolean {\n if (this._isPromotable(span)) {\n this.promote(span.traceId);\n return true;\n }\n return this._promoted.has(span.traceId) || sampledByTraceId(span.traceId, this._ratio);\n }\n\n private _isPromotable(span: Decidable): boolean {\n return (\n span.status.code === SpanStatusCode.ERROR ||\n span.durationMs > this._slowMs ||\n Object.keys(span.attributes).some(isAiAttribute)\n );\n }\n\n /**\n * Keeps every later span of the trace. Called for what the sampler cannot see itself: a log at\n * warning or above names a trace worth keeping the same way an errored span does.\n */\n promote(traceId: string): void {\n // Re-inserting moves the id to the end, so an active trace is not evicted ahead of a stale one.\n this._promoted.delete(traceId);\n this._promoted.add(traceId);\n while (this._promoted.size > this._maxTrackedTraces) {\n const oldest = this._promoted.values().next();\n if (oldest.done) {\n break;\n }\n this._promoted.delete(oldest.value);\n }\n }\n}\n\nconst GEN_AI_PREFIX = 'gen_ai.';\n\n/** `gen_ai.*` marks a model call; `dxos.ai.kind` the turn around it and the tool calls inside it. */\nconst isAiAttribute = (key: string): boolean => key.startsWith(GEN_AI_PREFIX) || key === SpanAttributes.AI.kind;\n\n/**\n * Deterministic per trace, so every span of a trace decides the same way. Reads the low 8 hex digits\n * of the 32-hex-digit trace id — the id is random, so any window is uniform, and 32 bits is well\n * inside the exact range of a double.\n */\nconst sampledByTraceId = (traceId: string, ratio: number): boolean => {\n if (ratio >= 1) {\n return true;\n }\n if (ratio <= 0 || traceId.length < 8) {\n return false;\n }\n const value = Number.parseInt(traceId.slice(-8), 16);\n return Number.isNaN(value) ? false : value / 0x1_0000_0000 < ratio;\n};\n\n/** OTel reports durations as `[seconds, nanoseconds]`. */\nexport const hrTimeToMs = ([seconds, nanos]: [number, number]): number => seconds * 1_000 + nanos / 1e6;\n\n/**\n * Applies the same rules in-process, for the path that exports without a worker (node, and a browser\n * with no observability worker). Without it that path would export everything, since head sampling\n * no longer thins anything.\n */\nexport class TailSamplingSpanProcessor implements SpanProcessor {\n private readonly _sampler: TailSampler;\n\n constructor(\n private readonly _delegate: SpanProcessor,\n options?: Options,\n ) {\n this._sampler = new TailSampler(options);\n }\n\n onStart(span: Span, parentContext: Context): void {\n this._delegate.onStart(span, parentContext);\n }\n\n /** See {@link TailSampler.promote}. */\n promote(traceId: string): void {\n this._sampler.promote(traceId);\n }\n\n onEnd(span: ReadableSpan): void {\n const { traceId } = span.spanContext();\n const decidable = {\n traceId,\n status: span.status,\n attributes: span.attributes,\n durationMs: hrTimeToMs(span.duration),\n };\n if (this._sampler.keep(decidable)) {\n this._delegate.onEnd(span);\n }\n }\n\n forceFlush(): Promise<void> {\n return this._delegate.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this._delegate.shutdown();\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\n// The log-writer worker imports this through its own subpath, so nothing else loads the\n// OpenTelemetry SDK.\n\nimport {\n type Attributes,\n type Context,\n type HrTime,\n type Link,\n type SpanContext,\n type SpanKind,\n type SpanStatus,\n} from '@opentelemetry/api';\nimport { TraceState, hrTimeDuration } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { type Resource, defaultResource, resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n BatchSpanProcessor,\n type ReadableSpan,\n type Span as SdkSpan,\n type SpanExporter,\n type SpanProcessor,\n} from '@opentelemetry/sdk-trace-base';\n\nimport * as AiContent from './ai-content.ts';\nimport { type OtelDestination, signalUrl } from './otel.ts';\nimport * as TailSampling from './tail-sampling.ts';\n\nexport type Init = {\n type: 'otel-traces-init';\n destinations: OtelDestination[];\n resourceAttributes: Record<string, string>;\n};\n\nexport type Span = {\n type: 'otel-span';\n name: string;\n kind: SpanKind;\n traceId: string;\n spanId: string;\n traceFlags: number;\n traceState?: string;\n parentSpanContext?: { traceId: string; spanId: string; traceFlags: number };\n startTime: HrTime;\n endTime: HrTime;\n status: SpanStatus;\n attributes: Attributes;\n events: { name: string; time: HrTime; attributes?: Attributes; droppedAttributesCount?: number }[];\n links: { context: { traceId: string; spanId: string; traceFlags: number }; attributes?: Attributes }[];\n droppedAttributesCount: number;\n droppedEventsCount: number;\n droppedLinksCount: number;\n instrumentationScope: { name: string; version?: string };\n};\n\nexport type Message = Init | Span;\n\nexport type Handle = { post: (record: Span) => void };\n\nexport const serializeReadableSpan = (span: ReadableSpan): Span => {\n const spanContext = span.spanContext();\n return {\n type: 'otel-span',\n name: span.name,\n kind: span.kind,\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n traceFlags: spanContext.traceFlags,\n traceState: spanContext.traceState?.serialize(),\n parentSpanContext: span.parentSpanContext\n ? {\n traceId: span.parentSpanContext.traceId,\n spanId: span.parentSpanContext.spanId,\n traceFlags: span.parentSpanContext.traceFlags,\n }\n : undefined,\n startTime: span.startTime,\n endTime: span.endTime,\n status: span.status,\n attributes: { ...span.attributes },\n events: span.events.map((event) => ({\n name: event.name,\n time: event.time,\n attributes: event.attributes ? { ...event.attributes } : undefined,\n droppedAttributesCount: event.droppedAttributesCount,\n })),\n links: span.links.map((link) => ({\n context: {\n traceId: link.context.traceId,\n spanId: link.context.spanId,\n traceFlags: link.context.traceFlags,\n },\n attributes: link.attributes ? { ...link.attributes } : undefined,\n })),\n droppedAttributesCount: span.droppedAttributesCount,\n droppedEventsCount: span.droppedEventsCount,\n droppedLinksCount: span.droppedLinksCount,\n instrumentationScope: {\n name: span.instrumentationScope.name,\n version: span.instrumentationScope.version,\n },\n };\n};\n\nexport class PortSpanProcessor implements SpanProcessor {\n constructor(private readonly _post: (record: Span) => void) {}\n\n onStart(_span: SdkSpan, _parentContext: Context): void {}\n\n onEnd(span: ReadableSpan): void {\n this._post(serializeReadableSpan(span));\n }\n\n async shutdown(): Promise<void> {}\n\n async forceFlush(): Promise<void> {}\n}\n\nexport type Options = {\n exporter?: SpanExporter;\n /** Overrides the default tail-sampling rules; pass `{ ratio: 1 }` to export everything. */\n sampling?: TailSampling.Options;\n};\n\nexport class Sink {\n readonly #resource: Resource;\n readonly #processors: BatchSpanProcessor[];\n readonly #sampler: TailSampling.TailSampler;\n\n constructor(init: Init, options: Options = {}) {\n this.#sampler = new TailSampling.TailSampler(options.sampling);\n this.#resource = defaultResource().merge(resourceFromAttributes(init.resourceAttributes));\n this.#processors = init.destinations.map(\n (destination) =>\n new BatchSpanProcessor(\n options.exporter ??\n new OTLPTraceExporter({\n url: signalUrl(destination, 'traces'),\n headers: destination.headers,\n concurrencyLimit: 10,\n }),\n ),\n );\n }\n\n /** Keeps the rest of a trace something outside the span stream vouched for, e.g. a warning log. */\n promote(traceId: string): void {\n this.#sampler.promote(traceId);\n }\n\n append(record: Span): void {\n if (\n !this.#sampler.keep({\n traceId: record.traceId,\n status: record.status,\n attributes: record.attributes,\n durationMs: TailSampling.hrTimeToMs(hrTimeDuration(record.startTime, record.endTime)),\n })\n ) {\n return;\n }\n const span = this.#materialize(record);\n for (const processor of this.#processors) {\n processor.onEnd(span);\n }\n }\n\n async flush(): Promise<void> {\n await Promise.all(this.#processors.map((processor) => processor.forceFlush()));\n }\n\n async close(): Promise<void> {\n await Promise.all(this.#processors.map((processor) => processor.shutdown()));\n }\n\n #materialize(record: Span): ReadableSpan {\n const spanContext: SpanContext = {\n traceId: record.traceId,\n spanId: record.spanId,\n traceFlags: record.traceFlags,\n traceState: record.traceState !== undefined ? new TraceState(record.traceState) : undefined,\n };\n const links: Link[] = record.links.map((link) => ({\n context: { ...link.context },\n attributes: link.attributes,\n }));\n return {\n name: record.name,\n kind: record.kind,\n spanContext: () => spanContext,\n parentSpanContext: record.parentSpanContext ? { ...record.parentSpanContext } : undefined,\n startTime: record.startTime,\n endTime: record.endTime,\n status: record.status,\n attributes: AiContent.withoutAiContent(record.attributes),\n links,\n events: record.events.map((event) => ({\n name: event.name,\n time: event.time,\n attributes: event.attributes,\n droppedAttributesCount: event.droppedAttributesCount,\n })),\n duration: hrTimeDuration(record.startTime, record.endTime),\n ended: true,\n resource: this.#resource,\n instrumentationScope: { ...record.instrumentationScope },\n droppedAttributesCount: record.droppedAttributesCount,\n droppedEventsCount: record.droppedEventsCount,\n droppedLinksCount: record.droppedLinksCount,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chunk-OtelSpanSink.mjs","names":[],"sources":["../../src/extensions/otel/ai-content.ts","../../src/extensions/otel/tail-sampling.ts","../../src/extensions/otel/OtelSpanSink.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Attributes, type Context } from '@opentelemetry/api';\nimport { type ReadableSpan, type Span, type SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport * as SpanAttributes from '@dxos/effect/SpanAttributes';\n\n/**\n * The prompt, the response, and the tool names. They stay on the span for the AI analytics sink,\n * which applies the content policy, and never leave the process with it: a trace backend gets the\n * model call's metadata.\n */\nexport const AI_CONTENT_ATTRIBUTES: ReadonlySet<string> = new Set([\n SpanAttributes.AI.input,\n SpanAttributes.AI.output,\n SpanAttributes.AI.tools,\n]);\n\nexport const withoutAiContent = (attributes: Attributes): Attributes =>\n Object.fromEntries(Object.entries(attributes).filter(([key]) => !AI_CONTENT_ATTRIBUTES.has(key)));\n\n/** A view of the span without its content attributes; the span itself is what other processors read. */\nexport const stripAiContent = (span: ReadableSpan): ReadableSpan =>\n Object.create(span, { attributes: { value: withoutAiContent(span.attributes), enumerable: true } });\n\n/** Strips AI content from every span it forwards. Sits directly in front of an exporter. */\nexport class AiContentStrippingSpanProcessor implements SpanProcessor {\n constructor(private readonly _delegate: SpanProcessor) {}\n\n onStart(span: Span, parentContext: Context): void {\n this._delegate.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n this._delegate.onEnd(stripAiContent(span));\n }\n\n forceFlush(): Promise<void> {\n return this._delegate.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this._delegate.shutdown();\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Attributes, type Context, SpanStatusCode } from '@opentelemetry/api';\nimport type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';\n\nimport * as SpanAttributes from '@dxos/effect/SpanAttributes';\n\n/**\n * Tail sampling for spans arriving at the observability worker.\n *\n * Head sampling cannot express either rule we want. A sampler runs when a span is *created*, so it\n * cannot know that the span will fail, and it sees the span name before the provider has annotated\n * it. Both decisions are only available once the span has ended — which is what \"tail\" means. The\n * canonical implementation of this is the OpenTelemetry Collector's tail-sampling processor, which\n * buffers whole traces server-side; there is no collector between Composer and SigNoz, so the same\n * decisions are made here instead, on the spans this realm produced.\n *\n * The rules are the canonical tail-sampling set — keep what errored, keep what was slow, sample the\n * rest — as described in OpenTelemetry's own writeup and implemented by the Collector's\n * `tailsamplingprocessor` policies (`status_code`, `latency`, `probabilistic`):\n *\n * - https://opentelemetry.io/blog/2022/tail-sampling/\n * - https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/tailsamplingprocessor\n *\n * Expressing them as a processor that filters in `onEnd` is the documented way to drop spans from a\n * JS SDK, since a `Sampler` cannot: https://github.com/open-telemetry/opentelemetry-js/discussions/2817\n *\n * In order:\n * - a span that **errored** is kept, and its trace is promoted;\n * - a span slower than {@link DEFAULT_SLOW_MS} is kept, and its trace is promoted;\n * - a span marked `dxos.sampling.keep` — a rare flow such as an invitation, which a dashboard counts\n * one by one and a 30% sample would mostly hide — is kept, and its trace is promoted;\n * - a span carrying `gen_ai.*` or `dxos.ai.kind` — a model call, or the turn and tool calls\n * around it that the AI analytics sink reports — is kept, and its trace is promoted, so a model call is never a\n * fraction of the calls that happened. This one is ours rather than canonical: the AI events are\n * what price the product, and a sampled fraction of them reports a fraction of the spend;\n * - any other span of a **promoted** trace is kept;\n * - everything else is kept at {@link DEFAULT_RATIO}, keyed on the trace id so the decision is the\n * same for every span of a trace.\n *\n * Promotion is remembered rather than buffered: holding span payloads until a trace settles would\n * mean an unbounded buffer and a decision delay, and a conversation's trace can stay open for\n * minutes. Remembering only the trace id costs nothing and recovers the case that matters, since a\n * parent ends *after* its children — so promoting on the first error keeps the ancestors that\n * explain it. Siblings that already ended and were dropped are not recovered; that is the price of\n * not buffering, and it is why an errored trace can arrive with gaps rather than whole.\n */\n\n/** Kept fraction for a trace that nothing promoted. Matches the head ratio this replaced. */\nexport const DEFAULT_RATIO = 0.3;\n\n/**\n * Above this, a span is kept as slow.\n *\n * Far above the 5s the Collector's examples use, because this app's slowest spans are model calls\n * and a conversation turn legitimately runs for tens of seconds — a 5s threshold would promote\n * nearly every AI trace and quietly turn the ratio off for exactly the traffic there is most of.\n * At 30s what is left is a hang rather than a slow answer.\n */\nexport const DEFAULT_SLOW_MS = 30_000;\n\n/**\n * Bound on remembered trace ids. Reached only under a burst far above normal span rates, and a trace\n * evicted early degrades to the ratio rather than to a dropped span.\n */\nconst DEFAULT_MAX_TRACKED_TRACES = 10_000;\n\nexport type Options = {\n ratio?: number;\n slowMs?: number;\n maxTrackedTraces?: number;\n};\n\n/** The parts of an ended span the rules read, whether it arrived over a port or ended in-process. */\nexport type Decidable = {\n readonly traceId: string;\n readonly status: { readonly code: SpanStatusCode };\n readonly attributes: Attributes;\n readonly durationMs: number;\n};\n\nexport class TailSampler {\n private readonly _ratio: number;\n private readonly _slowMs: number;\n private readonly _maxTrackedTraces: number;\n /** Insertion-ordered, so the oldest entry is the first key — an LRU without the bookkeeping. */\n private readonly _promoted = new Set<string>();\n\n constructor({\n ratio = DEFAULT_RATIO,\n slowMs = DEFAULT_SLOW_MS,\n maxTrackedTraces = DEFAULT_MAX_TRACKED_TRACES,\n }: Options = {}) {\n this._ratio = ratio;\n this._slowMs = slowMs;\n this._maxTrackedTraces = maxTrackedTraces;\n }\n\n /** Whether the span should be forwarded to the exporter. */\n keep(span: Decidable): boolean {\n if (this._isPromotable(span)) {\n this.promote(span.traceId);\n return true;\n }\n return this._promoted.has(span.traceId) || sampledByTraceId(span.traceId, this._ratio);\n }\n\n private _isPromotable(span: Decidable): boolean {\n return (\n span.status.code === SpanStatusCode.ERROR ||\n span.durationMs > this._slowMs ||\n isKeepMarked(span.attributes) ||\n Object.keys(span.attributes).some(isAiAttribute)\n );\n }\n\n /**\n * Keeps every later span of the trace. Called for what the sampler cannot see itself: a log at\n * warning or above names a trace worth keeping the same way an errored span does.\n */\n promote(traceId: string): void {\n // Re-inserting moves the id to the end, so an active trace is not evicted ahead of a stale one.\n this._promoted.delete(traceId);\n this._promoted.add(traceId);\n while (this._promoted.size > this._maxTrackedTraces) {\n const oldest = this._promoted.values().next();\n if (oldest.done) {\n break;\n }\n this._promoted.delete(oldest.value);\n }\n }\n}\n\nconst GEN_AI_PREFIX = 'gen_ai.';\n\n/** `gen_ai.*` marks a model call; `dxos.ai.kind` the turn around it and the tool calls inside it. */\nconst isAiAttribute = (key: string): boolean => key.startsWith(GEN_AI_PREFIX) || key === SpanAttributes.AI.kind;\n\n/** Manual `@dxos/tracing` spans namespace their attributes under `ctx.`, so the marker arrives either way. */\nconst isKeepMarked = (attributes: Attributes): boolean =>\n attributes[SpanAttributes.SAMPLING.keep] === true || attributes[`ctx.${SpanAttributes.SAMPLING.keep}`] === true;\n\n/**\n * Deterministic per trace, so every span of a trace decides the same way. Reads the low 8 hex digits\n * of the 32-hex-digit trace id — the id is random, so any window is uniform, and 32 bits is well\n * inside the exact range of a double.\n */\nconst sampledByTraceId = (traceId: string, ratio: number): boolean => {\n if (ratio >= 1) {\n return true;\n }\n if (ratio <= 0 || traceId.length < 8) {\n return false;\n }\n const value = Number.parseInt(traceId.slice(-8), 16);\n return Number.isNaN(value) ? false : value / 0x1_0000_0000 < ratio;\n};\n\n/** OTel reports durations as `[seconds, nanoseconds]`. */\nexport const hrTimeToMs = ([seconds, nanos]: [number, number]): number => seconds * 1_000 + nanos / 1e6;\n\n/**\n * Applies the same rules in-process, for the path that exports without a worker (node, and a browser\n * with no observability worker). Without it that path would export everything, since head sampling\n * no longer thins anything.\n */\nexport class TailSamplingSpanProcessor implements SpanProcessor {\n private readonly _sampler: TailSampler;\n\n constructor(\n private readonly _delegate: SpanProcessor,\n options?: Options,\n ) {\n this._sampler = new TailSampler(options);\n }\n\n onStart(span: Span, parentContext: Context): void {\n this._delegate.onStart(span, parentContext);\n }\n\n /** See {@link TailSampler.promote}. */\n promote(traceId: string): void {\n this._sampler.promote(traceId);\n }\n\n onEnd(span: ReadableSpan): void {\n const { traceId } = span.spanContext();\n const decidable = {\n traceId,\n status: span.status,\n attributes: span.attributes,\n durationMs: hrTimeToMs(span.duration),\n };\n if (this._sampler.keep(decidable)) {\n this._delegate.onEnd(span);\n }\n }\n\n forceFlush(): Promise<void> {\n return this._delegate.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this._delegate.shutdown();\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\n// The log-writer worker imports this through its own subpath, so nothing else loads the\n// OpenTelemetry SDK.\n\nimport {\n type Attributes,\n type Context,\n type HrTime,\n type Link,\n type SpanContext,\n type SpanKind,\n type SpanStatus,\n} from '@opentelemetry/api';\nimport { TraceState, hrTimeDuration } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { type Resource, defaultResource, resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n BatchSpanProcessor,\n type ReadableSpan,\n type Span as SdkSpan,\n type SpanExporter,\n type SpanProcessor,\n} from '@opentelemetry/sdk-trace-base';\n\nimport * as AiContent from './ai-content.ts';\nimport { type OtelDestination, signalUrl } from './otel.ts';\nimport * as TailSampling from './tail-sampling.ts';\n\nexport type Init = {\n type: 'otel-traces-init';\n destinations: OtelDestination[];\n resourceAttributes: Record<string, string>;\n};\n\nexport type Span = {\n type: 'otel-span';\n name: string;\n kind: SpanKind;\n traceId: string;\n spanId: string;\n traceFlags: number;\n traceState?: string;\n parentSpanContext?: { traceId: string; spanId: string; traceFlags: number };\n startTime: HrTime;\n endTime: HrTime;\n status: SpanStatus;\n attributes: Attributes;\n events: { name: string; time: HrTime; attributes?: Attributes; droppedAttributesCount?: number }[];\n links: { context: { traceId: string; spanId: string; traceFlags: number }; attributes?: Attributes }[];\n droppedAttributesCount: number;\n droppedEventsCount: number;\n droppedLinksCount: number;\n instrumentationScope: { name: string; version?: string };\n};\n\nexport type Message = Init | Span;\n\nexport type Handle = { post: (record: Span) => void };\n\nexport const serializeReadableSpan = (span: ReadableSpan): Span => {\n const spanContext = span.spanContext();\n return {\n type: 'otel-span',\n name: span.name,\n kind: span.kind,\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n traceFlags: spanContext.traceFlags,\n traceState: spanContext.traceState?.serialize(),\n parentSpanContext: span.parentSpanContext\n ? {\n traceId: span.parentSpanContext.traceId,\n spanId: span.parentSpanContext.spanId,\n traceFlags: span.parentSpanContext.traceFlags,\n }\n : undefined,\n startTime: span.startTime,\n endTime: span.endTime,\n status: span.status,\n attributes: { ...span.attributes },\n events: span.events.map((event) => ({\n name: event.name,\n time: event.time,\n attributes: event.attributes ? { ...event.attributes } : undefined,\n droppedAttributesCount: event.droppedAttributesCount,\n })),\n links: span.links.map((link) => ({\n context: {\n traceId: link.context.traceId,\n spanId: link.context.spanId,\n traceFlags: link.context.traceFlags,\n },\n attributes: link.attributes ? { ...link.attributes } : undefined,\n })),\n droppedAttributesCount: span.droppedAttributesCount,\n droppedEventsCount: span.droppedEventsCount,\n droppedLinksCount: span.droppedLinksCount,\n instrumentationScope: {\n name: span.instrumentationScope.name,\n version: span.instrumentationScope.version,\n },\n };\n};\n\nexport class PortSpanProcessor implements SpanProcessor {\n constructor(private readonly _post: (record: Span) => void) {}\n\n onStart(_span: SdkSpan, _parentContext: Context): void {}\n\n onEnd(span: ReadableSpan): void {\n this._post(serializeReadableSpan(span));\n }\n\n async shutdown(): Promise<void> {}\n\n async forceFlush(): Promise<void> {}\n}\n\nexport type Options = {\n exporter?: SpanExporter;\n /** Overrides the default tail-sampling rules; pass `{ ratio: 1 }` to export everything. */\n sampling?: TailSampling.Options;\n};\n\nexport class Sink {\n readonly #resource: Resource;\n readonly #processors: BatchSpanProcessor[];\n readonly #sampler: TailSampling.TailSampler;\n\n constructor(init: Init, options: Options = {}) {\n this.#sampler = new TailSampling.TailSampler(options.sampling);\n this.#resource = defaultResource().merge(resourceFromAttributes(init.resourceAttributes));\n this.#processors = init.destinations.map(\n (destination) =>\n new BatchSpanProcessor(\n options.exporter ??\n new OTLPTraceExporter({\n url: signalUrl(destination, 'traces'),\n headers: destination.headers,\n concurrencyLimit: 10,\n }),\n ),\n );\n }\n\n /** Keeps the rest of a trace something outside the span stream vouched for, e.g. a warning log. */\n promote(traceId: string): void {\n this.#sampler.promote(traceId);\n }\n\n append(record: Span): void {\n if (\n !this.#sampler.keep({\n traceId: record.traceId,\n status: record.status,\n attributes: record.attributes,\n durationMs: TailSampling.hrTimeToMs(hrTimeDuration(record.startTime, record.endTime)),\n })\n ) {\n return;\n }\n const span = this.#materialize(record);\n for (const processor of this.#processors) {\n processor.onEnd(span);\n }\n }\n\n async flush(): Promise<void> {\n await Promise.all(this.#processors.map((processor) => processor.forceFlush()));\n }\n\n async close(): Promise<void> {\n await Promise.all(this.#processors.map((processor) => processor.shutdown()));\n }\n\n #materialize(record: Span): ReadableSpan {\n const spanContext: SpanContext = {\n traceId: record.traceId,\n spanId: record.spanId,\n traceFlags: record.traceFlags,\n traceState: record.traceState !== undefined ? new TraceState(record.traceState) : undefined,\n };\n const links: Link[] = record.links.map((link) => ({\n context: { ...link.context },\n attributes: link.attributes,\n }));\n return {\n name: record.name,\n kind: record.kind,\n spanContext: () => spanContext,\n parentSpanContext: record.parentSpanContext ? { ...record.parentSpanContext } : undefined,\n startTime: record.startTime,\n endTime: record.endTime,\n status: record.status,\n attributes: AiContent.withoutAiContent(record.attributes),\n links,\n events: record.events.map((event) => ({\n name: event.name,\n time: event.time,\n attributes: event.attributes,\n droppedAttributesCount: event.droppedAttributesCount,\n })),\n duration: hrTimeDuration(record.startTime, record.endTime),\n ended: true,\n resource: this.#resource,\n instrumentationScope: { ...record.instrumentationScope },\n droppedAttributesCount: record.droppedAttributesCount,\n droppedEventsCount: record.droppedEventsCount,\n droppedLinksCount: record.droppedLinksCount,\n };\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Observability.d.ts","sourceRoot":"","sources":["../../../src/Observability.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,aAAa,CAAC;AAI/D,OAAO,KAAK,sBAAsB,MAAM,6BAA6B,CAAC;AAEtE,cAAc,oBAAoB,CAAC;AAoBnC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,aAAa,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;AAEpG;;GAEG;AAEH,MAAM,WAAW,aAAa;IAC5B,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,eAAe,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxE,QAAQ,CACN,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,sBAAsB,CAAC,UAAU,EAC9C,iBAAiB,CAAC,EAAE,sBAAsB,CAAC,UAAU,GACpD,IAAI,CAAC;IACR,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,sBAAsB,CAAC,IAAI,GAAG,IAAI,CAAC;IAC3F,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,sBAAsB,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,sBAAsB,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC;IACxC,EAAE,EAAE,sBAAsB,CAAC,EAAE,CAAC;IAC9B,GAAG,EAAE,sBAAsB,CAAC,GAAG,CAAC;IAChC,6EAA6E;IAC7E,WAAW,CAAC,IAAI,EAAE,sBAAsB,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvE,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC;CACzC;AA6RD,eAAO,MAAM,IAAI,QAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAA4C,CAAC;AAEhG,eAAO,MAAM,YAAY,eAAgB,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAC,kJAOpF,CAAC;AAEL,eAAO,MAAM,eAAe,iBAAkB,YAAY,qJAMtD,CAAC;AAEL,eAAO,MAAM,UAAU,4GAIrB,CAAC"}
1
+ {"version":3,"file":"Observability.d.ts","sourceRoot":"","sources":["../../../src/Observability.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,aAAa,CAAC;AAI/D,OAAO,KAAK,sBAAsB,MAAM,6BAA6B,CAAC;AAEtE,cAAc,oBAAoB,CAAC;AAoBnC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,aAAa,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;AAEpG;;GAEG;AAEH,MAAM,WAAW,aAAa;IAC5B,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,eAAe,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxE,QAAQ,CACN,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,sBAAsB,CAAC,UAAU,EAC9C,iBAAiB,CAAC,EAAE,sBAAsB,CAAC,UAAU,GACpD,IAAI,CAAC;IACR,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,sBAAsB,CAAC,IAAI,GAAG,IAAI,CAAC;IAC3F,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,sBAAsB,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,sBAAsB,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC;IACxC,EAAE,EAAE,sBAAsB,CAAC,EAAE,CAAC;IAC9B,GAAG,EAAE,sBAAsB,CAAC,GAAG,CAAC;IAChC,6EAA6E;IAC7E,WAAW,CAAC,IAAI,EAAE,sBAAsB,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvE,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC;CACzC;AAoSD,eAAO,MAAM,IAAI,QAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAA4C,CAAC;AAEhG,eAAO,MAAM,YAAY,eAAgB,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAC,kJAOpF,CAAC;AAEL,eAAO,MAAM,eAAe,iBAAkB,YAAY,qJAMtD,CAAC;AAEL,eAAO,MAAM,UAAU,4GAIrB,CAAC"}
@@ -198,6 +198,13 @@ class ObservabilityImpl {
198
198
  }
199
199
  return key;
200
200
  },
201
+ uploadNdjson: async (ndjson, kind) => {
202
+ let key;
203
+ for (const extension of this._getExtensions('support')) {
204
+ key = (await extension.uploadNdjson(ndjson, kind)) ?? key;
205
+ }
206
+ return key;
207
+ },
201
208
  sessionContext: () => {
202
209
  for (const extension of this._getExtensions('support')) {
203
210
  const context = extension.sessionContext();
@@ -1 +1 @@
1
- {"version":3,"file":"Observability.js","sourceRoot":"","sources":["../../../src/Observability.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Observability.js","sourceRoot":"","sources":["../../../src/Observability.ts"],"names":[],"mappings":"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"}
@@ -206,11 +206,23 @@ describe('Observability', () => {
206
206
  const uploadLogs = vi.fn(async () => 'logs/1.ndjson');
207
207
  const sessionContext = vi.fn(() => ({ distinctId: 'd', widgetSessionId: 'w' }));
208
208
  const flushLogs = vi.fn(async () => { });
209
+ const uploadNdjson = vi.fn(async () => 'trajectories/1.ndjson.gz');
209
210
  const supportExt = createMockExtension({
210
- apis: [{ kind: 'support', isAvailable: () => Effect.succeed(true), uploadLogs, sessionContext, flushLogs }],
211
+ apis: [
212
+ {
213
+ kind: 'support',
214
+ isAvailable: () => Effect.succeed(true),
215
+ uploadLogs,
216
+ uploadNdjson,
217
+ sessionContext,
218
+ flushLogs,
219
+ },
220
+ ],
211
221
  });
212
222
  const obs = yield* Function.pipe(Observability.make(), Observability.addExtension(Effect.succeed(supportExt)), Observability.initialize);
213
223
  expect(yield* Effect.promise(() => obs.support.uploadLogs())).toBe('logs/1.ndjson');
224
+ expect(yield* Effect.promise(() => obs.support.uploadNdjson('{}', 'trajectory'))).toBe('trajectories/1.ndjson.gz');
225
+ expect(uploadNdjson).toHaveBeenCalledWith('{}', 'trajectory');
214
226
  expect(obs.support.sessionContext()).toEqual({ distinctId: 'd', widgetSessionId: 'w' });
215
227
  yield* Effect.promise(() => obs.support.flushLogs({ ticketId: 'ticket-1' }));
216
228
  expect(flushLogs).toHaveBeenCalledWith({ ticketId: 'ticket-1' });