neatlogs 1.1.19 → 1.1.21

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 (69) hide show
  1. package/README.md +34 -2
  2. package/dist/ai-sdk.cjs +186 -11
  3. package/dist/ai-sdk.cjs.map +1 -1
  4. package/dist/ai-sdk.d.ts +17 -7
  5. package/dist/ai-sdk.mjs +189 -12
  6. package/dist/ai-sdk.mjs.map +1 -1
  7. package/dist/anthropic.cjs +31 -14
  8. package/dist/anthropic.cjs.map +1 -1
  9. package/dist/anthropic.mjs +31 -14
  10. package/dist/anthropic.mjs.map +1 -1
  11. package/dist/azure-openai.cjs +31 -14
  12. package/dist/azure-openai.cjs.map +1 -1
  13. package/dist/azure-openai.mjs +31 -14
  14. package/dist/azure-openai.mjs.map +1 -1
  15. package/dist/bedrock.cjs +31 -14
  16. package/dist/bedrock.cjs.map +1 -1
  17. package/dist/bedrock.mjs +31 -14
  18. package/dist/bedrock.mjs.map +1 -1
  19. package/dist/browser.cjs +26 -1
  20. package/dist/browser.cjs.map +1 -1
  21. package/dist/browser.d.ts +1 -1
  22. package/dist/browser.mjs +26 -1
  23. package/dist/browser.mjs.map +1 -1
  24. package/dist/claude-agent-sdk.cjs +2 -2
  25. package/dist/claude-agent-sdk.cjs.map +1 -1
  26. package/dist/claude-agent-sdk.mjs +2 -2
  27. package/dist/claude-agent-sdk.mjs.map +1 -1
  28. package/dist/cli.cjs +452 -200
  29. package/dist/cli.cjs.map +1 -1
  30. package/dist/cli.mjs +427 -173
  31. package/dist/cli.mjs.map +1 -1
  32. package/dist/google-genai.cjs +31 -14
  33. package/dist/google-genai.cjs.map +1 -1
  34. package/dist/google-genai.mjs +31 -14
  35. package/dist/google-genai.mjs.map +1 -1
  36. package/dist/index.cjs +652 -333
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.ts +15 -6
  39. package/dist/index.mjs +505 -184
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/langchain.cjs +3 -3
  42. package/dist/langchain.cjs.map +1 -1
  43. package/dist/langchain.mjs +3 -3
  44. package/dist/langchain.mjs.map +1 -1
  45. package/dist/mastra-wrap.cjs +2 -2
  46. package/dist/mastra-wrap.cjs.map +1 -1
  47. package/dist/mastra-wrap.mjs +2 -2
  48. package/dist/mastra-wrap.mjs.map +1 -1
  49. package/dist/openai-agents.cjs.map +1 -1
  50. package/dist/openai-agents.mjs.map +1 -1
  51. package/dist/openai.cjs +31 -14
  52. package/dist/openai.cjs.map +1 -1
  53. package/dist/openai.mjs +31 -14
  54. package/dist/openai.mjs.map +1 -1
  55. package/dist/opencode-plugin.cjs +1 -1
  56. package/dist/opencode-plugin.cjs.map +1 -1
  57. package/dist/opencode-plugin.mjs +1 -1
  58. package/dist/opencode-plugin.mjs.map +1 -1
  59. package/dist/openrouter-agent.cjs +31 -14
  60. package/dist/openrouter-agent.cjs.map +1 -1
  61. package/dist/openrouter-agent.mjs +31 -14
  62. package/dist/openrouter-agent.mjs.map +1 -1
  63. package/dist/pi-agent.cjs.map +1 -1
  64. package/dist/pi-agent.mjs.map +1 -1
  65. package/dist/vertex-ai.cjs +31 -14
  66. package/dist/vertex-ai.cjs.map +1 -1
  67. package/dist/vertex-ai.mjs +31 -14
  68. package/dist/vertex-ai.mjs.map +1 -1
  69. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/azure-openai.ts","../src/core/auto-root.ts","../src/core/context.ts","../src/prompt/template.ts","../src/core/logger.ts","../src/core/mask.ts","../src/core/identity.ts","../src/core/provider.ts","../src/core/active-client.ts","../src/core/end-user.ts","../src/core/session.ts","../src/core/media.ts","../src/core/upload-authority.ts","../src/decorators/base.ts","../src/constants.ts","../src/core/choice-accumulator.ts"],"sourcesContent":["/**\n * Neatlogs Azure OpenAI wrapper — ES6 Proxy-based.\n *\n * Azure OpenAI is accessed through the `openai` SDK's `AzureOpenAI` client,\n * which exposes the same `chat.completions.create()` shape as `OpenAI`. This\n * wrapper traces those calls with provider `azure` so Azure traffic is\n * distinguished from direct OpenAI traffic.\n *\n * Usage:\n * import { wrapAzureOpenAI } from 'neatlogs/azure-openai';\n * import { AzureOpenAI } from 'openai';\n * const client = wrapAzureOpenAI(new AzureOpenAI({ endpoint, apiKey, apiVersion }));\n * await client.chat.completions.create({ model: 'gpt-4o', messages: [...] });\n *\n * Intercepts:\n * - client.chat.completions.create() — non-streaming and streaming\n * - client.responses.create() — Responses API\n */\n\nimport { SpanStatusCode, type Span } from '@opentelemetry/api';\nimport { getProviderTracer } from './core/auto-root.js';\nimport { captureMedia } from './core/media.js';\nimport { ChoiceAccumulator } from './core/choice-accumulator.js';\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n withNeatlogsSpan,\n} from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.azure_openai';\nconst PROVIDER = 'azure';\nconst SYSTEM = 'azure';\n\nexport function wrapAzureOpenAI<T extends object>(client: T): T {\n return wrapNamespace(client, []) as T;\n}\n\n/**\n * Wrap a tool/function implementation to emit TOOL spans when executed.\n *\n * Usage:\n * const getWeather = traceTool('get_weather', async (args: { city: string }) => {\n * return `Weather in ${args.city}: sunny`;\n * });\n */\nexport function traceTool<TArgs = any, TResult = any>(\n name: string,\n fn: (args: TArgs) => TResult | Promise<TResult>,\n): (args: TArgs) => Promise<TResult> {\n return async function tracedTool(args: TArgs): Promise<TResult> {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const span = tracer.startSpan(\n `tool.${name}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n },\n },\n getNeatlogsParentContext(),\n );\n span.setAttribute(\n 'input.value',\n safeStringify(captureMedia(span, 'neatlogs.tool.input', args, 'input')),\n );\n return withNeatlogsSpan(span, async () => {\n try {\n const result = await fn(args);\n span.setAttribute(\n 'output.value',\n safeStringify(captureMedia(span, 'neatlogs.tool.output', result, 'output')),\n );\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n } finally {\n span.end();\n }\n });\n };\n}\n\nfunction wrapNamespace(target: any, path: string[]): any {\n return new Proxy(target, {\n get(obj, prop, receiver) {\n const value = Reflect.get(obj, prop, receiver);\n if (typeof prop === 'symbol' || String(prop).startsWith('_')) return value;\n\n const currentPath = [...path, String(prop)];\n const pathStr = currentPath.join('.');\n\n if (pathStr === 'chat.completions.create' && typeof value === 'function') {\n return tracedChatCompletionsCreate(value.bind(obj));\n }\n if (pathStr === 'responses.create' && typeof value === 'function') {\n return tracedResponsesCreate(value.bind(obj));\n }\n\n if (value && typeof value === 'object' && !Array.isArray(value) && isNamespace(currentPath)) {\n return wrapNamespace(value, currentPath);\n }\n\n return value;\n },\n });\n}\n\nfunction isNamespace(path: string[]): boolean {\n if (path.length > 3) return false;\n const key = path[path.length - 1];\n return ['chat', 'completions', 'responses', 'beta'].includes(key);\n}\n\n// ---------------------------------------------------------------------------\n// chat.completions.create — non-streaming + streaming\n// ---------------------------------------------------------------------------\n\nfunction tracedChatCompletionsCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n const isStream = opts?.stream === true;\n\n const span = tracer.startSpan('azure_openai.chat.completions.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': PROVIDER,\n 'neatlogs.llm.system': SYSTEM,\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': isStream,\n },\n }, getNeatlogsParentContext());\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n span.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg.role ?? '');\n if (typeof msg.content === 'string') {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, msg.content);\n } else if (msg.content) {\n const captured = captureMedia(\n span,\n `neatlogs.llm.input_messages.${i}`,\n msg.content,\n 'input',\n );\n span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, safeStringify(captured));\n }\n if (msg.tool_call_id) {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.tool_call_id`, msg.tool_call_id);\n }\n }\n\n if (opts?.tools) {\n for (let i = 0; i < opts.tools.length; i++) {\n const fn = opts.tools[i]?.function ?? {};\n span.setAttribute(`neatlogs.llm.tools.${i}.name`, fn.name ?? '');\n if (fn.description) span.setAttribute(`neatlogs.llm.tools.${i}.description`, fn.description);\n if (fn.parameters) span.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify(fn.parameters));\n }\n }\n\n setInvocationParams(span, opts);\n\n if (isStream) {\n opts = { ...opts };\n const streamOpts = opts.stream_options ?? {};\n if (!streamOpts.include_usage) {\n opts.stream_options = { ...streamOpts, include_usage: true };\n }\n }\n\n const promise = withNeatlogsSpan(span, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n if (isStream) {\n return wrapAsyncIterableStream(response, span);\n }\n finalizeChatResponse(span, response);\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// responses.create\n// ---------------------------------------------------------------------------\n\nfunction tracedResponsesCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n\n const span = tracer.startSpan('azure_openai.responses.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': PROVIDER,\n 'neatlogs.llm.system': SYSTEM,\n 'neatlogs.llm.model_name': model,\n 'input.value': '',\n },\n }, getNeatlogsParentContext());\n\n const capturedInput = captureMedia(\n span,\n 'neatlogs.llm.input_messages.0',\n opts?.input,\n 'input',\n );\n span.setAttribute('input.value', safeStringify(capturedInput ?? ''));\n\n const promise = withNeatlogsSpan(span, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n const capturedOutput = captureMedia(\n span,\n 'neatlogs.llm.output_messages.0',\n response?.output,\n 'output',\n );\n if (response?.output_text) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', response.output_text);\n } else if (response?.output) {\n span.setAttribute('output.value', safeStringify(capturedOutput));\n }\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n if (response?.usage) {\n if (response.usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', response.usage.input_tokens);\n if (response.usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', response.usage.output_tokens);\n }\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming support\n// ---------------------------------------------------------------------------\n\nfunction wrapAsyncIterableStream(stream: any, span: Span): any {\n const accumulator = new ChoiceAccumulator();\n const safeSpan = mediaSafeAccumulatorSpan(span);\n let cancellationRequested = false;\n const originalAsyncIterator = stream?.[Symbol.asyncIterator]?.bind(stream);\n\n if (!originalAsyncIterator) {\n accumulator.addResponse(stream, span);\n accumulator.finish(safeSpan);\n return stream;\n }\n\n const wrapped = Object.create(Object.getPrototypeOf(stream));\n Object.assign(wrapped, stream);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await iterator.next();\n if (result.done) {\n accumulator.finish(safeSpan, cancellationRequested);\n return result;\n }\n accumulator.addChunk(span, result.value);\n return result;\n } catch (err) {\n accumulator.fail(safeSpan, err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n cancellationRequested = true;\n const result = await (iterator.return?.(value) ?? { done: true, value: undefined });\n if (result.done) accumulator.finish(safeSpan, true);\n else accumulator.addChunk(span, result.value);\n return result;\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n accumulator.fail(safeSpan, err);\n if (iterator.throw) return iterator.throw(err);\n throw err;\n },\n };\n };\n\n return wrapped;\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming response finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeChatResponse(span: Span, response: any): void {\n const choices = response?.choices ?? [];\n for (let i = 0; i < choices.length; i++) {\n const message = choices[i]?.message;\n if (!message) continue;\n\n span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, 'assistant');\n if (message.content) {\n const captured = captureMedia(\n span,\n `neatlogs.llm.output_messages.${i}`,\n message.content,\n 'output',\n );\n span.setAttribute(\n `neatlogs.llm.output_messages.${i}.content`,\n typeof captured === 'string' ? captured : safeStringify(captured),\n );\n }\n if (message.tool_calls) {\n for (let j = 0; j < message.tool_calls.length; j++) {\n const tc = message.tool_calls[j];\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.function?.name ?? '');\n span.setAttribute(\n `neatlogs.llm.tool_calls.${j}.arguments`,\n sanitizedJsonToolArguments(\n span,\n `neatlogs.llm.tool_calls.${j}.arguments`,\n tc.function?.arguments ?? '{}',\n ),\n );\n }\n }\n if (choices[i].finish_reason) {\n span.setAttribute('neatlogs.llm.finish_reason', choices[i].finish_reason);\n }\n }\n\n const usage = response?.usage;\n if (usage) {\n if (usage.prompt_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.prompt_tokens);\n if (usage.completion_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.completion_tokens);\n if (usage.total_tokens != null) span.setAttribute('neatlogs.llm.token_count.total', usage.total_tokens);\n if (usage.prompt_tokens_details?.cached_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.prompt_tokens_details.cached_tokens);\n }\n if (usage.completion_tokens_details?.reasoning_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.reasoning', usage.completion_tokens_details.reasoning_tokens);\n }\n }\n\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction sanitizedJsonToolArguments(\n span: Span,\n prefix: string,\n value: unknown,\n): string {\n if (typeof value !== 'string') {\n return safeStringify(captureMedia(span, prefix, value, 'output'));\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n span.setAttribute('neatlogs.stream.incomplete', true);\n span.setAttribute('neatlogs.stream.incomplete_reason', 'invalid_tool_arguments');\n return '[incomplete: invalid tool arguments omitted]';\n }\n const original = safeStringify(parsed);\n const sanitized = safeStringify(captureMedia(span, prefix, parsed, 'output'));\n return original === sanitized ? value : sanitized;\n}\n\nfunction mediaSafeAccumulatorSpan(span: Span): Span {\n return new Proxy(span, {\n get(target, property) {\n if (property === 'setAttribute') {\n return (name: string, value: unknown) =>\n target.setAttribute(\n name,\n /^neatlogs\\.llm\\.tool_calls\\.\\d+\\.arguments$/.test(name)\n ? sanitizedJsonToolArguments(target, name, value)\n : (value as any),\n );\n }\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\nfunction setInvocationParams(span: Span, opts: any): void {\n if (opts?.temperature != null) span.setAttribute('neatlogs.llm.temperature', opts.temperature);\n if (opts?.top_p != null) span.setAttribute('neatlogs.llm.top_p', opts.top_p);\n if (opts?.max_tokens != null) span.setAttribute('neatlogs.llm.max_tokens', opts.max_tokens);\n if (opts?.frequency_penalty != null) span.setAttribute('neatlogs.llm.frequency_penalty', opts.frequency_penalty);\n if (opts?.presence_penalty != null) span.setAttribute('neatlogs.llm.presence_penalty', opts.presence_penalty);\n if (opts?.stop) span.setAttribute('neatlogs.llm.stop_sequences', safeStringify(opts.stop));\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction recordError(span: Span, err: unknown): void {\n if (err instanceof Error) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });\n span.recordException(err);\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });\n }\n span.end();\n}\n","/**\n * Auto-root for direct-provider wrappers.\n *\n * The backend only renders a trace once it contains a *parentless* span of a\n * root-eligible kind (WORKFLOW / CHAIN / AGENT / MCP_TOOL). Direct-provider\n * wrappers (wrapOpenAI, wrapAnthropic, wrapBedrock, ...) only ever emit\n * non-root spans (LLM / EMBEDDING / TOOL). So a bare\n * `const client = wrapOpenAI(new OpenAI())` with no surrounding `span()` /\n * `trace()` produces an orphan span and the trace never renders.\n *\n * `getProviderTracer(name)` returns a Tracer facade used *only* by those\n * direct-provider wrappers: when `startSpan` would otherwise produce a\n * parentless, non-root span, it transparently opens a WORKFLOW root (named\n * after the configured `workflowName`) and ends it when the provider span\n * ends. Every wrapper path — non-streaming, streaming finalize, error —\n * funnels through `span.end()`, so wrapping `end()` covers them all with no\n * changes to the wrappers' streaming code.\n *\n * Framework wrappers (langchain, mastra, openai-agents, ai-sdk, ...) keep\n * using `trace.getTracer(...)` directly — they already emit their own root and\n * thread context explicitly, so auto-root must never fire for them.\n */\n\nimport {\n trace as otelTrace,\n type Tracer,\n type Span,\n type Context,\n type SpanOptions,\n} from '@opentelemetry/api';\n\nimport { getSessionConfig } from './context.js';\nimport { applySessionAttributes } from './session.js';\nimport { applyEndUserAttributes } from './end-user.js';\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n getActiveNeatlogsSpan,\n} from './provider.js';\nimport { getActiveClient } from './active-client.js';\nimport { aliasMediaCaptureSpan } from './media.js';\n\n// A parentless span of one of these kinds already satisfies the backend's\n// root requirement, so it must NOT be wrapped in another root.\nconst ROOT_KINDS = new Set(['workflow', 'chain', 'agent', 'mcp_tool']);\n\nfunction autoRootEnabled(): boolean {\n const val = (process.env.NEATLOGS_AUTO_ROOT ?? '').trim().toLowerCase();\n return !['false', '0', 'no', 'off'].includes(val);\n}\n\nfunction resolveRootWorkflowName(): string {\n const clientName = getActiveClient()?.workflowName;\n if (typeof clientName === 'string' && clientName.trim()) return clientName;\n try {\n const name = getSessionConfig()?.workflowName;\n if (typeof name === 'string' && name.trim()) return name;\n } catch {\n /* ignore */\n }\n return 'workflow';\n}\n\n/**\n * Stamp request-scoped identify() identity onto a freshly-created auto-root.\n * The auto-root IS the trace root, and wrapper-only code has no user-controlled\n * root to put trace()/span() args on, so identity comes purely from the\n * identify() context (passed undefined per-call values fall back to it).\n */\nfunction stampAutoRootIdentity(root: Span): void {\n applySessionAttributes(root, undefined, true);\n applyEndUserAttributes(root, undefined, undefined, true);\n}\n\n/**\n * Wrap a provider span so that ending it also ends the auto-created WORKFLOW\n * root. A Proxy keeps this transparent across OTel versions: every property /\n * method except `end` passes straight through to the child span.\n */\nfunction wrapSpanWithRoot(child: Span, root: Span): Span {\n let ended = false;\n const proxy = new Proxy(child, {\n get(target, prop, _receiver) {\n if (prop === 'end') {\n return (...args: any[]): void => {\n if (ended) return;\n ended = true;\n try {\n (target.end as (...a: any[]) => void)(...args);\n } finally {\n try {\n root.end();\n } catch {\n /* ignore */\n }\n }\n };\n }\n const value = Reflect.get(target, prop, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n aliasMediaCaptureSpan(proxy, child);\n return proxy;\n}\n\n/**\n * Self-root helper for callback/event-driven handlers (LangChain, etc.).\n *\n * A callback handler builds its own parent context per run-id rather than going\n * through {@link AutoRootTracer}. When a run BEGINS with a non-root kind (a bare\n * `llm.invoke()` fires the LLM callback with no chain above it; a standalone\n * tool/retriever similarly), the span it creates is parentless and non-root, so\n * the backend can't anchor the trace and it never renders. Call this when there\n * is no LangChain parent: if the kind is non-root and nothing is actively\n * recording in `baseCtx`, it opens a WORKFLOW root and returns it plus the\n * child context to create the span in. The caller MUST end the returned\n * `root` after the child span ends (see {@link endAutoRoot}).\n *\n * Returns `{ root, ctx }` when a root was opened, or `{ root: undefined, ctx: baseCtx }`\n * when no auto-root is needed (root-eligible kind, already-recording parent, or\n * disabled).\n */\nexport function maybeOpenAutoRoot(\n tracer: Tracer,\n kind: string,\n baseCtx: Context,\n): { root: Span | undefined; ctx: Context } {\n const k = String(kind ?? '').toLowerCase();\n // A foreign provider's span must never count as our parent.\n const existing = getActiveNeatlogsSpan();\n if (\n !autoRootEnabled() ||\n ROOT_KINDS.has(k) ||\n (existing !== undefined && existing.isRecording())\n ) {\n return { root: undefined, ctx: baseCtx };\n }\n const root = tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n baseCtx,\n );\n stampAutoRootIdentity(root);\n return { root, ctx: otelTrace.setSpan(baseCtx, root) };\n}\n\n/** End an auto-root opened by {@link maybeOpenAutoRoot}. Safe on undefined. */\nexport function endAutoRoot(root: Span | undefined): void {\n if (!root) return;\n try {\n root.end();\n } catch {\n /* ignore */\n }\n}\n\nclass AutoRootTracer implements Tracer {\n constructor(private readonly _tracer: Tracer) {}\n\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const kind = String(\n (options?.attributes as Record<string, unknown> | undefined)?.[\n 'neatlogs.span.kind'\n ] ?? '',\n ).toLowerCase();\n\n // Parent from our private context, never the foreign global.\n const ctx = context ?? getNeatlogsParentContext();\n const parent = getActiveNeatlogsSpan();\n const needsRoot =\n autoRootEnabled() &&\n !ROOT_KINDS.has(kind) &&\n !(parent !== undefined && parent.isRecording());\n\n if (!needsRoot) {\n return this._tracer.startSpan(name, options, ctx);\n }\n\n const root = this._tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n ctx,\n );\n stampAutoRootIdentity(root);\n const childCtx = otelTrace.setSpan(ctx, root);\n const child = this._tracer.startSpan(name, options, childCtx);\n return wrapSpanWithRoot(child, root);\n }\n\n // startActiveSpan is used by traceTool (callback-scoped, always under an\n // active span in practice) — pass straight through, no auto-root.\n startActiveSpan = ((...args: any[]) =>\n (this._tracer.startActiveSpan as (...a: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'];\n}\n\n/**\n * Tracer for direct-provider wrappers (wrapOpenAI, wrapAnthropic, ...).\n * Identical to `trace.getTracer(name)` but adds transparent auto-root so a\n * bare `wrapOpenAI(new OpenAI())` renders a trace without a manual `span()` /\n * `trace()` wrapper. Do NOT use for framework wrappers.\n */\nexport function getProviderTracer(name: string): Tracer {\n // Resolve from the private provider.\n return new AutoRootTracer(getNeatlogsTracer(name));\n}\n","/**\n * Context wrapper for manual span creation.\n *\n * Provides the `trace()` function — the TypeScript equivalent of the Python\n * `trace()` context manager. Creates an OTel span, sets prompt template/variable\n * context, and executes the user callback within the span.\n *\n * @example\n * ```typescript\n * await trace({ name: 'my-trace' }, async (span) => {\n * // user code runs here with the span active\n * });\n * ```\n */\n\nimport {\n trace as otelTrace,\n context as otelContext,\n createContextKey,\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span,\n} from '@opentelemetry/api';\nimport { PromptContext, UserPromptContext, PromptTemplate, UserPromptTemplate } from '../prompt/template.js';\nimport { registerMask } from './mask.js';\nimport { applyEndUserAttributes } from './end-user.js';\nimport { applySessionAttributes } from './session.js';\nimport { currentSessionId } from './identity.js';\nimport { captureMedia } from './media.js';\n\nimport { getLogger } from './logger.js';\nimport { safeJsonDumps, serializeObj } from '../decorators/base.js';\nimport type { TraceOptions, MaskFunction } from '../types.js';\nimport {\n getActiveNeatlogsSpan,\n getNeatlogsActiveContext,\n getNeatlogsParentContext,\n getNeatlogsRootSpan,\n getNeatlogsTracer,\n withNeatlogsSpan,\n} from './provider.js';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Module-level session config (set by init.ts via _setSessionConfig)\n// ---------------------------------------------------------------------------\n\nlet _sessionConfig: Record<string, any> = {};\n\n/**\n * Set the session configuration. Called by init() during SDK setup.\n * @internal\n */\nexport function _setSessionConfig(config: Record<string, any>): void {\n _sessionConfig = config;\n}\n\n/**\n * Get a copy of the current session configuration.\n */\nexport function getSessionConfig(): Record<string, any> {\n return { ..._sessionConfig };\n}\n\n// ---------------------------------------------------------------------------\n// OTel context keys for prompt data propagation\n// ---------------------------------------------------------------------------\n\nexport const PROMPT_VARIABLES_KEY = createContextKey('neatlogs.prompt_variables');\nexport const PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.prompt_template');\nexport const PROMPT_VERSION_KEY = createContextKey('neatlogs.prompt_version');\nexport const USER_PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.user_prompt_template');\nexport const USER_PROMPT_VARIABLES_KEY = createContextKey('neatlogs.user_prompt_variables');\n\n// ---------------------------------------------------------------------------\n// Known TraceOptions keys (not forwarded as extra span attributes)\n// ---------------------------------------------------------------------------\n\nconst KNOWN_OPTION_KEYS = new Set([\n 'name',\n 'kind',\n 'input',\n 'promptTemplate',\n 'promptVariables',\n 'userPromptTemplate',\n 'userPromptVariables',\n 'version',\n 'mask',\n 'attributes',\n 'sessionId',\n 'parentSessionId',\n 'sessionFeatureName',\n 'sessionEntryPoint',\n 'endUserId',\n 'endUserMetadata',\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Set standard and extra attributes on a span.\n * @internal\n */\nexport function _setSpanAttributes(\n span: Span,\n kind: string | undefined,\n attributes: Record<string, any>,\n): void {\n // trace() is also the public API for user-visible WORKFLOW, AGENT, TOOL,\n // EMBEDDING, and other spans. Marking every trace() span as internal causes\n // those spans (and an all-internal trace) to be filtered by ingestion. Only\n // the synthetic LLM wrapper span is internal by default; callers can still\n // opt any other span into internal handling through explicit attributes.\n if (kind?.toUpperCase() === 'LLM') {\n span.setAttribute('neatlogs.internal', true);\n }\n span.setAttribute('openinference.span.kind', kind ?? 'CHAIN');\n\n for (const [key, value] of Object.entries(attributes)) {\n span.setAttribute(key, value);\n }\n}\n\n/**\n * After the user callback completes, capture prompt variables that were\n * set by PromptTemplate.compile() / UserPromptTemplate.compile() and\n * record them as span attributes.\n * @internal\n */\nexport function _finalizePromptCapture(\n span: Span,\n isPromptTemplateObj: boolean,\n isUserPromptTemplateObj: boolean,\n): void {\n if (isPromptTemplateObj) {\n const capturedVars = PromptContext.getVariables();\n if (capturedVars) {\n span.setAttribute(\n 'llm.prompt_template_variables',\n JSON.stringify(capturedVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from PromptContext: ${Object.keys(capturedVars).join(', ')}`,\n );\n }\n }\n\n if (isUserPromptTemplateObj) {\n const capturedUserVars = UserPromptContext.getVariables();\n if (capturedUserVars) {\n span.setAttribute(\n 'llm.user_prompt_template_variables',\n JSON.stringify(capturedUserVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from UserPromptContext: ${Object.keys(capturedUserVars).join(', ')}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Explicit trace-level output\n// ---------------------------------------------------------------------------\n\n/**\n * Explicitly set the trace-level \"final output\" shown for the current trace.\n *\n * By default the trace's displayed output is derived from the root span's\n * captured output. Call this to declare it instead — useful when the turn's\n * meaningful result lives in a tool call or is a status object you don't want\n * shown (e.g. an agent that suspends awaiting user input). The value is stamped\n * as `neatlogs.trace.output` on the trace ROOT span, which the backend prefers\n * over the derived output.\n *\n * No-op when called outside an active trace. Never throws.\n *\n * @param value - The value to show as the trace's final output.\n *\n * @example\n * ```typescript\n * await trace({ name: 'turn', sessionId }, async () => {\n * const plan = await proposePlan();\n * setTraceOutput(plan.title); // show the plan, not the raw status object\n * });\n * ```\n */\nexport function setTraceOutput(value: unknown): void {\n try {\n const span = getNeatlogsRootSpan() ?? getActiveNeatlogsSpan();\n if (!span) return;\n const captured = captureMedia(\n span,\n 'neatlogs.trace.output',\n serializeObj(value),\n 'output',\n );\n span.setAttribute('neatlogs.trace.output', safeJsonDumps(captured));\n } catch (err) {\n logger.warn(`[setTraceOutput] failed to set trace output: ${err}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main trace() function\n// ---------------------------------------------------------------------------\n\n/**\n * Async callback wrapper for manual span creation with prompt tracking.\n *\n * Creates an OTel span, optionally sets prompt template/variable context,\n * and executes the provided callback within the span.\n *\n * **Session-Aware Trace Creation:**\n * - If a session id is resolved (per-call `sessionId` or the request-scoped\n * `identify()` context) AND no active parent span exists, this creates a NEW\n * root trace (for multi-turn conversations).\n * - Otherwise, creates a normal child span within the existing trace.\n *\n * @param options - Trace configuration options\n * @param fn - Callback that receives the active span\n * @returns The return value of the callback\n *\n * @example\n * ```typescript\n * // Basic usage\n * await trace({ name: 'my-pipeline' }, async (span) => {\n * await step1();\n * await step2();\n * });\n *\n * // With prompt template\n * const template = new PromptTemplate('Hello {{name}}');\n * await trace({ name: 'prompt', promptTemplate: template }, async (span) => {\n * const rendered = template.compile({ name: 'world' });\n * // ...\n * });\n * ```\n */\nexport async function trace<T>(\n options: TraceOptions,\n fn: (span: Span) => T | Promise<T>,\n): Promise<T> {\n const {\n name,\n kind,\n input,\n promptTemplate,\n promptVariables,\n userPromptTemplate,\n userPromptVariables,\n version,\n mask,\n sessionId: sessionIdOption,\n parentSessionId,\n sessionFeatureName,\n sessionEntryPoint,\n endUserId,\n endUserMetadata,\n attributes: explicitAttributes,\n ...extraOptions\n } = options;\n\n // Per-call sessionId wins, else the request-scoped identify() context.\n // Session identity is NOT process-global — init() never sets it.\n const sessionId = sessionIdOption ?? currentSessionId();\n\n // Determine whether we are inside an existing trace. Delayed work can retain\n // an already-ended intermediate span in AsyncLocalStorage. Prefer a live root\n // when one is available; if the root reference is unavailable, preserve the\n // ended parent context so the delayed span keeps the original trace id instead\n // of incorrectly opening a new standalone trace.\n const currentSpan = getActiveNeatlogsSpan();\n const rootSpan = getNeatlogsRootSpan();\n const currentSpanIsRecording = currentSpan?.isRecording() === true;\n const rootSpanIsRecording = rootSpan?.isRecording() === true;\n const shouldFallbackToRoot = !currentSpanIsRecording && rootSpanIsRecording;\n const hasInheritedTraceContext =\n currentSpan !== undefined || rootSpan !== undefined;\n const shouldCreateRootTrace =\n !!sessionId && !hasInheritedTraceContext && !rootSpanIsRecording;\n\n // ---------------------------------------------------------------------------\n // Process prompt templates\n // ---------------------------------------------------------------------------\n\n let templateString: string | undefined;\n let isPromptTemplateObj = false;\n\n if (promptTemplate !== undefined) {\n if (promptTemplate instanceof PromptTemplate) {\n isPromptTemplateObj = true;\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using PromptTemplate object with variables: ${promptTemplate.variables.join(', ')}`,\n );\n } else if (typeof promptTemplate === 'string') {\n templateString = promptTemplate;\n } else {\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n let userTemplateString: string | undefined;\n let isUserPromptTemplateObj = false;\n\n if (userPromptTemplate !== undefined) {\n if (userPromptTemplate instanceof UserPromptTemplate) {\n isUserPromptTemplateObj = true;\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using UserPromptTemplate object with variables: ${userPromptTemplate.variables.join(', ')}`,\n );\n } else if (typeof userPromptTemplate === 'string') {\n userTemplateString = userPromptTemplate;\n } else {\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Build OTel context with prompt values\n // ---------------------------------------------------------------------------\n\n // Start from the Neatlogs active context (private store in isolated mode), NOT\n // otelContext.active() — the latter is the foreign co-tenant's context, whose\n // active span must never become our parent. Prompt values threaded here then\n // propagate to descendant LLM spans through the private store.\n let ctx = getNeatlogsActiveContext();\n if (shouldFallbackToRoot) {\n ctx = otelTrace.setSpan(ctx, rootSpan!);\n }\n\n const variablesJson = promptVariables ? JSON.stringify(promptVariables) : undefined;\n const userVariablesJson = userPromptVariables ? JSON.stringify(userPromptVariables) : undefined;\n\n if (variablesJson) {\n ctx = ctx.setValue(PROMPT_VARIABLES_KEY, variablesJson);\n logger.debug(`[trace] Set neatlogs.prompt_variables in context: ${variablesJson}`);\n }\n if (templateString) {\n ctx = ctx.setValue(PROMPT_TEMPLATE_KEY, templateString);\n logger.debug(`[trace] Set neatlogs.prompt_template in context: ${templateString}`);\n }\n if (userVariablesJson) {\n ctx = ctx.setValue(USER_PROMPT_VARIABLES_KEY, userVariablesJson);\n logger.debug(`[trace] Set neatlogs.user_prompt_variables in context: ${userVariablesJson}`);\n }\n if (userTemplateString) {\n ctx = ctx.setValue(USER_PROMPT_TEMPLATE_KEY, userTemplateString);\n logger.debug(`[trace] Set neatlogs.user_prompt_template in context: ${userTemplateString}`);\n }\n if (version) {\n ctx = ctx.setValue(PROMPT_VERSION_KEY, version);\n logger.debug(`[trace] Set neatlogs.prompt_version in context: ${version}`);\n }\n\n // ---------------------------------------------------------------------------\n // Collect extra attributes (non-standard option keys)\n // ---------------------------------------------------------------------------\n\n const extraAttributes: Record<string, any> = { ...(explicitAttributes ?? {}) };\n for (const [key, value] of Object.entries(extraOptions)) {\n if (!KNOWN_OPTION_KEYS.has(key)) {\n extraAttributes[key] = value;\n }\n }\n\n // End-user belongs to the trace root only. This trace() call is a root when it\n // creates a new root trace, or when it isn't nested in an already-active trace.\n const isRootTrace = shouldCreateRootTrace || !hasInheritedTraceContext;\n\n // ---------------------------------------------------------------------------\n // Create span and execute callback\n // ---------------------------------------------------------------------------\n\n const tracer = getNeatlogsTracer('neatlogs.trace');\n\n const spanCallback = async (span: Span): Promise<T> => {\n _setSpanAttributes(span, kind, extraAttributes);\n\n // Session/end-user belong to the trace root only; skipped on a non-root span.\n applySessionAttributes(span, sessionId, isRootTrace, {\n parentSessionId,\n sessionFeatureName,\n sessionEntryPoint,\n });\n applyEndUserAttributes(span, endUserId, endUserMetadata, isRootTrace);\n\n if (input !== undefined && input !== null) {\n const capturedInput = captureMedia(\n span,\n `neatlogs.${String(kind ?? 'workflow').toLowerCase()}.input`,\n serializeObj(input),\n 'input',\n );\n span.setAttribute('input.value', safeJsonDumps(capturedInput));\n }\n\n if (mask) {\n const maskId = registerMask(mask);\n span.setAttribute('neatlogs.mask_id', maskId);\n }\n\n try {\n const result: T = await fn(span);\n\n _finalizePromptCapture(span, isPromptTemplateObj, isUserPromptTemplateObj);\n\n if (result !== undefined && result !== null) {\n const spanAttrs = (span as any).attributes ?? (span as any)._attributes;\n const hasOutput = spanAttrs && (\n 'output.value' in spanAttrs || 'neatlogs.output.value' in spanAttrs\n );\n if (!hasOutput) {\n const capturedOutput = captureMedia(\n span,\n `neatlogs.${String(kind ?? 'workflow').toLowerCase()}.output`,\n serializeObj(result),\n 'output',\n );\n span.setAttribute('output.value', safeJsonDumps(capturedOutput));\n }\n }\n\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n } finally {\n if (isPromptTemplateObj) {\n PromptContext.clear();\n }\n if (isUserPromptTemplateObj) {\n UserPromptContext.clear();\n }\n span.end();\n }\n };\n\n const parentContext = shouldCreateRootTrace\n ? ROOT_CONTEXT\n : getNeatlogsParentContext(ctx);\n // Seed the span kind at CREATION, not just in the callback: the span processor\n // classifies LLM spans in onStart() (which fires from startSpan, before the\n // callback runs). Without this, a trace({ kind: 'LLM', promptTemplate }) span\n // isn't recognized as LLM in time and its prompt-template attributes are dropped.\n const startAttributes = kind ? { 'openinference.span.kind': kind } : undefined;\n const span = tracer.startSpan(name, startAttributes ? { attributes: startAttributes } : {}, parentContext);\n\n if (shouldCreateRootTrace) {\n logger.debug(`[trace] Creating NEW root trace '${name}' (sessionId=${sessionId})`);\n } else {\n logger.debug(`[trace] Creating child span '${name}'`);\n }\n\n return withNeatlogsSpan(span, () => spanCallback(span), ctx);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { PromptMessage } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Async-local context for system prompts\n// ---------------------------------------------------------------------------\n\ninterface PromptContextData {\n template: string;\n variables: Record<string, any>;\n}\n\nconst _promptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages system prompt metadata in async-local context for automatic tracing.\n */\nexport class PromptContext {\n /**\n * Store system prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n // enterWith mutates the current async context in-place\n _promptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve system prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _promptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve system prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _promptStorage.getStore()?.variables;\n }\n\n /**\n * Clear system prompt context.\n */\n static clear(): void {\n _promptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Async-local context for user prompts\n// ---------------------------------------------------------------------------\n\nconst _userPromptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages user/human prompt metadata in async-local context for automatic tracing.\n */\nexport class UserPromptContext {\n /**\n * Store user prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n _userPromptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve user prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _userPromptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve user prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _userPromptStorage.getStore()?.variables;\n }\n\n /**\n * Clear user prompt context.\n */\n static clear(): void {\n _userPromptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared context setter type for the base template class\n// ---------------------------------------------------------------------------\n\ninterface ContextSetter {\n set(template: string, variables: Record<string, any>): void;\n}\n\n// ---------------------------------------------------------------------------\n// BasePromptTemplate — shared logic for system and user prompts\n// ---------------------------------------------------------------------------\n\nconst VARIABLE_PATTERN = /\\{\\{(\\w+)\\}\\}/g;\n\n/**\n * Base class for prompt templates with `{{variable}}` placeholders.\n *\n * Extracts variables, compiles templates, and renders strings.\n * Subclasses only need to specify the context class and display name.\n */\nabstract class BasePromptTemplate {\n protected readonly _template: string | PromptMessage[];\n protected readonly _variables: string[];\n\n /**\n * @param template - Either a string with `{{variable}}` placeholders or\n * an array of `PromptMessage` objects whose `content` fields contain placeholders.\n */\n constructor(template: string | PromptMessage[]) {\n this._template = template;\n this._variables = this._extractVariables();\n }\n\n /** List of unique variable names found in this template. */\n get variables(): string[] {\n return this._variables;\n }\n\n /** The raw template (string or message array). */\n get template(): string | PromptMessage[] {\n return this._template;\n }\n\n /** The context class to store template/variables for automatic tracing. */\n protected abstract get _contextSetter(): ContextSetter;\n\n /** Display name for toString(). */\n protected abstract get _displayName(): string;\n\n /**\n * Compile the prompt template with the given variables.\n *\n * @param variables - Key/value pairs to substitute for `{{key}}` placeholders.\n * @returns The rendered string or rendered message array.\n * @throws {Error} If any required variables are missing.\n */\n compile(variables?: Record<string, any>): string | PromptMessage[] {\n const vars = variables ?? {};\n\n const missing = this._variables.filter((v) => !(v in vars));\n if (missing.length > 0) {\n throw new Error(\n `Missing required variables: ${missing.join(', ')}. Template requires: ${this._variables.join(', ')}`,\n );\n }\n\n this._contextSetter.set(\n typeof this._template === 'string'\n ? this._template\n : this._template.map((msg) => `${msg.role}: ${msg.content}`).join('\\n'),\n vars,\n );\n\n if (typeof this._template === 'string') {\n return this._renderString(this._template, vars);\n }\n\n return this._template.map((msg) => ({\n role: msg.role,\n content: this._renderString(msg.content, vars),\n }));\n }\n\n /**\n * Replace `{{key}}` placeholders in a string with the corresponding values.\n */\n _renderString(text: string, variables: Record<string, any>): string {\n let result = text;\n for (const [key, value] of Object.entries(variables)) {\n result = result.replaceAll(`{{${key}}}`, String(value));\n }\n return result;\n }\n\n toString(): string {\n if (typeof this._template === 'string') {\n return this._template.length > 50\n ? `${this._displayName}('${this._template.slice(0, 50)}...')`\n : `${this._displayName}('${this._template}')`;\n }\n return `${this._displayName}(${this._template.length} messages, variables=${JSON.stringify(this._variables)})`;\n }\n\n // ---- private ----\n\n private _extractVariables(): string[] {\n if (typeof this._template === 'string') {\n return [...new Set(Array.from(this._template.matchAll(VARIABLE_PATTERN), (m) => m[1]))];\n }\n\n const found: string[] = [];\n for (const msg of this._template) {\n if (msg.content) {\n for (const match of msg.content.matchAll(VARIABLE_PATTERN)) {\n found.push(match[1]);\n }\n }\n }\n return [...new Set(found)];\n }\n}\n\n// ---------------------------------------------------------------------------\n// PromptTemplate — system/AI instruction prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the system/AI instruction prompt with `{{variable}}` placeholders.\n */\nexport class PromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return PromptContext;\n }\n\n protected get _displayName(): string {\n return 'PromptTemplate';\n }\n}\n\n// ---------------------------------------------------------------------------\n// UserPromptTemplate — user/human turn prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the user/human turn prompt with `{{variable}}` placeholders.\n *\n * Identical to {@link PromptTemplate} but stores context in {@link UserPromptContext}.\n */\nexport class UserPromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return UserPromptContext;\n }\n\n protected get _displayName(): string {\n return 'UserPromptTemplate';\n }\n}\n","/**\n * Centralized logging configuration for Neatlogs SDK.\n * Provides structured logging with configurable levels.\n */\n\ntype LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n DEBUG: 0,\n INFO: 1,\n WARN: 2,\n ERROR: 3,\n NONE: 4,\n};\n\nlet _logLevel: LogLevel = 'INFO';\nlet _disabled = false;\n\nfunction _getConfiguredLevel(): LogLevel {\n const envLevel = process.env.NEATLOGS_LOG_LEVEL?.toUpperCase();\n if (envLevel && envLevel in LOG_LEVELS) {\n return envLevel as LogLevel;\n }\n return 'INFO';\n}\n\n// Initialize from env on first import\n_logLevel = _getConfiguredLevel();\n\nexport function getLogger() {\n return {\n debug(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.DEBUG) {\n console.debug(`[neatlogs] ${message}`, ...args);\n }\n },\n info(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.INFO) {\n console.info(`[neatlogs] ${message}`, ...args);\n }\n },\n warn(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.WARN) {\n console.warn(`[neatlogs] ${message}`, ...args);\n }\n },\n error(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.ERROR) {\n console.error(`[neatlogs] ${message}`, ...args);\n }\n },\n };\n}\n\nexport function setLogLevel(level: LogLevel): void {\n _logLevel = level;\n}\n\nexport function enableDebugLogging(): void {\n _logLevel = 'DEBUG';\n}\n\nexport function disableLogging(): void {\n _disabled = true;\n}\n\nexport function enableLogging(): void {\n _disabled = false;\n}\n","/**\n * PII masking support for Neatlogs spans.\n *\n * Users supply a callable that receives the full span dict and returns\n * the (possibly modified) span dict. Return null to drop the span entirely.\n */\n\nimport type { MaskFunction } from '../types.js';\nimport { getLogger } from './logger.js';\n\nconst logger = getLogger();\nexport const DEFAULT_MASK_TIMEOUT_MS = 5_000;\n\nexport interface ApplyMaskOptions {\n signalType?: 'span' | 'log';\n timeoutMs?: number;\n}\n\n/** Module-level registry: key -> mask function */\nconst _MASK_REGISTRY = new Map<string, MaskFunction>();\nconst _MASK_RESULTS = new WeakMap<object, Promise<Record<string, any> | null>>();\n\nlet _nextId = 0;\n\n/**\n * Register a mask function and return its lookup key.\n */\nexport function registerMask(fn: MaskFunction): string {\n const key = String(++_nextId);\n _MASK_REGISTRY.set(key, fn);\n return key;\n}\n\n/**\n * Apply the effective mask function to span data.\n *\n * Per-span mask (stored in attributes[\"neatlogs.mask_id\"]) takes\n * precedence over the global mask. Returns the (possibly modified) dict.\n */\nfunction effectiveMask(\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n): MaskFunction | null {\n const maskId = spanData?.attributes?.['neatlogs.mask_id'];\n let maskFn: MaskFunction | undefined | null = null;\n\n if (maskId) {\n maskFn = _MASK_REGISTRY.get(String(maskId)) ?? null;\n }\n\n if (!maskFn) {\n maskFn = globalMask ?? null;\n }\n\n return maskFn;\n}\n\nexport async function applyMask(\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n options: ApplyMaskOptions = {},\n): Promise<Record<string, any> | null> {\n const maskFn = effectiveMask(spanData, globalMask);\n if (!maskFn) return spanData;\n\n const signalType = options.signalType ?? 'span';\n const timeoutMs = options.timeoutMs ?? DEFAULT_MASK_TIMEOUT_MS;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n try {\n // Run user masking outside SpanProcessor.onEnd() so a slow callback does\n // not block application request completion.\n const maskPromise = Promise.resolve().then(() =>\n maskFn(spanData, { signal: controller.signal, signalType, timeoutMs }),\n );\n const timeoutPromise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n controller.abort();\n reject(new Error(`mask callback exceeded ${timeoutMs}ms`));\n }, timeoutMs);\n timer.unref?.();\n });\n const result = await Promise.race([maskPromise, timeoutPromise]);\n // null means \"drop this span entirely\"; undefined means \"keep original\"\n if (result === null) return null;\n return result !== undefined ? result : spanData;\n } catch (exc) {\n const signalName = spanData?.name ?? spanData?.signal_type ?? signalType;\n logger.error(\n `mask callable failed for ${signalType} '${signalName}': ${exc} — dropping it to prevent unmasked export.`,\n );\n return null;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n controller.abort();\n }\n}\n\n/** Schedule and retain one masking result for the exporter that receives span. */\nexport function scheduleMask(\n span: object,\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n): Promise<Record<string, any> | null> {\n const result = applyMask(spanData, globalMask, { signalType: 'span' });\n _MASK_RESULTS.set(span, result);\n return result;\n}\n\n/** Return the result scheduled by NeatlogsSpanProcessor for this snapshot. */\nexport function getScheduledMask(span: object): Promise<Record<string, any> | null> | undefined {\n return _MASK_RESULTS.get(span);\n}\n\n/**\n * Clear all registered masks. Used for testing.\n * @internal\n */\nexport function _clearMaskRegistry(): void {\n _MASK_REGISTRY.clear();\n _nextId = 0;\n}\n","/**\n * Request-scoped session & end-user identity.\n *\n * Session and end-user identity are PER-REQUEST, not process-global, so they are\n * never set on `init()`. The primary way to set them is at the trace root\n * (`trace({ sessionId })` / `span({ sessionId })`). For code that only uses\n * `wrap()` — where the trace root is created internally by the wrapper and the\n * caller has no root to put arguments on — use the `identify()` context:\n *\n * await neatlogs.identify(\n * { sessionId: 'chat_123', endUserId: 'user_456', endUserMetadata: { plan: 'pro' } },\n * async () => {\n * await wrappedClient.chat.completions.create(...); // auto-root reads this\n * },\n * );\n *\n * Resolution at the trace root (per field): explicit per-call argument wins, then\n * the `identify()` context, then nothing. Identity is stamped on the ROOT span\n * only; the backend rolls it up to the trace and its session.\n *\n * Backed by `AsyncLocalStorage` so it propagates across `await` boundaries (and\n * nested async tasks), exactly like OpenTelemetry's own context propagation.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nexport interface IdentityStore {\n sessionId?: string;\n parentSessionId?: string;\n sessionFeatureName?: string;\n sessionEntryPoint?: string;\n endUserId?: string;\n endUserMetadata?: Record<string, any>;\n}\n\nexport interface IdentifyOptions {\n sessionId?: string;\n /** Optional immediate parent session. */\n parentSessionId?: string;\n /** Product feature that initiated this request. */\n sessionFeatureName?: string;\n /** Application entry point that initiated this request. */\n sessionEntryPoint?: string;\n endUserId?: string;\n endUserMetadata?: Record<string, any>;\n}\n\n// tsup bundles each entry point (index, openai, azure-openai, ...) separately,\n// so a plain module-level `new AsyncLocalStorage()` would be DUPLICATED per\n// bundle — identify() (in index) would write to a different store than a\n// provider wrapper's auto-root (in e.g. azure-openai) reads, silently losing\n// identity on the wrapper-only path. Pin a single instance on globalThis so all\n// bundled copies (ESM + CJS) resolve to the same store.\nconst _GLOBAL_KEY = Symbol.for('neatlogs.identity.storage');\ntype _GlobalWithStore = typeof globalThis & {\n [_GLOBAL_KEY]?: AsyncLocalStorage<IdentityStore>;\n};\nconst _g = globalThis as _GlobalWithStore;\nconst _storage: AsyncLocalStorage<IdentityStore> =\n _g[_GLOBAL_KEY] ?? (_g[_GLOBAL_KEY] = new AsyncLocalStorage<IdentityStore>());\n\nexport function currentSessionId(): string | undefined {\n return _storage.getStore()?.sessionId;\n}\n\nexport function currentParentSessionId(): string | undefined {\n return _storage.getStore()?.parentSessionId;\n}\n\nexport function currentSessionFeatureName(): string | undefined {\n return _storage.getStore()?.sessionFeatureName;\n}\n\nexport function currentSessionEntryPoint(): string | undefined {\n return _storage.getStore()?.sessionEntryPoint;\n}\n\nexport function currentEndUserId(): string | undefined {\n return _storage.getStore()?.endUserId;\n}\n\nexport function currentEndUserMetadata(): Record<string, any> | undefined {\n return _storage.getStore()?.endUserMetadata;\n}\n\n/**\n * Bind session / end-user identity for the duration of `fn`.\n *\n * Only defined fields are set, so a nested `identify()` overrides one field\n * without clearing the others (it merges onto the enclosing store). The previous\n * store is automatically restored when `fn` returns / rejects. Returns whatever\n * `fn` returns (sync value or Promise).\n *\n * @example\n * ```typescript\n * await neatlogs.identify({ sessionId: 'chat_123' }, async () => {\n * await wrappedClient.chat.completions.create(...);\n * });\n * ```\n */\nexport function identify<T>(opts: IdentifyOptions, fn: () => T): T {\n const prev = _storage.getStore();\n const next: IdentityStore = { ...(prev ?? {}) };\n if (opts.sessionId !== undefined) next.sessionId = opts.sessionId;\n if (opts.parentSessionId !== undefined) next.parentSessionId = opts.parentSessionId;\n if (opts.sessionFeatureName !== undefined) next.sessionFeatureName = opts.sessionFeatureName;\n if (opts.sessionEntryPoint !== undefined) next.sessionEntryPoint = opts.sessionEntryPoint;\n if (opts.endUserId !== undefined) next.endUserId = opts.endUserId;\n if (opts.endUserMetadata !== undefined) next.endUserMetadata = opts.endUserMetadata;\n return _storage.run(next, fn);\n}\n","/**\n * Neatlogs-owned tracing state.\n *\n * Spans are created by the private Neatlogs provider and their parent is carried\n * in a private context key. The process-global OpenTelemetry span is\n * deliberately left untouched so other observability SDKs cannot export or\n * become parents of Neatlogs spans.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n ROOT_CONTEXT,\n INVALID_SPAN_CONTEXT,\n createContextKey,\n trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\nimport { getActiveClient } from './active-client.js';\n\n// Carries the trace-ROOT span down the private context so descendants can target\n// it (e.g. setTraceOutput). getActiveNeatlogsSpan() returns the innermost span,\n// which is not the root once nested; this key preserves the root reference.\nconst NEATLOGS_ROOT_SPAN_KEY = createContextKey('neatlogs.root_span');\n\n// Entry points are bundled independently (`neatlogs`, `neatlogs/openai`,\n// `neatlogs/ai`, `neatlogs/mastra`, … each in both CJS and ESM), so every piece\n// of shared tracing state — the private span store AND the resolved provider /\n// provider — must live on `globalThis` behind a `Symbol.for` key.\n// Otherwise `init()` (run from the `neatlogs` bundle) sets `_provider` in ITS\n// module copy while a wrapper imported from `neatlogs/openai` reads a different,\n// still-null copy and silently falls back to the foreign global provider.\nconst PRIVATE_SPAN_STORAGE_KEY = Symbol.for(\n 'neatlogs.private_span_async_local_storage',\n);\nconst PRIVATE_PROVIDER_STATE_KEY = Symbol.for(\n 'neatlogs.private_provider_state',\n);\ninterface PrivateProviderState {\n provider: TracerProvider | null;\n}\ntype NeatlogsGlobal = typeof globalThis & {\n [PRIVATE_SPAN_STORAGE_KEY]?: AsyncLocalStorage<Context>;\n [PRIVATE_PROVIDER_STATE_KEY]?: PrivateProviderState;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\n// Stores the full Neatlogs Context (parent span PLUS any threaded values such as\n// trace()'s prompt-template keys), not just the span. We never\n// activate the OTel global context, so this private store is the ONLY channel\n// through which those values propagate down to descendant spans.\nconst privateContextStorage =\n neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] ??\n (neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] = new AsyncLocalStorage<Context>());\nconst providerState: PrivateProviderState =\n neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] ??\n (neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] = {\n provider: null,\n });\n// Wrappers may be constructed or accidentally invoked before init(). They must\n// never fall back to a foreign process-global provider, so pre-init calls use a\n// local no-op tracer and safely emit no exported spans.\nconst preInitTracer: Tracer = {\n startSpan(): Span {\n return otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n _name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n const fn =\n typeof arg2 === 'function'\n ? arg2\n : typeof arg3 === 'function'\n ? arg3\n : arg4;\n return fn!(otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT)) as ReturnType<F>;\n },\n};\n\n/** @internal Configure the provider used by Neatlogs-created spans. */\nexport function _setNeatlogsProvider(provider: TracerProvider | null): void {\n providerState.provider = provider;\n}\n\n/** Resolve a tracer from the private provider when one is configured. */\nexport function getNeatlogsTracer(name: string): Tracer {\n const client = getActiveClient();\n if (client) return client.getTracer(name);\n return providerState.provider?.getTracer(name) ?? preInitTracer;\n}\n\n/** A reusable tracer facade that resolves the active Client on every call. */\nexport function getRoutingNeatlogsTracer(name: string): Tracer {\n return {\n startSpan(spanName: string, options?: SpanOptions, context?: Context): Span {\n return isolateTracer(getNeatlogsTracer(name)).startSpan(\n spanName,\n options,\n context,\n );\n },\n startActiveSpan: ((...args: any[]) =>\n (isolateTracer(getNeatlogsTracer(name)).startActiveSpan as (...inner: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'],\n };\n}\n\n/**\n * @internal The private Neatlogs provider, or null before\n * init(). Used by integrations that must repoint a self-instrumenting library's\n * captured provider onto ours.\n */\nexport function getNeatlogsProvider(): TracerProvider | null {\n const client = getActiveClient();\n if (client) return client.tracerProvider;\n return providerState.provider;\n}\n\n/** Run a Client callback without inheriting another pipeline's private span. */\nexport function runWithFreshNeatlogsContext<T>(fn: () => T): T {\n return privateContextStorage.run(ROOT_CONTEXT, fn);\n}\n\n/**\n * Wrap a tracer so that spans it creates parent from — and, for\n * `startActiveSpan`, activate on — the PRIVATE Neatlogs context instead of the\n * global one.\n *\n * We hand this to libraries that create their own spans off a tracer we give\n * them (the Vercel AI SDK's `experimental_telemetry.tracer`, which calls\n * `tracer.startActiveSpan()` internally). Without the facade the AI SDK's native\n * spans would parent from `context.active()` — the foreign co-tenant's context —\n * and `startActiveSpan` would push them onto the GLOBAL context, so a foreign\n * tracer's next span reads our native span as its parent. Both directions leak.\n *\n */\nexport function isolateTracer(tracer: Tracer): Tracer {\n const facade: Tracer = {\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const parent = context ?? getNeatlogsActiveContext();\n return tracer.startSpan(name, options, parent);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n // Normalize the 2/3/4-arg overloads of startActiveSpan.\n let options: SpanOptions | undefined;\n let context: Context | undefined;\n let fn: F;\n if (typeof arg2 === 'function') {\n fn = arg2 as F;\n } else if (typeof arg3 === 'function') {\n options = arg2 as SpanOptions;\n fn = arg3 as F;\n } else {\n options = arg2 as SpanOptions;\n context = arg3 as Context;\n fn = arg4 as F;\n }\n const parent = context ?? getNeatlogsActiveContext();\n const span = tracer.startSpan(name, options, parent);\n return withNeatlogsSpan(span, () => fn(span), parent) as ReturnType<F>;\n },\n };\n return facade;\n}\n\n/**\n * The base context to build new Neatlogs spans and values on. NEVER contains a\n * foreign provider's span: it reads our private store, or ROOT_CONTEXT when\n * nothing is active.\n */\nexport function getNeatlogsActiveContext(): Context {\n return privateContextStorage.getStore() ?? ROOT_CONTEXT;\n}\n\n/** Return only the active Neatlogs span (never a foreign provider's span). */\nexport function getActiveNeatlogsSpan(): Span | undefined {\n return otelTrace.getSpan(getNeatlogsActiveContext());\n}\n\n/**\n * Return the trace-ROOT Neatlogs span, or undefined when no trace is active.\n *\n * Unlike {@link getActiveNeatlogsSpan} (innermost), this is the outermost span\n * of the current trace — the one the backend derives trace-level output from\n * (`parent_span_id=''`). It is stashed on the private context the first time a\n * span becomes active, so nested calls still resolve to the root.\n */\nexport function getNeatlogsRootSpan(): Span | undefined {\n return getNeatlogsActiveContext().getValue(NEATLOGS_ROOT_SPAN_KEY) as\n | Span\n | undefined;\n}\n\n/**\n * Build a parent context that cannot contain a foreign provider's span.\n *\n * The active Neatlogs context already carries the parent span AND any values\n * the caller threaded in upstream (e.g. `trace()`'s prompt-template values), so\n * those values reach the span processor via `onStart(parentContext)`. An\n * explicit `baseContext` (a caller's own value-carrying context) is honored as-is.\n */\nexport function getNeatlogsParentContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * A base context for callers that thread parent linkage themselves\n * (callback/event handlers that keep their own run-id → span map: LangChain,\n * OpenAI-Agents, Claude Agent SDK).\n *\n * This is the ACTIVE Neatlogs context — the private store's\n * context if a Neatlogs `trace()`/`span()` encloses this call, else\n * ROOT_CONTEXT. So a handler's own root/entry span nests under an enclosing\n * Neatlogs trace (preserving its session + end-user id) when one exists, and\n * auto-roots cleanly when one doesn't. A foreign provider's active span can\n * never leak in as an ancestor because the global context is never read.\n */\nexport function getNeatlogsBaseContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * Build an execution context for a Neatlogs span while preserving the active\n * foreign context. The span rides our private context store.\n */\nexport function getNeatlogsExecutionContext(\n span: Span,\n baseContext: Context = ROOT_CONTEXT,\n): Context {\n return otelTrace.setSpan(baseContext, span);\n}\n\n/**\n * Run a callback with a Neatlogs span active under the appropriate policy.\n *\n * The stored context is `setSpan(base, span)` — carrying the span PLUS whatever\n * values `base` holds — so threaded values (prompt templates)\n * propagate to descendant spans through our private store instead of the global\n * OTel context we deliberately never touch.\n */\nexport function withNeatlogsSpan<T>(\n span: Span,\n fn: () => T,\n baseContext?: Context,\n): T {\n const base = baseContext ?? getNeatlogsActiveContext();\n let ctx = otelTrace.setSpan(base, span);\n // The first span activated in a context with no root recorded IS the root of\n // this trace; remember it so descendants (setTraceOutput) can target it.\n if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === undefined) {\n ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);\n }\n return privateContextStorage.run(ctx, fn);\n}\n\n/**\n * @internal Run with a private Neatlogs parent without treating that parent as\n * a locally-recording trace root. This is used for an extracted remote parent:\n * the first local recording span should remain the target for local trace-level\n * output, while still inheriting the remote trace/span IDs.\n */\nexport function withNeatlogsRemoteParent<T>(span: Span, fn: () => T): T {\n return privateContextStorage.run(otelTrace.setSpan(ROOT_CONTEXT, span), fn);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Tracer, TracerProvider } from '@opentelemetry/api';\n\nexport interface ActiveNeatlogsClient {\n readonly workflowName: string;\n readonly tracerProvider: TracerProvider;\n getTracer(scope: string): Tracer;\n getLogger(): any | null;\n}\n\nconst ACTIVE_CLIENT_STORAGE_KEY = Symbol.for(\n 'neatlogs.active_client_async_local_storage',\n);\ntype NeatlogsGlobal = typeof globalThis & {\n [ACTIVE_CLIENT_STORAGE_KEY]?: AsyncLocalStorage<ActiveNeatlogsClient>;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\nconst storage =\n neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] ??\n (neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] =\n new AsyncLocalStorage<ActiveNeatlogsClient>());\n\nexport function getActiveClient(): ActiveNeatlogsClient | undefined {\n return storage.getStore();\n}\n\nexport function runWithClient<T>(\n client: ActiveNeatlogsClient,\n fn: () => T,\n): T {\n return storage.run(client, fn);\n}\n","/**\n * End-user identity.\n *\n * The *end user* is the user of OUR CUSTOMER'S application — the person\n * interacting with the AI product our customer built. This is deliberately\n * distinct from the operator-level `userId` set in {@link init}, which identifies\n * whoever is running the SDK (a developer, a service account, a CI job).\n *\n * Model: **one end-user per trace.** Identity is declared once at a trace\n * boundary — `init()` (process-global default), `trace()` (per-request), or\n * `span()` (per root) — via new options on those existing constructs. There is\n * no separate `identify()` call and no nested per-span override: a trace belongs\n * to a single end-user.\n *\n * End-user identity belongs to the trace as a whole, so the SDK only stamps it\n * on the trace's ROOT span (any span kind, created via `trace()` / `span()`). A\n * non-root child span never carries it. The backend reads end-user from the root\n * span and rolls the value up to the trace (and its session) so\n * filtering/analytics are trace- and session-level, not per-span.\n *\n * Canonical span attributes:\n * `neatlogs.end_user.id` — the end-user identifier (string)\n * `neatlogs.end_user.metadata` — JSON object of arbitrary end-user fields\n */\n\nimport { type Span } from '@opentelemetry/api';\nimport { getLogger } from './logger.js';\nimport { currentEndUserId, currentEndUserMetadata } from './identity.js';\nimport { getActiveNeatlogsSpan } from './provider.js';\n\nconst logger = getLogger();\n\nexport const END_USER_ID_KEY = 'neatlogs.end_user.id';\nexport const END_USER_METADATA_KEY = 'neatlogs.end_user.metadata';\n\n/** Coerce end-user metadata to a JSON string. Returns undefined when empty. */\nexport function normalizeEndUserMetadata(\n metadata: string | Record<string, any> | undefined | null,\n): string | undefined {\n if (metadata === undefined || metadata === null) return undefined;\n if (typeof metadata === 'string') return metadata || undefined;\n try {\n return JSON.stringify(metadata);\n } catch {\n return JSON.stringify(String(metadata));\n }\n}\n\n/**\n * True when no recording span is currently active — i.e. a span created now\n * would be the trace root.\n */\nexport function isRootSpan(): boolean {\n // Resolve from the private span store in isolated mode (a foreign provider's\n // active span must not count as our parent), else the active OTel span.\n const current = getActiveNeatlogsSpan();\n return !(current && current.isRecording());\n}\n\n/**\n * Stamp end-user id/metadata onto a ROOT span (best-effort). When `isRoot` is\n * false the span is a child and the value is ignored — the backend reads\n * end-user from the root span and rolls it up to the trace and session.\n */\nexport function applyEndUserAttributes(\n span: Span,\n endUserId?: string,\n endUserMetadata?: string | Record<string, any> | null,\n isRoot = true,\n): void {\n // Resolution at the root (per field): explicit per-call arg wins, else the\n // identify() context (request-scoped). Non-root spans never carry identity.\n const resolvedId = isRoot ? (endUserId ?? currentEndUserId()) : endUserId;\n const resolvedMeta = isRoot\n ? (endUserMetadata ?? currentEndUserMetadata())\n : endUserMetadata;\n if (!resolvedId && !resolvedMeta) return;\n if (!isRoot) {\n logger.debug(\n '[end_user] Ignoring endUserId/endUserMetadata on a non-root span — ' +\n 'declare it on the trace root (top-level trace()/span()) or identify().',\n );\n return;\n }\n if (resolvedId) {\n span.setAttribute(END_USER_ID_KEY, String(resolvedId));\n }\n const metaJson = normalizeEndUserMetadata(resolvedMeta);\n if (metaJson) {\n span.setAttribute(END_USER_METADATA_KEY, metaJson);\n }\n}\n","/**\n * Session identity.\n *\n * A *session* is one conversation / thread / journey — it groups the multiple\n * traces of a multi-turn interaction (one turn = one trace). Identity is\n * declared once at a trace boundary — `init()` (process-global default),\n * `trace()` (per-turn), or `span()` (per root) — via the `sessionId` option on\n * those existing constructs. The SDK stamps only the trace's ROOT span; the\n * backend rolls the value up to group the traces of a session together.\n *\n * For single-turn workflows a session maps 1:1 to a trace: when no session is\n * declared the backend backfills `session_id = trace_id`.\n *\n * Canonical span attribute:\n * `neatlogs.session.id` — the session identifier (string)\n */\n\nimport { type Span } from '@opentelemetry/api';\nimport { getLogger } from './logger.js';\nimport {\n currentParentSessionId,\n currentSessionEntryPoint,\n currentSessionFeatureName,\n currentSessionId,\n} from './identity.js';\n\nconst logger = getLogger();\n\nexport const SESSION_ID_KEY = 'neatlogs.session.id';\nexport const PARENT_SESSION_ID_KEY = 'neatlogs.session.parent_id';\nexport const SESSION_FEATURE_NAME_KEY = 'neatlogs.session.feature.name';\nexport const SESSION_ENTRY_POINT_KEY = 'neatlogs.session.entry_point';\n\nexport interface SessionAttributeOptions {\n parentSessionId?: string;\n sessionFeatureName?: string;\n sessionEntryPoint?: string;\n}\n\nfunction cleanOptionalString(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const cleaned = value.trim();\n return cleaned || undefined;\n}\n\nfunction setAttributeFailOpen(span: Span, key: string, value: string): void {\n try {\n span.setAttribute(key, value);\n } catch (error) {\n logger.debug(`[session] Failed to set ${key}: ${error}`);\n }\n}\n\n/**\n * Stamp the session id onto a ROOT span (best-effort). When `isRoot` is false\n * the span is a child and the value is ignored — the backend reads session from\n * the root span and groups traces by it.\n */\nexport function applySessionAttributes(\n span: Span,\n sessionId?: string,\n isRoot = true,\n options: SessionAttributeOptions = {},\n): void {\n // Resolution at the root: explicit per-call arg wins, else the identify()\n // context (request-scoped). Non-root spans never carry session identity.\n if (!isRoot) {\n if (\n sessionId ||\n options.parentSessionId ||\n options.sessionFeatureName ||\n options.sessionEntryPoint\n ) {\n logger.debug(\n '[session] Ignoring session attributes on a non-root span — declare them on the ' +\n 'trace root (top-level trace()/span()) or identify().',\n );\n }\n return;\n }\n\n const resolvedSessionId = sessionId ?? currentSessionId();\n if (resolvedSessionId) {\n try {\n span.setAttribute(SESSION_ID_KEY, String(resolvedSessionId));\n } catch (error) {\n logger.debug(`[session] Failed to set ${SESSION_ID_KEY}: ${error}`);\n }\n }\n\n const parentSessionId = cleanOptionalString(\n options.parentSessionId ?? currentParentSessionId(),\n );\n const comparableSessionId = cleanOptionalString(resolvedSessionId);\n if (parentSessionId && parentSessionId !== comparableSessionId) {\n setAttributeFailOpen(span, PARENT_SESSION_ID_KEY, parentSessionId);\n }\n\n const sessionFeatureName = cleanOptionalString(\n options.sessionFeatureName ?? currentSessionFeatureName(),\n );\n if (sessionFeatureName) {\n setAttributeFailOpen(span, SESSION_FEATURE_NAME_KEY, sessionFeatureName);\n }\n\n const sessionEntryPoint = cleanOptionalString(\n options.sessionEntryPoint ?? currentSessionEntryPoint(),\n );\n if (sessionEntryPoint) {\n setAttributeFailOpen(span, SESSION_ENTRY_POINT_KEY, sessionEntryPoint);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { getActiveClient } from \"./active-client.js\";\nimport type { DeliveryDiagnostics } from \"./delivery-diagnostics.js\";\nimport {\n assertReadyUploadReceipt,\n DEFAULT_MAX_TYPED_MEDIA_BYTES,\n isSupportedTypedMediaMimeType,\n TelemetryUploadError,\n type UploadAuthority,\n type UploadPayload,\n} from \"./upload-authority.js\";\n\nexport type MediaRecord = Record<string, string | number>;\n\nexport const DEFAULT_INLINE_MEDIA_BYTES = 100_000;\nexport const DEFAULT_MAX_PENDING_MEDIA_ITEMS = 32;\nexport const DEFAULT_MEDIA_EXPORT_DEADLINE_MS = 15_000;\nconst MAX_MEDIA_TRAVERSAL_DEPTH = 32;\nconst MAX_MEDIA_TRAVERSAL_NODES = 4_096;\nconst MAX_MEDIA_REPLACEMENT_KEY_CHARS = 1024 * 1024;\nconst MAX_MEDIA_REFERENCE_CHARS = 8_192;\nconst MAX_CAPTURED_MEDIA_REFERENCES = 64;\nconst MAX_ENCODED_MEDIA_CHARS = Math.ceil((DEFAULT_MAX_TYPED_MEDIA_BYTES * 4) / 3) + 8;\nconst REDACTED_MEDIA_STRUCTURE = \"[REDACTED_MEDIA_STRUCTURE]\";\nconst MEDIA_PAYLOAD_SIGNAL =\n /(?:data\\s*:|\"(?:image_url|input_audio|inline_data|inlineData|file_data|fileData|file_url|file_uri|fileUri|b64_json|mime_type|mimeType|media_type|mediaType)\"\\s*:|\"type\"\\s*:\\s*\"(?:image|audio|video|document|file|input_file|image_generation_call)\")/i;\n\ninterface DiscoveredMedia {\n record: MediaRecord;\n original?: string;\n binaryOriginal?: Uint8Array;\n bytes?: Uint8Array;\n}\n\ninterface MediaDiscoveryResult {\n items: DiscoveredMedia[];\n truncated: boolean;\n}\n\ninterface PendingMedia {\n bytes: Uint8Array;\n prefixes: string[];\n record: MediaRecord;\n token: string;\n sha256: string;\n byteLength: number;\n mimeType: string;\n}\n\ninterface PendingMediaAccount {\n bytes: number;\n items: number;\n released: boolean;\n owner: PendingMediaOwnerState;\n span: WeakRef<object>;\n}\n\ninterface PendingMediaBucket {\n items: PendingMedia[];\n account: PendingMediaAccount;\n unregisterToken: object;\n}\ninterface PendingMediaOwnerState {\n bytes: number;\n items: number;\n accounts: Set<PendingMediaAccount>;\n}\n\ninterface MediaRuntimeState {\n pendingBySpan: WeakMap<object, PendingMediaBucket>;\n pendingMediaOwners: WeakMap<object, PendingMediaOwnerState>;\n mediaCaptureAvailability: WeakMap<object, { available: boolean; reason: string }>;\n mediaCaptureOwners: WeakMap<object, object>;\n spanAliases: WeakMap<object, object>;\n defaultPendingMediaOwner: object;\n}\n\nconst MEDIA_RUNTIME_KEY = Symbol.for(\"neatlogs.media-runtime.v1\");\nconst runtimeGlobal = globalThis as typeof globalThis & Record<symbol, unknown>;\nconst mediaRuntime =\n (runtimeGlobal[MEDIA_RUNTIME_KEY] as MediaRuntimeState | undefined) ??\n ({\n pendingBySpan: new WeakMap(),\n pendingMediaOwners: new WeakMap(),\n mediaCaptureAvailability: new WeakMap(),\n mediaCaptureOwners: new WeakMap(),\n spanAliases: new WeakMap(),\n defaultPendingMediaOwner: {},\n } satisfies MediaRuntimeState);\nruntimeGlobal[MEDIA_RUNTIME_KEY] = mediaRuntime;\n\nconst {\n pendingBySpan,\n pendingMediaOwners,\n mediaCaptureAvailability,\n mediaCaptureOwners,\n spanAliases,\n defaultPendingMediaOwner,\n} = mediaRuntime;\n\nfunction canonicalSpan(span: object): object {\n return spanAliases.get(span) ?? span;\n}\n\n/** Make a transparent span facade share media state with its SDK span. */\nexport function aliasMediaCaptureSpan(alias: object, target: object): void {\n spanAliases.set(alias, canonicalSpan(target));\n}\n\n/** Bind a recording SDK span to the upload gate selected by its pipeline. */\nexport function setMediaCaptureAvailability(\n span: object,\n available: boolean,\n reason = \"telemetry_uploads_disabled\",\n owner?: object,\n): void {\n const target = canonicalSpan(span);\n mediaCaptureAvailability.set(target, {\n available,\n reason: safeUnavailableReason(reason),\n });\n if (owner) mediaCaptureOwners.set(target, owner);\n}\n\nfunction pendingOwnerState(owner: object): PendingMediaOwnerState {\n const prior = pendingMediaOwners.get(owner);\n if (prior) return prior;\n const state = {\n bytes: 0,\n items: 0,\n accounts: new Set<PendingMediaAccount>(),\n };\n pendingMediaOwners.set(owner, state);\n return state;\n}\n\nfunction releasePendingAccount(account: PendingMediaAccount): void {\n if (account.released) return;\n account.released = true;\n account.owner.bytes = Math.max(0, account.owner.bytes - account.bytes);\n account.owner.items = Math.max(0, account.owner.items - account.items);\n account.owner.accounts.delete(account);\n}\n\nconst pendingFinalizer =\n typeof FinalizationRegistry === \"undefined\"\n ? undefined\n : new FinalizationRegistry<PendingMediaAccount>(releasePendingAccount);\n\nfunction pendingBucket(span: object): PendingMediaBucket {\n span = canonicalSpan(span);\n const prior = pendingBySpan.get(span);\n if (prior) return prior;\n const owner = pendingOwnerState(\n mediaCaptureOwners.get(span) ?? getActiveClient() ?? defaultPendingMediaOwner,\n );\n const account: PendingMediaAccount = {\n bytes: 0,\n items: 0,\n released: false,\n owner,\n span: new WeakRef(span),\n };\n const unregisterToken = {};\n const bucket = { items: [], account, unregisterToken };\n owner.accounts.add(account);\n pendingBySpan.set(span, bucket);\n pendingFinalizer?.register(span, account, unregisterToken);\n return bucket;\n}\n\nfunction detachPendingBucket(span: object): PendingMediaBucket | undefined {\n const alias = span;\n span = canonicalSpan(alias);\n spanAliases.delete(alias);\n const bucket = pendingBySpan.get(span);\n if (!bucket) return undefined;\n pendingBySpan.delete(span);\n mediaCaptureAvailability.delete(span);\n mediaCaptureOwners.delete(span);\n pendingFinalizer?.unregister(bucket.unregisterToken);\n return bucket;\n}\n\nfunction uploadToken(record: MediaRecord): string {\n const material = `${record.sha256}:${record.mime_type}:${record.purpose}`;\n return `nl_pending_media_${createHash(\"sha256\").update(material).digest(\"hex\").slice(0, 24)}`;\n}\n\nfunction mediaKind(mimeType: string, declared = \"\"): string {\n const value = declared\n .toLowerCase()\n .replace(/^(?:input|output)_/, \"\")\n .replace(/_url$/, \"\");\n if (value === \"image_generation_call\") return \"image\";\n if (value === \"file\") return \"document\";\n if ([\"image\", \"audio\", \"video\", \"document\"].includes(value)) return value;\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n if (mimeType.startsWith(\"video/\")) return \"video\";\n if (mimeType === \"application/pdf\" || mimeType.startsWith(\"text/\")) {\n return \"document\";\n }\n return \"media\";\n}\n\nfunction declaredMediaKind(value: string): string {\n const normalized = mediaKind(\"\", value);\n return normalized === \"media\" ? \"\" : normalized;\n}\n\nfunction canonicalMimeType(value: string): string {\n const mimeType = value.trim().split(\";\", 1)[0]?.toLowerCase() ?? \"\";\n return mimeType === \"image/jpg\"\n ? \"image/jpeg\"\n : mimeType === \"audio/mp3\"\n ? \"audio/mpeg\"\n : mimeType;\n}\n\ninterface DecodedBase64 {\n bytes?: Uint8Array;\n byteLength: number;\n sha256: string;\n}\n\nfunction rejectedMediaRecord(\n original: string,\n mimeType: string,\n declared: string,\n purpose: string,\n reason: string,\n source: \"inline\" | \"url\",\n): DiscoveredMedia {\n const fingerprint = createHash(\"sha256\")\n .update(original.slice(0, 4096))\n .update(String(original.length))\n .digest(\"hex\");\n return {\n original,\n record: {\n id: `nl_media_rejected_${fingerprint.slice(0, 24)}`,\n type: mediaKind(mimeType, declared),\n source,\n mime_type: mimeType || \"application/octet-stream\",\n purpose,\n state: \"failed\",\n safe_preview: `upload failed: validate/${reason}`,\n },\n };\n}\n\nfunction decodeBase64(value: string, retainBytes = true): DecodedBase64 | null {\n const normalized = value.replace(/\\s+/g, \"\");\n if (!/^[A-Za-z0-9+/]*={0,2}$/.test(normalized) || normalized.length % 4 === 1) {\n return null;\n }\n try {\n const padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n const estimatedBytes = Math.floor((normalized.length * 3) / 4) - padding;\n if (retainBytes && estimatedBytes <= DEFAULT_MAX_TYPED_MEDIA_BYTES) {\n const bytes = Buffer.from(normalized, \"base64\");\n if (bytes.toString(\"base64\").replace(/=+$/, \"\") !== normalized.replace(/=+$/, \"\")) {\n return null;\n }\n return {\n bytes,\n byteLength: bytes.byteLength,\n sha256: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n };\n }\n const digest = createHash(\"sha256\");\n let byteLength = 0;\n const chunkCharacters = 64 * 1024;\n for (let offset = 0; offset < normalized.length; offset += chunkCharacters) {\n const chunk = Buffer.from(normalized.slice(offset, offset + chunkCharacters), \"base64\");\n digest.update(chunk);\n byteLength += chunk.byteLength;\n }\n if (byteLength !== estimatedBytes) return null;\n return { byteLength, sha256: digest.digest(\"hex\") };\n } catch {\n return null;\n }\n}\n\nfunction inlineRecord(\n input: string | Uint8Array,\n mimeType: string,\n declared: string,\n purpose: string,\n retainBytes = true,\n): DiscoveredMedia | null {\n mimeType = canonicalMimeType(mimeType);\n let original: string | undefined;\n let binaryOriginal: Uint8Array | undefined;\n let bytes: Uint8Array | undefined;\n let byteLength: number;\n let sha256: string;\n if (typeof input === \"string\") {\n original = input;\n let encoded = input.trim();\n if (/^data:/i.test(encoded)) {\n const comma = encoded.indexOf(\",\");\n if (comma < 0) return null;\n const header = encoded.slice(5, comma);\n if (!header.toLowerCase().split(\";\").includes(\"base64\")) return null;\n mimeType = canonicalMimeType(header.split(\";\", 1)[0] || mimeType);\n encoded = encoded.slice(comma + 1);\n }\n if (encoded.length > MAX_ENCODED_MEDIA_CHARS) {\n return rejectedMediaRecord(\n original,\n mimeType,\n declared,\n purpose,\n \"payload_too_large\",\n \"inline\",\n );\n }\n const decoded = decodeBase64(encoded, retainBytes);\n if (!decoded) {\n return rejectedMediaRecord(\n original,\n mimeType,\n declared,\n purpose,\n \"invalid_inline_media\",\n \"inline\",\n );\n }\n bytes = decoded.bytes;\n byteLength = decoded.byteLength;\n sha256 = decoded.sha256;\n } else {\n binaryOriginal = input;\n byteLength = input.byteLength;\n sha256 = createHash(\"sha256\").update(input).digest(\"hex\");\n if (retainBytes && byteLength <= DEFAULT_MAX_TYPED_MEDIA_BYTES) {\n bytes = Uint8Array.from(input);\n }\n }\n const eligible =\n byteLength > DEFAULT_INLINE_MEDIA_BYTES && byteLength <= DEFAULT_MAX_TYPED_MEDIA_BYTES;\n const tooLarge = byteLength > DEFAULT_MAX_TYPED_MEDIA_BYTES;\n return {\n original,\n binaryOriginal,\n bytes,\n record: {\n id: `nl_media_${sha256.slice(0, 24)}`,\n type: mediaKind(mimeType, declared),\n source: \"inline\",\n mime_type: mimeType || \"application/octet-stream\",\n byte_length: byteLength,\n sha256,\n purpose,\n state: tooLarge ? \"failed\" : eligible ? \"pending-upload\" : \"inline\",\n ...(tooLarge\n ? { safe_preview: \"upload failed: validate/payload_too_large\" }\n : eligible\n ? { safe_preview: \"awaiting authenticated upload\" }\n : {}),\n },\n };\n}\n\nfunction referenceRecord(\n reference: string,\n mimeType: string,\n declared: string,\n purpose: string,\n): DiscoveredMedia | null {\n mimeType = canonicalMimeType(mimeType);\n if (reference.length > MAX_MEDIA_REFERENCE_CHARS) {\n return rejectedMediaRecord(\n reference,\n mimeType,\n declared,\n purpose,\n \"media_reference_too_large\",\n \"url\",\n );\n }\n let safeReference = reference;\n let isURLReference = false;\n const normalizedReference = reference.trim();\n const protocolRelative = normalizedReference.startsWith(\"//\");\n const relativeReference =\n !protocolRelative &&\n !/^[a-z][a-z\\d+.-]*:/i.test(normalizedReference) &&\n (normalizedReference.includes(\"/\") ||\n normalizedReference.includes(\"?\") ||\n normalizedReference.includes(\"#\"));\n try {\n const parsed = protocolRelative || relativeReference\n ? new URL(normalizedReference, \"https://neatlogs.invalid\")\n : new URL(normalizedReference);\n if (parsed.protocol.toLowerCase() === \"data:\") {\n return rejectedMediaRecord(\n reference,\n mimeType,\n declared,\n purpose,\n \"invalid_inline_media\",\n \"inline\",\n );\n }\n const hierarchical =\n protocolRelative ||\n relativeReference ||\n parsed.protocol === \"http:\" ||\n parsed.protocol === \"https:\" ||\n /^[a-z][a-z\\d+.-]*:\\/\\//i.test(normalizedReference) ||\n parsed.pathname.startsWith(\"/\");\n if (hierarchical) {\n isURLReference = true;\n parsed.username = \"\";\n parsed.password = \"\";\n parsed.search = \"\";\n parsed.hash = \"\";\n safeReference = protocolRelative\n ? `//${parsed.host}${parsed.pathname}`\n : relativeReference\n ? parsed.pathname\n : parsed.toString();\n } else if (parsed.protocol) {\n isURLReference = true;\n safeReference = parsed.protocol;\n }\n } catch {\n if (/^(?:\\/\\/|[a-z][a-z\\d+.-]*:)/i.test(normalizedReference)) {\n return rejectedMediaRecord(\n reference,\n mimeType,\n declared,\n purpose,\n \"invalid_media_reference\",\n \"url\",\n );\n }\n }\n const digest = createHash(\"sha256\").update(reference).digest(\"hex\");\n return {\n original: reference,\n record: {\n id: `nl_media_${digest.slice(0, 24)}`,\n type: mediaKind(mimeType, declared),\n source: isURLReference ? \"url\" : \"provider\",\n mime_type: mimeType || \"application/octet-stream\",\n reference: safeReference,\n purpose,\n state: \"available\",\n },\n };\n}\n\nfunction discoverMedia(\n value: unknown,\n purpose: string,\n retainBytes = true,\n): MediaDiscoveryResult {\n const found: DiscoveredMedia[] = [];\n const discoveredKeys = new Set<string>();\n let retainedBytes = 0;\n let truncated = false;\n const append = (item: DiscoveredMedia | null): void => {\n if (!item) return;\n if (found.length >= MAX_CAPTURED_MEDIA_REFERENCES) {\n truncated = true;\n return;\n }\n const key = `${item.record.id}:${item.record.mime_type}:${item.record.reference ?? \"\"}:${item.record.type}`;\n if (discoveredKeys.has(key)) return;\n discoveredKeys.add(key);\n if (item.bytes && item.record.state === \"pending-upload\") {\n if (retainedBytes + item.bytes.byteLength > DEFAULT_MAX_TYPED_MEDIA_BYTES) {\n item.bytes = undefined;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/pending_media_memory_limit\";\n } else {\n retainedBytes += item.bytes.byteLength;\n }\n }\n found.push(item);\n };\n const visited = new WeakSet<object>();\n let visitedNodes = 0;\n const visit = (node: any, inheritedDeclared = \"\", inheritedMimeType = \"\", depth = 0): void => {\n if (typeof node === \"string\" && /^\\s*data:/i.test(node)) {\n append(inlineRecord(node, inheritedMimeType, inheritedDeclared, purpose, retainBytes));\n return;\n }\n if (!node || typeof node !== \"object\") return;\n if (found.length >= MAX_CAPTURED_MEDIA_REFERENCES) {\n truncated = true;\n return;\n }\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return;\n }\n if (visited.has(node)) return;\n visited.add(node);\n visitedNodes += 1;\n try {\n if (node instanceof Uint8Array) return;\n if (Array.isArray(node)) {\n for (const item of node) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) break;\n if (inheritedDeclared && (typeof item === \"string\" || item instanceof Uint8Array)) {\n const record =\n typeof item === \"string\" && /^(?:https?:|s3:|gs:|\\/\\/)/i.test(item)\n ? referenceRecord(item, inheritedMimeType, inheritedDeclared, purpose)\n : inlineRecord(item, inheritedMimeType, inheritedDeclared, purpose, retainBytes);\n append(record);\n } else {\n visit(item, inheritedDeclared, inheritedMimeType, depth + 1);\n }\n }\n return;\n }\n const entries: [string, unknown][] = Object.entries(node);\n const read = (key: string): unknown => entries.find(([name]) => name === key)?.[1];\n const declared = String(read(\"type\") ?? inheritedDeclared);\n const normalizedDeclared = declaredMediaKind(declared);\n const mediaDeclared = normalizedDeclared || declaredMediaKind(inheritedDeclared);\n let mimeType = canonicalMimeType(\n String(\n read(\"mime_type\") ??\n read(\"mimeType\") ??\n read(\"media_type\") ??\n read(\"mediaType\") ??\n inheritedMimeType,\n ),\n );\n const declaredFormat = read(\"format\") ?? read(\"output_format\") ?? read(\"outputFormat\");\n if (!mimeType && mediaDeclared && typeof declaredFormat === \"string\") {\n const format = declaredFormat.toLowerCase();\n mimeType = canonicalMimeType(\n mediaDeclared === \"document\" && format === \"pdf\"\n ? \"application/pdf\"\n : `${mediaDeclared}/${format}`,\n );\n }\n const imageUrl = read(\"image_url\");\n let image = imageUrl;\n if (imageUrl && typeof imageUrl === \"object\") {\n try {\n image = (imageUrl as Record<string, unknown>).url;\n } catch {\n image = undefined;\n }\n }\n if (typeof image === \"string\") {\n const record = /^\\s*data:/i.test(image)\n ? inlineRecord(image, mimeType, \"image\", purpose, retainBytes)\n : referenceRecord(image, mimeType, \"image\", purpose);\n append(record);\n }\n const audio = read(\"input_audio\") ?? read(\"inputAudio\");\n if (audio && typeof audio === \"object\" && typeof (audio as any).data === \"string\") {\n const format = String((audio as any).format ?? \"unknown\");\n const record = inlineRecord(\n (audio as any).data,\n mimeType || `audio/${format}`,\n \"audio\",\n purpose,\n retainBytes,\n );\n append(record);\n }\n const inline = read(\"inline_data\") ?? read(\"inlineData\");\n if (inline && typeof inline === \"object\" && typeof (inline as any).data === \"string\") {\n const record = inlineRecord(\n (inline as any).data,\n String((inline as any).mime_type ?? (inline as any).mimeType ?? mimeType),\n mediaDeclared,\n purpose,\n retainBytes,\n );\n append(record);\n }\n const fileData = read(\"file_data\") ?? read(\"fileData\");\n if (typeof fileData === \"string\") {\n const record = inlineRecord(\n fileData,\n mimeType,\n mediaDeclared || \"document\",\n purpose,\n retainBytes,\n );\n append(record);\n }\n const raw =\n read(\"data\") ??\n read(\"bytes\") ??\n read(\"b64_json\") ??\n (normalizedDeclared === \"image\" ? read(\"result\") : undefined);\n if ((typeof raw === \"string\" || raw instanceof Uint8Array) && (mediaDeclared || mimeType)) {\n const record = inlineRecord(raw, mimeType, mediaDeclared, purpose, retainBytes);\n append(record);\n }\n const reference =\n read(\"file_id\") ?? read(\"file_url\") ?? read(\"file_uri\") ?? read(\"fileUri\") ?? read(\"url\");\n if (\n typeof reference === \"string\" &&\n ([\"file\", \"url\"].includes(normalizedDeclared) || !!mediaDeclared || !!mimeType)\n ) {\n const record = /^\\s*data:/i.test(reference)\n ? inlineRecord(reference, mimeType, mediaDeclared || \"document\", purpose, retainBytes)\n : referenceRecord(reference, mimeType, mediaDeclared || \"document\", purpose);\n append(record);\n }\n for (const [key, item] of entries) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) break;\n const keyed = key\n .toLowerCase()\n .replace(/^input_/, \"\")\n .replace(/s$/, \"\");\n const childDeclared = declaredMediaKind(keyed) || mediaDeclared;\n let childMimeType = mimeType;\n if (!childMimeType && childDeclared && typeof declaredFormat === \"string\") {\n const format = declaredFormat.toLowerCase();\n childMimeType = canonicalMimeType(\n childDeclared === \"document\" && format === \"pdf\"\n ? \"application/pdf\"\n : `${childDeclared}/${format}`,\n );\n }\n visit(item, childDeclared, childMimeType, depth + 1);\n }\n } catch {\n return;\n }\n };\n visit(value);\n return { items: found, truncated };\n}\n\nfunction placeholder(record: MediaRecord): Record<string, unknown> {\n return {\n neatlogs_media: Object.fromEntries(\n Object.entries(record).filter(([key]) =>\n [\n \"id\",\n \"type\",\n \"source\",\n \"mime_type\",\n \"byte_length\",\n \"sha256\",\n \"purpose\",\n \"state\",\n \"reference\",\n \"content_encoding\",\n \"safe_preview\",\n \"upload_token\",\n ].includes(key),\n ),\n ),\n };\n}\n\nfunction sanitizedCopy(value: unknown, discovered: DiscoveredMedia[]): unknown {\n const replacements = new Map<string, unknown>();\n const largeReplacements: Array<[string, unknown]> = [];\n for (const item of discovered) {\n if (!item.original) continue;\n if (item.record.state === \"pending-upload\" || item.record.state === \"failed\") {\n const replacement = placeholder(item.record);\n if (item.original.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n largeReplacements.push([item.original, replacement]);\n } else {\n replacements.set(item.original, replacement);\n }\n } else if (typeof item.record.reference === \"string\") {\n if (item.original.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n largeReplacements.push([item.original, item.record.reference]);\n } else {\n replacements.set(item.original, item.record.reference);\n }\n }\n }\n const visited = new WeakMap<object, unknown>();\n let visitedNodes = 0;\n const clone = (node: any, depth = 0): any => {\n if (typeof node === \"string\") {\n const direct =\n node.length <= MAX_MEDIA_REPLACEMENT_KEY_CHARS ? replacements.get(node) : undefined;\n if (direct !== undefined) return direct;\n return largeReplacements.find(([original]) => original === node)?.[1] ?? node;\n }\n if (node instanceof Uint8Array) {\n const match = discovered.find(\n (item) =>\n item.binaryOriginal === node ||\n (item.bytes?.buffer === node.buffer && item.bytes.byteOffset === node.byteOffset),\n );\n return match && [\"pending-upload\", \"failed\"].includes(String(match.record.state))\n ? placeholder(match.record)\n : node;\n }\n if (!node || typeof node !== \"object\") return node;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const prior = visited.get(node);\n if (prior) return \"[CIRCULAR]\";\n visitedNodes += 1;\n if (Array.isArray(node)) {\n const result: unknown[] = [];\n visited.set(node, result);\n for (const item of node) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n result.push(REDACTED_MEDIA_STRUCTURE);\n break;\n }\n result.push(clone(item, depth + 1));\n }\n return result;\n }\n const result: Record<string, unknown> = {};\n visited.set(node, result);\n try {\n for (const [key, item] of Object.entries(node)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n result.neatlogs_truncated = REDACTED_MEDIA_STRUCTURE;\n break;\n }\n result[key] = clone(item, depth + 1);\n }\n } catch {\n return REDACTED_MEDIA_STRUCTURE;\n }\n return result;\n };\n return clone(value);\n}\n\nexport function mediaReferences(value: unknown, purpose: string): MediaRecord[] {\n return discoverMedia(value, purpose, false).items.map(({ record }) =>\n record.state === \"pending-upload\"\n ? {\n ...record,\n state: \"failed\",\n safe_preview: \"upload unavailable: telemetry_uploads_disabled\",\n }\n : { ...record },\n );\n}\n\n/** Remove large inline bytes and URL credentials from a captured value. */\nexport function sanitizeMediaPayload(value: unknown, purpose = \"capture\"): unknown {\n try {\n const discovered = discoverMedia(value, purpose, false);\n return discovered.truncated\n ? REDACTED_MEDIA_STRUCTURE\n : sanitizedCopy(value, discovered.items);\n } catch {\n return REDACTED_MEDIA_STRUCTURE;\n }\n}\n\nexport interface CapturedMediaValue {\n value: unknown;\n count: number;\n}\n\n/** Capture typed media metadata from a stable index and return a telemetry-safe clone. */\nexport function captureMediaWithIndex(\n span: {\n setAttribute(name: string, value: string | number): unknown;\n isRecording?(): boolean;\n },\n prefix: string,\n value: unknown,\n purpose: string,\n mediaIndexOffset = 0,\n): CapturedMediaValue {\n let recording = false;\n try {\n recording = typeof span.isRecording !== \"function\" || span.isRecording();\n } catch {\n span.setAttribute(`${prefix}.media.capture_error`, \"media_capture_failed\");\n return { value: REDACTED_MEDIA_STRUCTURE, count: 0 };\n }\n const spanKey = canonicalSpan(span as object);\n const gate = mediaCaptureAvailability.get(spanKey);\n const uploadsAvailable = gate?.available ?? true;\n let discovery: MediaDiscoveryResult;\n try {\n discovery = discoverMedia(value, purpose, recording && uploadsAvailable);\n } catch {\n span.setAttribute(`${prefix}.media.capture_error`, \"media_capture_failed\");\n return { value: REDACTED_MEDIA_STRUCTURE, count: 0 };\n }\n discovery.items.forEach((item, index) => {\n const recordPrefix = `${prefix}.media.${mediaIndexOffset + index}`;\n if (item.record.state === \"pending-upload\") {\n if (!recording) {\n item.bytes = undefined;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload unavailable: span_not_recording\";\n } else if (!uploadsAvailable) {\n item.bytes = undefined;\n item.record.state = \"failed\";\n item.record.safe_preview = `upload unavailable: ${gate?.reason ?? \"telemetry_uploads_disabled\"}`;\n } else if (!item.bytes) {\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/staged_payload_missing\";\n } else {\n const token = uploadToken(item.record);\n item.record.upload_token = token;\n const bucket = pendingBucket(spanKey);\n const duplicate = bucket.items.find(\n (candidate) =>\n candidate.sha256 === item.record.sha256 && candidate.mimeType === item.record.mime_type,\n );\n if (duplicate) {\n if (!duplicate.prefixes.includes(recordPrefix)) {\n duplicate.prefixes.push(recordPrefix);\n }\n } else if (bucket.account.owner.items >= DEFAULT_MAX_PENDING_MEDIA_ITEMS) {\n item.bytes = undefined;\n delete item.record.upload_token;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/pending_media_item_limit\";\n } else if (\n bucket.account.owner.bytes + item.bytes.byteLength >\n DEFAULT_MAX_TYPED_MEDIA_BYTES\n ) {\n item.bytes = undefined;\n delete item.record.upload_token;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/pending_media_memory_limit\";\n } else {\n bucket.account.owner.bytes += item.bytes.byteLength;\n bucket.account.owner.items += 1;\n bucket.account.bytes += item.bytes.byteLength;\n bucket.account.items += 1;\n bucket.items.push({\n bytes: item.bytes,\n prefixes: [recordPrefix],\n record: { ...item.record },\n token,\n sha256: String(item.record.sha256),\n byteLength: Number(item.record.byte_length),\n mimeType: String(item.record.mime_type),\n });\n }\n if (bucket.items.length === 0) {\n pendingBySpan.delete(span as object);\n pendingFinalizer?.unregister(bucket.unregisterToken);\n releasePendingAccount(bucket.account);\n }\n }\n }\n Object.entries(item.record).forEach(([key, field]) =>\n span.setAttribute(`${recordPrefix}.${key}`, field),\n );\n });\n return {\n value: discovery.truncated\n ? REDACTED_MEDIA_STRUCTURE\n : sanitizedCopy(value, discovery.items),\n count: discovery.items.length,\n };\n}\n\n/** Capture typed media metadata and return a telemetry-safe clone of the value. */\nexport function captureMedia(\n span: {\n setAttribute(name: string, value: string | number): unknown;\n isRecording?(): boolean;\n },\n prefix: string,\n value: unknown,\n purpose: string,\n): unknown {\n return captureMediaWithIndex(span, prefix, value, purpose).value;\n}\n\nexport function setMediaAttributes(\n span: { setAttribute(name: string, value: string | number): unknown },\n prefix: string,\n value: unknown,\n purpose: string,\n): void {\n captureMedia(span, prefix, value, purpose);\n}\n\n/** Sanitize media that reached a completed span through a generic integration. */\nexport function captureMediaInSnapshot(\n span: object,\n snapshot: Record<string, any>,\n uploadsAvailable: boolean,\n unavailableReason = \"telemetry_uploads_disabled\",\n): void {\n setMediaCaptureAvailability(span, uploadsAvailable, unavailableReason);\n const spanAttributes =\n snapshot.attributes && typeof snapshot.attributes === \"object\"\n ? snapshot.attributes\n : (snapshot.attributes = {});\n const setter = {\n isRecording: () => true,\n setAttribute(name: string, value: string | number) {\n spanAttributes[name] = value;\n },\n };\n aliasMediaCaptureSpan(setter, span);\n let mediaIndex = 0;\n\n const sanitizeAttributes = (attributes: unknown): void => {\n if (!attributes || typeof attributes !== \"object\" || Array.isArray(attributes)) return;\n for (const [key, original] of Object.entries(attributes)) {\n let value: unknown = original;\n let parsedJson = false;\n if (typeof original === \"string\") {\n if (!MEDIA_PAYLOAD_SIGNAL.test(original)) continue;\n const trimmed = original.trimStart();\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n value = JSON.parse(original);\n parsedJson = true;\n } catch {\n if (/data\\s*:\\s*(?:image|audio|video|application\\/pdf)/i.test(original)) {\n (attributes as Record<string, unknown>)[key] = REDACTED_MEDIA_STRUCTURE;\n }\n continue;\n }\n }\n }\n\n const purpose = key.includes(\"output\") ? \"output\" : \"input\";\n let safeValue: unknown;\n let count = 0;\n const sanitizeOnly = mediaIndex >= MAX_CAPTURED_MEDIA_REFERENCES;\n if (sanitizeOnly) {\n safeValue = sanitizeMediaPayload(value, purpose);\n } else {\n const captured = captureMediaWithIndex(\n setter,\n \"neatlogs.content\",\n value,\n purpose,\n mediaIndex,\n );\n safeValue = captured.value;\n count = captured.count;\n }\n mediaIndex += count;\n if (count === 0 && !sanitizeOnly) continue;\n const replacement =\n typeof original === \"string\" && (parsedJson || typeof safeValue !== \"string\")\n ? JSON.stringify(safeValue)\n : safeValue;\n if (replacement === original) continue;\n (attributes as Record<string, unknown>)[key] = replacement;\n }\n };\n\n sanitizeAttributes(spanAttributes);\n sanitizeAttributes(snapshot.resource?.attributes ?? snapshot.resource);\n if (Array.isArray(snapshot.events)) {\n for (const event of snapshot.events) sanitizeAttributes(event?.attributes);\n }\n if (Array.isArray(snapshot.links)) {\n for (const link of snapshot.links) sanitizeAttributes(link?.attributes);\n }\n}\n\nfunction safeFailure(error: unknown): { stage: string; reason: string } {\n if (!(error instanceof TelemetryUploadError)) {\n return { stage: \"upload\", reason: \"unexpected_error\" };\n }\n return {\n stage: error.stage,\n reason: /^[a-zA-Z0-9_.-]{1,64}$/.test(error.reasonCode)\n ? error.reasonCode\n : \"unsafe_reason_code\",\n };\n}\n\nfunction safeUnavailableReason(value: string): string {\n return /^[a-zA-Z0-9_.-]{1,64}$/.test(value) ? value : \"upload_authority_unavailable\";\n}\n\nfunction rewritePendingPlaceholder(\n attributes: Record<string, any>,\n item: PendingMedia,\n update: MediaRecord,\n): void {\n const matches = (value: Record<string, any>): boolean =>\n value.upload_token === item.token ||\n (value.id === item.record.id && value.sha256 === item.sha256);\n const applyUpdate = (value: Record<string, any>): Record<string, any> => {\n const after = { ...value, ...update };\n delete after.upload_token;\n if (after.state === \"available\") delete after.safe_preview;\n return after;\n };\n const seen = new WeakSet<object>();\n let visitedNodes = 0;\n const rewrite = (value: any, depth = 0): any => {\n if (typeof value === \"string\") {\n if (!value.includes(item.token) && !value.includes(String(item.record.id))) {\n return value;\n }\n if (value.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const trimmed = value.trimStart();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return value;\n try {\n const parsed = JSON.parse(value);\n const rewritten = rewrite(parsed);\n return rewritten === parsed ? value : JSON.stringify(rewritten);\n } catch {\n return value;\n }\n }\n if (!value || typeof value !== \"object\") return value;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n if (seen.has(value)) return \"[CIRCULAR]\";\n seen.add(value);\n visitedNodes += 1;\n if (!Array.isArray(value) && matches(value)) return applyUpdate(value);\n let changed = false;\n const output = Array.isArray(value) ? [...value] : { ...value };\n for (const [key, child] of Object.entries(value)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const rewritten = rewrite(child, depth + 1);\n if (rewritten !== child) {\n (output as Record<string, any>)[key] = rewritten;\n changed = true;\n }\n }\n if (!Array.isArray(value)) {\n for (const prefix of item.prefixes) {\n const tokenKey = `${prefix}.upload_token`;\n const matchesFlat =\n value[tokenKey] === item.token ||\n (value[`${prefix}.id`] === item.record.id && value[`${prefix}.sha256`] === item.sha256);\n if (!matchesFlat) continue;\n delete (output as Record<string, any>)[tokenKey];\n for (const [field, fieldValue] of Object.entries(update)) {\n (output as Record<string, any>)[`${prefix}.${field}`] = fieldValue;\n }\n if (update.state === \"available\") {\n delete (output as Record<string, any>)[`${prefix}.safe_preview`];\n }\n changed = true;\n }\n }\n return changed ? output : value;\n };\n\n for (const [key, value] of Object.entries(attributes)) {\n attributes[key] = rewrite(value);\n }\n for (const prefix of item.prefixes) {\n const tokenKey = `${prefix}.upload_token`;\n const matchesFlat =\n attributes[tokenKey] === item.token ||\n (attributes[`${prefix}.id`] === item.record.id &&\n attributes[`${prefix}.sha256`] === item.sha256);\n if (!matchesFlat) continue;\n delete attributes[tokenKey];\n for (const [field, fieldValue] of Object.entries(update)) {\n attributes[`${prefix}.${field}`] = fieldValue;\n }\n if (update.state === \"available\") delete attributes[`${prefix}.safe_preview`];\n }\n}\n\nfunction mediaUploadTokens(value: unknown, requirePendingState: boolean): Set<string> {\n const tokens = new Set<string>();\n const seen = new WeakSet<object>();\n let visitedNodes = 0;\n const visit = (node: any, depth = 0): void => {\n if (typeof node === \"string\") {\n if (!node.includes(\"nl_pending_media_\")) return;\n if (!requirePendingState && /^nl_pending_media_[0-9a-f]{24}$/.test(node)) {\n tokens.add(node);\n }\n const trimmed = node.trimStart();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return;\n try {\n visit(JSON.parse(node), depth + 1);\n } catch {\n // Invalid JSON cannot authorize an out-of-band upload.\n }\n return;\n }\n if (!node || typeof node !== \"object\" || seen.has(node)) return;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return;\n }\n seen.add(node);\n visitedNodes += 1;\n if (\n !Array.isArray(node) &&\n (!requirePendingState || node.state === \"pending-upload\") &&\n typeof node.upload_token === \"string\"\n ) {\n tokens.add(node.upload_token);\n }\n if (!Array.isArray(node)) {\n for (const [key, token] of Object.entries(node)) {\n if (!key.endsWith(\".upload_token\") || typeof token !== \"string\") continue;\n const prefix = key.slice(0, -\".upload_token\".length);\n if (!requirePendingState || node[`${prefix}.state`] === \"pending-upload\") {\n tokens.add(token);\n }\n }\n }\n try {\n for (const child of Object.values(node)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) break;\n visit(child, depth + 1);\n }\n } catch {\n return;\n }\n };\n visit(value);\n return tokens;\n}\n\nfunction scrubUploadTokens(value: unknown): unknown {\n const seen = new WeakMap<object, unknown>();\n let visitedNodes = 0;\n const rewrite = (node: any, depth = 0): any => {\n if (typeof node === \"string\") {\n if (!node.includes(\"nl_pending_media_\")) return node;\n const trimmed = node.trimStart();\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n if (node.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n return JSON.stringify(rewrite(JSON.parse(node), depth + 1));\n } catch {\n // Fall through to remove any opaque token embedded in plain text.\n }\n }\n return node.replace(/nl_pending_media_[0-9a-f]{24}/g, \"[REDACTED_UPLOAD_TOKEN]\");\n }\n if (!node || typeof node !== \"object\") return node;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const prior = seen.get(node);\n if (prior) return \"[CIRCULAR]\";\n visitedNodes += 1;\n const output: any = Array.isArray(node) ? [] : {};\n seen.set(node, output);\n try {\n for (const [key, child] of Object.entries(node)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n if (key === \"upload_token\" || key.endsWith(\".upload_token\")) continue;\n output[key] = rewrite(child, depth + 1);\n }\n } catch {\n return REDACTED_MEDIA_STRUCTURE;\n }\n return output;\n };\n return rewrite(value);\n}\n\nasync function uploadWithinDeadline(\n authority: UploadAuthority,\n payload: UploadPayload,\n deadlineUnixMs: number,\n) {\n const remaining = deadlineUnixMs - Date.now();\n if (remaining <= 0) {\n throw new TelemetryUploadError(\"prepare\", \"media_export_deadline_exceeded\", true);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n authority.upload(payload, { deadlineUnixMs }),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(\n () => reject(new TelemetryUploadError(\"prepare\", \"media_export_deadline_exceeded\", true)),\n remaining,\n );\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/** Upload only media references retained by the accepted masked snapshot. */\nexport async function resolvePendingMediaUploads(\n span: object,\n attributes: Record<string, any>,\n authority: UploadAuthority,\n diagnostics?: DeliveryDiagnostics,\n): Promise<boolean> {\n const bucket = detachPendingBucket(span);\n const pending = bucket?.items ?? [];\n const deadlineUnixMs = Date.now() + DEFAULT_MEDIA_EXPORT_DEADLINE_MS;\n let failed = false;\n try {\n const retainedTokens = mediaUploadTokens(attributes, true);\n const presentTokens = mediaUploadTokens(attributes, false);\n for (const item of pending) {\n const retained = item.prefixes.filter(\n (prefix) =>\n attributes[`${prefix}.upload_token`] === item.token &&\n attributes[`${prefix}.state`] === \"pending-upload\",\n );\n const retainedCount = Math.max(retained.length, retainedTokens.has(item.token) ? 1 : 0);\n if (!retainedTokens.has(item.token)) {\n const tokenWasRetainedInInvalidState = presentTokens.has(item.token);\n if (tokenWasRetainedInInvalidState) {\n failed = true;\n diagnostics?.recordTypedMedia(\"failures\", Math.max(retainedCount, 1));\n diagnostics?.recordUploadFailure(\"validate\", \"masked_pending_state\");\n }\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: tokenWasRetainedInInvalidState\n ? \"upload failed: validate/masked_pending_state\"\n : \"upload skipped: media reference removed by mask\",\n });\n continue;\n }\n if (!authority.available) {\n const unavailableReason = safeUnavailableReason(authority.unavailableReason);\n failed = true;\n diagnostics?.recordTypedMedia(\"unavailable\", retainedCount);\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(\"prepare\", unavailableReason);\n const safePreview = `upload unavailable: ${unavailableReason}`;\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n continue;\n }\n if (item.byteLength > Math.min(authority.maxPayloadBytes, DEFAULT_MAX_TYPED_MEDIA_BYTES)) {\n failed = true;\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(\"validate\", \"payload_too_large\");\n const safePreview = \"upload failed: validate/payload_too_large\";\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n continue;\n }\n if (!isSupportedTypedMediaMimeType(item.mimeType)) {\n failed = true;\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(\"validate\", \"unsupported_mime_type\");\n const safePreview = \"upload failed: validate/unsupported_mime_type\";\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n continue;\n }\n const payload: UploadPayload = {\n content: item.bytes,\n purpose: \"typed_media\",\n sha256: item.sha256,\n byteLength: item.byteLength,\n mimeType: item.mimeType,\n contentEncoding: \"identity\",\n idempotencyKey: `nl-ts-v1:typed_media:${item.sha256}:${item.mimeType}`,\n payloadSchema: \"neatlogs.media.v1\",\n };\n try {\n const receipt = assertReadyUploadReceipt(\n await uploadWithinDeadline(authority, payload, deadlineUnixMs),\n payload,\n );\n diagnostics?.recordTypedMedia(\"uploads\", retainedCount);\n rewritePendingPlaceholder(attributes, item, {\n id: receipt.reference.id,\n reference: receipt.reference.id,\n source: \"uploaded\",\n sha256: receipt.reference.sha256,\n byte_length: receipt.reference.byteLength,\n mime_type: receipt.reference.mimeType,\n purpose: receipt.reference.purpose,\n state: \"available\",\n content_encoding: receipt.reference.contentEncoding,\n });\n } catch (error) {\n failed = true;\n const failure = safeFailure(error);\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(failure.stage, failure.reason);\n const safePreview = `upload failed: ${failure.stage}/${failure.reason}`;\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n }\n }\n } finally {\n if (bucket) releasePendingAccount(bucket.account);\n const scrubbed = scrubUploadTokens(attributes);\n for (const key of Object.keys(attributes)) delete attributes[key];\n if (scrubbed && typeof scrubbed === \"object\" && !Array.isArray(scrubbed)) {\n Object.assign(attributes, scrubbed);\n }\n }\n return !failed;\n}\n\n/** Drop out-of-band bytes when a span is filtered or never exported. */\nexport function discardPendingMedia(span: object): void {\n const bucket = detachPendingBucket(span);\n if (bucket) releasePendingAccount(bucket.account);\n}\n\n/** Release all staged bytes owned by one SDK pipeline during shutdown. */\nexport function discardPendingMediaOwner(owner?: object): void {\n const state = pendingMediaOwners.get(owner ?? defaultPendingMediaOwner);\n if (!state) return;\n for (const account of [...state.accounts]) {\n const span = account.span.deref();\n if (span) {\n const bucket = pendingBySpan.get(span);\n if (bucket?.account === account) {\n pendingBySpan.delete(span);\n pendingFinalizer?.unregister(bucket.unregisterToken);\n }\n }\n releasePendingAccount(account);\n }\n pendingMediaOwners.delete(owner ?? defaultPendingMediaOwner);\n}\n","import { createHash } from \"node:crypto\";\n\nexport type UploadPurpose = \"typed_media\" | \"otlp_overflow\";\nexport type UploadContentEncoding = \"identity\" | \"gzip\";\nexport type UploadPayloadSchema = \"otlp.traces.v1\" | \"neatlogs.media.v1\";\n\nexport interface UploadPayload {\n content: Uint8Array;\n purpose: UploadPurpose;\n sha256: string;\n byteLength: number;\n mimeType: string;\n contentEncoding: UploadContentEncoding;\n idempotencyKey: string;\n payloadSchema?: UploadPayloadSchema;\n}\n\nexport interface UploadReference {\n id: string;\n purpose: UploadPurpose;\n sha256: string;\n byteLength: number;\n mimeType: string;\n contentEncoding: UploadContentEncoding;\n state: \"ready\";\n}\n\nexport interface UploadReceipt {\n uploadId: string;\n state: \"ready\";\n reference: UploadReference;\n}\n\nexport interface UploadRequestOptions {\n /** Absolute wall-clock deadline shared by the caller's enclosing export. */\n deadlineUnixMs?: number;\n}\n\nexport interface UploadAuthority {\n readonly available: boolean;\n readonly unavailableReason: string;\n readonly maxPayloadBytes: number;\n upload(payload: UploadPayload, options?: UploadRequestOptions): Promise<UploadReceipt>;\n}\n\nexport type UploadAuthorityOption = boolean | UploadAuthority;\n\nexport const DEFAULT_MAX_OTLP_OVERFLOW_BYTES = 20 * 1024 * 1024;\nexport const DEFAULT_MAX_TYPED_MEDIA_BYTES = 25 * 1024 * 1024;\nexport const DEFAULT_MAX_UPLOAD_BYTES = DEFAULT_MAX_TYPED_MEDIA_BYTES;\nexport const UPLOADS_DISABLED_REASON = \"telemetry_uploads_disabled\";\n\nexport class TelemetryUploadError extends Error {\n constructor(\n readonly stage: \"prepare\" | \"put\" | \"complete\" | \"validate\",\n readonly reasonCode: string,\n readonly retryable: boolean,\n ) {\n super(`telemetry upload ${stage} failed: ${reasonCode}`);\n this.name = \"TelemetryUploadError\";\n }\n}\n\nexport class DisabledUploadAuthority implements UploadAuthority {\n readonly available = false;\n readonly unavailableReason: string;\n readonly maxPayloadBytes: number;\n\n constructor(reason = UPLOADS_DISABLED_REASON, maxPayloadBytes = DEFAULT_MAX_UPLOAD_BYTES) {\n this.unavailableReason = reason;\n this.maxPayloadBytes = maxPayloadBytes;\n }\n\n async upload(_payload: UploadPayload): Promise<UploadReceipt> {\n throw new TelemetryUploadError(\"prepare\", this.unavailableReason, false);\n }\n}\n\ninterface PrepareRequest {\n version: 1;\n purpose: UploadPurpose;\n sha256: string;\n byte_length: number;\n mime_type: string;\n content_encoding: UploadContentEncoding;\n idempotency_key: string;\n payload_schema?: UploadPayloadSchema;\n}\n\ninterface WireReference {\n id: string;\n purpose: UploadPurpose;\n sha256: string;\n byte_length: number;\n mime_type: string;\n content_encoding: UploadContentEncoding;\n state: string;\n}\n\ninterface PreparedUpload {\n uploadId: string;\n state: \"prepared\";\n uploadUrl: string;\n uploadHeaders: Record<string, string>;\n reference: WireReference;\n}\n\ninterface ResumableUpload {\n uploadId: string;\n state: \"uploaded\" | \"validating\";\n reference: WireReference;\n}\n\ntype PrepareResult = PreparedUpload | ResumableUpload | UploadReceipt;\n\ninterface WireDiagnostic {\n stage: string;\n reasonCode: string;\n retryable: boolean;\n}\n\nexport interface HttpUploadAuthorityOptions {\n baseUrl: string;\n apiKey: string;\n fetch?: typeof globalThis.fetch;\n maxPayloadBytes?: number;\n deadlineMs?: number;\n attemptTimeoutMs?: number;\n maxAttempts?: number;\n}\n\nconst SHA256_PATTERN = /^[0-9a-f]{64}$/;\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nconst MIME_TYPE_PATTERN = /^[a-z0-9][a-z0-9!#$&^_.+-]*\\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;\nconst TYPED_MEDIA_MIME_TYPES = new Set([\n \"application/pdf\",\n \"audio/flac\",\n \"audio/mpeg\",\n \"audio/ogg\",\n \"audio/wav\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n]);\nconst SAFE_REASON_PATTERN = /^[a-zA-Z0-9_.-]{1,64}$/;\nconst MAX_RESPONSE_BYTES = 64 * 1024;\nconst SENSITIVE_UPLOAD_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"proxy-authorization\",\n \"x-api-key\",\n]);\nconst HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\nexport function isSupportedTypedMediaMimeType(value: string): boolean {\n return TYPED_MEDIA_MIME_TYPES.has(value);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requiredString(\n value: unknown,\n field: string,\n stage: TelemetryUploadError[\"stage\"],\n): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TelemetryUploadError(stage, `invalid_${field}`, false);\n }\n return value;\n}\n\nfunction validateReference(\n value: unknown,\n payload: UploadPayload,\n expectedUploadId: string,\n expectedState: string,\n stage: TelemetryUploadError[\"stage\"],\n): WireReference {\n if (!isObject(value)) {\n throw new TelemetryUploadError(stage, \"invalid_reference\", false);\n }\n const reference: WireReference = {\n id: requiredString(value.id, \"reference_id\", stage),\n purpose: value.purpose as UploadPurpose,\n sha256: requiredString(value.sha256, \"reference_sha256\", stage),\n byte_length: value.byte_length as number,\n mime_type: requiredString(value.mime_type, \"reference_mime_type\", stage),\n content_encoding: value.content_encoding as UploadContentEncoding,\n state: requiredString(value.state, \"reference_state\", stage),\n };\n if (!UUID_PATTERN.test(reference.id)) {\n throw new TelemetryUploadError(stage, \"invalid_reference_id\", false);\n }\n if (\n reference.id !== expectedUploadId ||\n reference.purpose !== payload.purpose ||\n reference.sha256 !== payload.sha256 ||\n reference.byte_length !== payload.byteLength ||\n reference.mime_type !== payload.mimeType ||\n reference.content_encoding !== payload.contentEncoding ||\n reference.state !== expectedState\n ) {\n throw new TelemetryUploadError(stage, \"reference_mismatch\", false);\n }\n return reference;\n}\n\n/** Validate untrusted/injected authority output and return a credential-free clone. */\nexport function assertReadyUploadReceipt(value: unknown, payload: UploadPayload): UploadReceipt {\n if (!isObject(value)) {\n throw new TelemetryUploadError(\"complete\", \"invalid_receipt\", false);\n }\n const uploadId = requiredString(value.uploadId, \"upload_id\", \"complete\");\n if (!UUID_PATTERN.test(uploadId) || value.state !== \"ready\" || !isObject(value.reference)) {\n throw new TelemetryUploadError(\"complete\", \"invalid_receipt\", false);\n }\n const reference = value.reference;\n if (\n typeof reference.id !== \"string\" ||\n !UUID_PATTERN.test(reference.id) ||\n reference.id !== uploadId ||\n reference.state !== \"ready\" ||\n reference.purpose !== payload.purpose ||\n reference.sha256 !== payload.sha256 ||\n reference.byteLength !== payload.byteLength ||\n reference.mimeType !== payload.mimeType ||\n reference.contentEncoding !== payload.contentEncoding\n ) {\n throw new TelemetryUploadError(\"complete\", \"receipt_mismatch\", false);\n }\n return {\n uploadId,\n state: \"ready\",\n reference: {\n id: reference.id,\n purpose: reference.purpose as UploadPurpose,\n sha256: reference.sha256,\n byteLength: reference.byteLength,\n mimeType: reference.mimeType,\n contentEncoding: reference.contentEncoding as UploadContentEncoding,\n state: \"ready\",\n },\n };\n}\n\nfunction validatePayload(payload: UploadPayload, maxPayloadBytes: number): void {\n if (!(payload.content instanceof Uint8Array)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_content\", false);\n }\n if (!SHA256_PATTERN.test(payload.sha256)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_sha256\", false);\n }\n if (createHash(\"sha256\").update(payload.content).digest(\"hex\") !== payload.sha256) {\n throw new TelemetryUploadError(\"validate\", \"invalid_sha256\", false);\n }\n if (\n !Number.isSafeInteger(payload.byteLength) ||\n payload.byteLength <= 0 ||\n payload.byteLength !== payload.content.byteLength\n ) {\n throw new TelemetryUploadError(\"validate\", \"invalid_byte_length\", false);\n }\n if (!([\"typed_media\", \"otlp_overflow\"] as const).includes(payload.purpose)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_purpose\", false);\n }\n const backendLimit =\n payload.purpose === \"otlp_overflow\"\n ? DEFAULT_MAX_OTLP_OVERFLOW_BYTES\n : DEFAULT_MAX_TYPED_MEDIA_BYTES;\n if (payload.byteLength > Math.min(maxPayloadBytes, backendLimit)) {\n throw new TelemetryUploadError(\"validate\", \"payload_too_large\", false);\n }\n if (!MIME_TYPE_PATTERN.test(payload.mimeType) || payload.mimeType.length > 160) {\n throw new TelemetryUploadError(\"validate\", \"invalid_mime_type\", false);\n }\n if (!([\"identity\", \"gzip\"] as const).includes(payload.contentEncoding)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_content_encoding\", false);\n }\n if (!payload.idempotencyKey || payload.idempotencyKey.length > 128) {\n throw new TelemetryUploadError(\"validate\", \"invalid_idempotency_key\", false);\n }\n if (payload.purpose === \"typed_media\") {\n if (!TYPED_MEDIA_MIME_TYPES.has(payload.mimeType)) {\n throw new TelemetryUploadError(\"validate\", \"unsupported_mime_type\", false);\n }\n if (payload.contentEncoding !== \"identity\") {\n throw new TelemetryUploadError(\"validate\", \"unsupported_content_encoding\", false);\n }\n if (payload.payloadSchema && payload.payloadSchema !== \"neatlogs.media.v1\") {\n throw new TelemetryUploadError(\"validate\", \"invalid_payload_schema\", false);\n }\n } else if (\n payload.mimeType !== \"application/x-protobuf\" ||\n payload.payloadSchema !== \"otlp.traces.v1\"\n ) {\n throw new TelemetryUploadError(\"validate\", \"invalid_overflow_contract\", false);\n }\n}\n\nfunction retryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\nasync function withinDeadline<T>(\n promise: Promise<T>,\n deadline: number,\n stage: TelemetryUploadError[\"stage\"],\n cancel?: () => Promise<void>,\n): Promise<T> {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n void cancel?.().catch(() => undefined);\n throw new TelemetryUploadError(stage, \"deadline_exceeded\", true);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(async () => {\n void cancel?.().catch(() => undefined);\n reject(new TelemetryUploadError(stage, \"deadline_exceeded\", true));\n }, remaining);\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nasync function boundedJson(\n response: Response,\n deadline: number,\n stage: TelemetryUploadError[\"stage\"],\n): Promise<unknown> {\n const declared = Number(response.headers.get(\"content-length\") ?? 0);\n if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {\n await response.body?.cancel().catch(() => undefined);\n throw new TelemetryUploadError(stage, \"response_too_large\", false);\n }\n const reader = response.body?.getReader();\n if (!reader) {\n const text = await withinDeadline(response.text(), deadline, stage);\n if (new TextEncoder().encode(text).byteLength > MAX_RESPONSE_BYTES) {\n throw new TelemetryUploadError(stage, \"response_too_large\", false);\n }\n try {\n return JSON.parse(text);\n } catch {\n throw new TelemetryUploadError(stage, \"invalid_json\", false);\n }\n }\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const { done, value } = await withinDeadline(reader.read(), deadline, stage, () =>\n reader.cancel(),\n );\n if (done) break;\n total += value.byteLength;\n if (total > MAX_RESPONSE_BYTES) {\n await reader.cancel();\n throw new TelemetryUploadError(stage, \"response_too_large\", false);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n try {\n return JSON.parse(new TextDecoder().decode(bytes));\n } catch {\n throw new TelemetryUploadError(stage, \"invalid_json\", false);\n }\n}\n\nfunction responseReason(value: unknown): string | undefined {\n return isObject(value) &&\n typeof value.reason_code === \"string\" &&\n SAFE_REASON_PATTERN.test(value.reason_code)\n ? value.reason_code\n : undefined;\n}\n\nfunction optionalDiagnostic(\n value: unknown,\n stage: TelemetryUploadError[\"stage\"],\n): WireDiagnostic | undefined {\n if (value === undefined) return undefined;\n if (\n !isObject(value) ||\n typeof value.stage !== \"string\" ||\n value.stage.length === 0 ||\n typeof value.reason_code !== \"string\" ||\n !SAFE_REASON_PATTERN.test(value.reason_code) ||\n typeof value.retryable !== \"boolean\"\n ) {\n throw new TelemetryUploadError(stage, \"invalid_diagnostic\", false);\n }\n return {\n stage: value.stage,\n reasonCode: value.reason_code,\n retryable: value.retryable,\n };\n}\n\n/** Authenticated implementation of the Phase 8 telemetry upload draft. */\nexport class HttpUploadAuthority implements UploadAuthority {\n readonly available = true;\n readonly unavailableReason = \"\";\n readonly maxPayloadBytes: number;\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n private readonly deadlineMs: number;\n private readonly attemptTimeoutMs: number;\n private readonly maxAttempts: number;\n\n constructor(options: HttpUploadAuthorityOptions) {\n this.baseUrl = new URL(options.baseUrl).origin;\n this.apiKey = options.apiKey.trim();\n if (!this.apiKey) throw new TypeError(\"apiKey is required for upload authority\");\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new TypeError(\"fetch is required for upload authority\");\n }\n this.fetchImpl = fetchImpl;\n this.maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_UPLOAD_BYTES;\n this.deadlineMs = options.deadlineMs ?? 15_000;\n this.attemptTimeoutMs = options.attemptTimeoutMs ?? 5_000;\n this.maxAttempts = options.maxAttempts ?? 2;\n if (!Number.isSafeInteger(this.maxPayloadBytes) || this.maxPayloadBytes <= 0) {\n throw new RangeError(\"maxPayloadBytes must be a positive safe integer\");\n }\n if (!Number.isFinite(this.deadlineMs) || this.deadlineMs <= 0) {\n throw new RangeError(\"deadlineMs must be positive\");\n }\n if (!Number.isFinite(this.attemptTimeoutMs) || this.attemptTimeoutMs <= 0) {\n throw new RangeError(\"attemptTimeoutMs must be positive\");\n }\n if (!Number.isSafeInteger(this.maxAttempts) || this.maxAttempts <= 0 || this.maxAttempts > 4) {\n throw new RangeError(\"maxAttempts must be an integer between 1 and 4\");\n }\n }\n\n async upload(payload: UploadPayload, options: UploadRequestOptions = {}): Promise<UploadReceipt> {\n validatePayload(payload, this.maxPayloadBytes);\n const deadline = Math.min(\n Date.now() + this.deadlineMs,\n options.deadlineUnixMs ?? Number.POSITIVE_INFINITY,\n );\n if (deadline <= Date.now()) {\n throw new TelemetryUploadError(\"prepare\", \"deadline_exceeded\", true);\n }\n const prepared = await this.prepare(payload, deadline);\n if (prepared.state === \"ready\") return prepared;\n if (prepared.state === \"prepared\") {\n await this.put(prepared, payload, deadline);\n }\n return this.complete(prepared, payload, deadline);\n }\n\n private async prepare(payload: UploadPayload, deadline: number): Promise<PrepareResult> {\n let lastError: TelemetryUploadError | undefined;\n for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {\n try {\n return await this.prepareAttempt(payload, deadline);\n } catch (error) {\n if (!(error instanceof TelemetryUploadError) || !error.retryable) throw error;\n lastError = error;\n }\n if (attempt + 1 < this.maxAttempts) {\n const delay = Math.min(100 * 2 ** attempt, Math.max(0, deadline - Date.now()));\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n throw lastError ?? new TelemetryUploadError(\"prepare\", \"request_failed\", true);\n }\n\n private async prepareAttempt(payload: UploadPayload, deadline: number): Promise<PrepareResult> {\n const body: PrepareRequest = {\n version: 1,\n purpose: payload.purpose,\n sha256: payload.sha256,\n byte_length: payload.byteLength,\n mime_type: payload.mimeType,\n content_encoding: payload.contentEncoding,\n idempotency_key: payload.idempotencyKey,\n ...(payload.payloadSchema ? { payload_schema: payload.payloadSchema } : {}),\n };\n const response = await this.request(\n \"prepare\",\n `${this.baseUrl}/v1/telemetry/uploads`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: JSON.stringify(body),\n credentials: \"omit\",\n redirect: \"error\",\n },\n deadline,\n new Set([200, 201, 202]),\n 1,\n );\n const data = await boundedJson(response, deadline, \"prepare\");\n if (!isObject(data)) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_response\", false);\n }\n const diagnostic = optionalDiagnostic(data.diagnostic, \"prepare\");\n const uploadId = requiredString(data.upload_id, \"upload_id\", \"prepare\");\n if (!UUID_PATTERN.test(uploadId)) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_id\", false);\n }\n if (response.status === 200 && data.state === \"ready\") {\n const reference = validateReference(data.reference, payload, uploadId, \"ready\", \"prepare\");\n return {\n uploadId,\n state: \"ready\",\n reference: {\n id: reference.id,\n purpose: reference.purpose,\n sha256: reference.sha256,\n byteLength: reference.byte_length,\n mimeType: reference.mime_type,\n contentEncoding: reference.content_encoding,\n state: \"ready\",\n },\n };\n }\n if (data.state === \"uploaded\" || data.state === \"validating\") {\n if (response.status !== 200 && response.status !== 202) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_response\", false);\n }\n const reference = validateReference(\n data.reference,\n payload,\n uploadId,\n String(data.state),\n \"prepare\",\n );\n return { uploadId, state: data.state, reference };\n }\n if (data.state === \"rejected\") {\n validateReference(data.reference, payload, uploadId, \"rejected\", \"prepare\");\n throw new TelemetryUploadError(\"prepare\", diagnostic?.reasonCode ?? \"upload_rejected\", false);\n }\n if (response.status !== 201 || data.state !== \"prepared\" || !isObject(data.upload)) {\n throw new TelemetryUploadError(\n \"prepare\",\n diagnostic?.reasonCode ?? \"invalid_response\",\n diagnostic?.retryable ?? false,\n );\n }\n const expiresAt = requiredString(data.expires_at, \"expires_at\", \"prepare\");\n const expires = Date.parse(expiresAt);\n if (!Number.isFinite(expires) || expires <= Date.now()) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_expiry\", false);\n }\n if (data.upload.method !== \"PUT\") {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_method\", false);\n }\n const uploadUrl = requiredString(data.upload.url, \"upload_url\", \"prepare\");\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(uploadUrl);\n } catch {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_url\", false);\n }\n if (parsedUrl.protocol !== \"https:\" || parsedUrl.username || parsedUrl.password) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_url\", false);\n }\n if (!isObject(data.upload.headers)) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_headers\", false);\n }\n const uploadHeaders: Record<string, string> = Object.create(null);\n for (const [key, value] of Object.entries(data.upload.headers)) {\n if (\n typeof value !== \"string\" ||\n !HEADER_NAME_PATTERN.test(key) ||\n /[\\r\\n]/.test(value) ||\n SENSITIVE_UPLOAD_HEADERS.has(key.toLowerCase())\n ) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_headers\", false);\n }\n uploadHeaders[key] = value;\n }\n const reference = validateReference(data.reference, payload, uploadId, \"prepared\", \"prepare\");\n return { uploadId, state: \"prepared\", uploadUrl, uploadHeaders, reference };\n }\n\n private async put(\n prepared: PreparedUpload,\n payload: UploadPayload,\n deadline: number,\n ): Promise<void> {\n const response = await this.request(\n \"put\",\n prepared.uploadUrl,\n {\n method: \"PUT\",\n headers: prepared.uploadHeaders,\n body: payload.content,\n credentials: \"omit\",\n redirect: \"error\",\n },\n deadline,\n );\n try {\n void response.body?.cancel().catch(() => undefined);\n } catch {\n // A successful object PUT is authoritative; cleanup must not trigger a retry.\n }\n }\n\n private async complete(\n prepared: PreparedUpload | ResumableUpload,\n payload: UploadPayload,\n deadline: number,\n ): Promise<UploadReceipt> {\n let lastError: TelemetryUploadError | undefined;\n for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {\n try {\n return await this.completeAttempt(prepared, payload, deadline);\n } catch (error) {\n if (!(error instanceof TelemetryUploadError) || !error.retryable) throw error;\n lastError = error;\n }\n if (attempt + 1 < this.maxAttempts) {\n const delay = Math.min(100 * 2 ** attempt, Math.max(0, deadline - Date.now()));\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n throw lastError ?? new TelemetryUploadError(\"complete\", \"request_failed\", true);\n }\n\n private async completeAttempt(\n prepared: PreparedUpload | ResumableUpload,\n payload: UploadPayload,\n deadline: number,\n ): Promise<UploadReceipt> {\n const response = await this.request(\n \"complete\",\n `${this.baseUrl}/v1/telemetry/uploads/${encodeURIComponent(prepared.uploadId)}/complete`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: JSON.stringify({\n sha256: payload.sha256,\n byte_length: payload.byteLength,\n }),\n credentials: \"omit\",\n redirect: \"error\",\n },\n deadline,\n new Set([200, 202]),\n 1,\n );\n const data = await boundedJson(response, deadline, \"complete\");\n if (!isObject(data)) {\n throw new TelemetryUploadError(\"complete\", \"invalid_response\", false);\n }\n const diagnostic = optionalDiagnostic(data.diagnostic, \"complete\");\n if (data.upload_id !== prepared.uploadId) {\n throw new TelemetryUploadError(\"complete\", \"upload_id_mismatch\", false);\n }\n if (![\"ready\", \"uploaded\", \"validating\", \"rejected\"].includes(String(data.state))) {\n throw new TelemetryUploadError(\"complete\", \"invalid_state\", false);\n }\n const reference = validateReference(\n data.reference,\n payload,\n prepared.uploadId,\n String(data.state),\n \"complete\",\n );\n if (data.state !== \"ready\") {\n const reason =\n diagnostic?.reasonCode ??\n (data.state === \"uploaded\" || data.state === \"validating\"\n ? \"validation_pending\"\n : \"upload_rejected\");\n throw new TelemetryUploadError(\n \"complete\",\n reason,\n diagnostic?.retryable ?? (data.state === \"uploaded\" || data.state === \"validating\"),\n );\n }\n return {\n uploadId: prepared.uploadId,\n state: \"ready\",\n reference: {\n id: reference.id,\n purpose: reference.purpose,\n sha256: reference.sha256,\n byteLength: reference.byte_length,\n mimeType: reference.mime_type,\n contentEncoding: reference.content_encoding,\n state: \"ready\",\n },\n };\n }\n\n private async request(\n stage: \"prepare\" | \"put\" | \"complete\",\n url: string,\n init: RequestInit,\n deadline: number,\n expectedStatuses?: ReadonlySet<number>,\n attemptLimit = this.maxAttempts,\n parseErrorBody = stage !== \"put\",\n ): Promise<Response> {\n let lastError: TelemetryUploadError | undefined;\n for (let attempt = 0; attempt < attemptLimit; attempt += 1) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new TelemetryUploadError(stage, \"deadline_exceeded\", true);\n }\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n Math.min(remaining, this.attemptTimeoutMs),\n );\n timer.unref?.();\n try {\n const response = await this.fetchImpl(url, {\n ...init,\n signal: controller.signal,\n });\n const accepted = expectedStatuses\n ? expectedStatuses.has(response.status)\n : response.status >= 200 && response.status < 300;\n if (accepted) return response;\n let retryable = retryableStatus(response.status);\n let reason = `http_${response.status}`;\n if (parseErrorBody) {\n try {\n const errorBody = await boundedJson(response, deadline, stage);\n reason = responseReason(errorBody) ?? reason;\n if (isObject(errorBody) && typeof errorBody.retryable === \"boolean\") {\n retryable = errorBody.retryable;\n }\n } catch {\n // HTTP status remains authoritative when an error body is malformed.\n }\n } else {\n await response.body?.cancel().catch(() => undefined);\n }\n lastError = new TelemetryUploadError(stage, reason, retryable);\n if (!retryable) throw lastError;\n } catch (error) {\n if (error instanceof TelemetryUploadError && !error.retryable) throw error;\n lastError =\n error instanceof TelemetryUploadError\n ? error\n : new TelemetryUploadError(\n stage,\n controller.signal.aborted ? \"timeout\" : \"network_error\",\n true,\n );\n } finally {\n clearTimeout(timer);\n }\n if (attempt + 1 < attemptLimit) {\n const delay = Math.min(100 * 2 ** attempt, Math.max(0, deadline - Date.now()));\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n throw lastError ?? new TelemetryUploadError(stage, \"request_failed\", true);\n }\n}\n\nexport function isUploadAuthority(value: unknown): value is UploadAuthority {\n return (\n isObject(value) &&\n typeof value.available === \"boolean\" &&\n typeof value.unavailableReason === \"string\" &&\n Number.isSafeInteger(value.maxPayloadBytes) &&\n (value.maxPayloadBytes as number) > 0 &&\n typeof value.upload === \"function\"\n );\n}\n\nexport function uploadsEnabledFromEnv(value: string | undefined): boolean {\n return [\"true\", \"1\", \"yes\"].includes((value ?? \"\").trim().toLowerCase());\n}\n\nexport function resolveUploadAuthority(\n option: UploadAuthorityOption | undefined,\n envValue: string | undefined,\n baseUrl: string,\n apiKey: string,\n): UploadAuthority {\n if (isUploadAuthority(option)) return option;\n const enabled = typeof option === \"boolean\" ? option : uploadsEnabledFromEnv(envValue);\n return enabled ? new HttpUploadAuthority({ baseUrl, apiKey }) : new DisabledUploadAuthority();\n}\n","/**\n * Core span decoration utilities.\n * Provides the low-level wrapping logic used by span() and Span().\n */\n\nimport {\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span as OtelSpan,\n} from \"@opentelemetry/api\";\nimport { getLogger } from \"../core/logger.js\";\nimport { registerMask } from \"../core/mask.js\";\nimport { applyEndUserAttributes, isRootSpan } from \"../core/end-user.js\";\nimport { applySessionAttributes } from \"../core/session.js\";\nimport {\n captureMedia,\n captureMediaWithIndex,\n sanitizeMediaPayload,\n} from \"../core/media.js\";\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n withNeatlogsSpan,\n} from \"../core/provider.js\";\nimport type { SpanOptions, MaskFunction } from \"../types.js\";\nimport {\n DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n DEFAULT_MAX_STREAM_CAPTURE_BYTES,\n utf8ByteLength,\n} from \"../constants.js\";\n\nconst logger = getLogger();\nconst TRACER_NAME = \"neatlogs\";\nconst STREAM_CAPTURE_TRUNCATED = \"[TRUNCATED_STREAM_OUTPUT]\";\n\n/** Safely serialize any value to JSON string. */\nexport function safeJsonDumps(value: any): string {\n try {\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === \"bigint\") return val.toString();\n if (val instanceof Error)\n return { message: val.message, name: val.name, stack: val.stack };\n if (typeof val === \"function\")\n return `[Function: ${val.name || \"anonymous\"}]`;\n return val;\n });\n } catch {\n return String(value);\n }\n}\n\n/** Serialize an object for span attributes. Handles toJSON(), plain objects, primitives. */\nexport function serializeObj(obj: any): any {\n if (obj === null || obj === undefined) return obj;\n // Preserve binary views until media capture can classify and sanitize them.\n // JSON.stringify retains the previous numeric-object representation for\n // ordinary, non-media Uint8Array values.\n if (obj instanceof Uint8Array) return obj;\n if (\n typeof obj === \"string\" ||\n typeof obj === \"number\" ||\n typeof obj === \"boolean\"\n )\n return obj;\n if (typeof obj.toJSON === \"function\") return obj.toJSON();\n if (Array.isArray(obj)) return obj.map(serializeObj);\n if (typeof obj === \"object\") {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = serializeObj(value);\n }\n return result;\n }\n return String(obj);\n}\n\n/** Set common span attributes from options. */\nexport function setCommonSpanAttrs(span: OtelSpan, opts: SpanOptions): void {\n if (opts.kind) {\n span.setAttribute(\"openinference.span.kind\", opts.kind);\n }\n if (opts.internal) {\n span.setAttribute(\"neatlogs.internal\", true);\n }\n if (opts.description) {\n span.setAttribute(\"neatlogs.description\", opts.description);\n }\n if (opts.mask) {\n const maskId = registerMask(opts.mask);\n span.setAttribute(\"neatlogs.mask_id\", maskId);\n }\n}\n\nexport interface DecorateSpanOptions extends SpanOptions {\n /** Override the span name (defaults to function name). */\n spanName?: string;\n /** Post-process callback after the function returns. */\n postprocessResult?: (\n span: OtelSpan,\n result: any,\n boundInputs: Record<string, any>,\n ) => void;\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { [Symbol.asyncIterator]?: unknown })[\n Symbol.asyncIterator\n ] === \"function\"\n );\n}\n\nfunction isSyncIterator(\n value: unknown,\n): value is Iterator<unknown> & Iterable<unknown> {\n return (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { next?: unknown }).next === \"function\" &&\n typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] ===\n \"function\"\n );\n}\n\nfunction isReadableStreamLike(value: unknown): value is {\n getReader: (...args: any[]) => any;\n cancel: (...args: any[]) => Promise<unknown>;\n} {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as { getReader?: unknown }).getReader === \"function\" &&\n typeof (value as { cancel?: unknown }).cancel === \"function\"\n );\n}\n\n/**\n * Core wrapper factory that creates an instrumented version of a function.\n * This is the low-level building block used by span() and Span().\n */\nexport function decorateSpan<TArgs extends any[], TReturn>(\n opts: DecorateSpanOptions,\n fn: (...args: TArgs) => TReturn,\n): (...args: TArgs) => TReturn {\n const spanName = opts.spanName ?? opts.name ?? (fn.name || \"anonymous\");\n const captureInput = opts.captureInput !== false;\n const captureOutput = opts.captureOutput !== false;\n const mediaPrefix = (direction: \"input\" | \"output\"): string => {\n const kind = String(opts.kind ?? \"span\").trim().toLowerCase() || \"span\";\n return `neatlogs.${kind}.${direction}`;\n };\n\n const wrapped = (...args: TArgs): any => {\n // Resolve the tracer at CALL time, not definition time: a decorator applied\n // before init() would otherwise capture the no-op global provider forever\n // and emit zero spans once the private provider is configured.\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // End-user belongs to the trace root only. Capture root status BEFORE the\n // span is created (once it's active, it would be the \"current\" span).\n const isRoot = isRootSpan();\n // Parent from our private span store; a root decorated function anchors a\n // fresh trace under ROOT_CONTEXT.\n const parentContext = isRoot ? ROOT_CONTEXT : getNeatlogsParentContext();\n const span = tracer.startSpan(spanName, {}, parentContext);\n return withNeatlogsSpan(span, () => {\n let finished = false;\n let streamInterrupted = false;\n const streamChunks: any[] = [];\n let totalStreamChunks = 0;\n let retainedStreamBytes = 0;\n let droppedStreamChunks = 0;\n let streamMediaCount = 0;\n\n const captureResult = (result: any): void => {\n if (captureOutput) {\n try {\n const serialized = serializeObj(result);\n const captured = captureMedia(\n span,\n mediaPrefix(\"output\"),\n serialized,\n \"output\",\n );\n span.setAttribute(\n \"output.value\",\n safeJsonDumps(captured),\n );\n } catch (err) {\n logger.debug(`Failed to capture output: ${err}`);\n }\n }\n\n if (opts.postprocessResult) {\n try {\n const boundInputs = _extractBoundInputs(fn, args);\n opts.postprocessResult(span, result, boundInputs);\n } catch (err) {\n logger.debug(`Postprocess failed: ${err}`);\n }\n }\n };\n\n const finishSuccess = (result: any): void => {\n if (finished) return;\n finished = true;\n captureResult(result);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n };\n\n const setStreamCounts = (): void => {\n span.setAttribute(\"neatlogs.stream.chunk_count\", totalStreamChunks);\n if (totalStreamChunks > DEFAULT_MAX_SEMANTIC_STREAM_EVENTS) {\n span.setAttribute(\n \"neatlogs.stream.events_dropped\",\n totalStreamChunks - DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n );\n }\n if (droppedStreamChunks > 0) {\n span.setAttribute(\"neatlogs.stream.output_truncated\", true);\n span.setAttribute(\n \"neatlogs.stream.chunks_not_captured\",\n droppedStreamChunks,\n );\n span.setAttribute(\n \"neatlogs.stream.retained_bytes\",\n retainedStreamBytes,\n );\n }\n };\n\n const streamCaptureValue = (): any[] =>\n droppedStreamChunks > 0\n ? [\n ...streamChunks,\n {\n neatlogs_stream_capture: STREAM_CAPTURE_TRUNCATED,\n chunks_not_captured: droppedStreamChunks,\n },\n ]\n : streamChunks;\n\n const finishError = (error: any): void => {\n if (finished) return;\n finished = true;\n if (error?.name === \"AbortError\") {\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n if (totalStreamChunks > 0) {\n setStreamCounts();\n captureResult(streamCaptureValue());\n }\n span.setStatus({ code: SpanStatusCode.UNSET });\n span.end();\n return;\n }\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error?.message ?? String(error),\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n span.end();\n };\n\n const recordChunk = (chunk: any): void => {\n const index = totalStreamChunks++;\n if (index >= DEFAULT_MAX_SEMANTIC_STREAM_EVENTS) {\n droppedStreamChunks += 1;\n return;\n }\n\n let serializedValue: unknown;\n let serialized: string;\n try {\n serializedValue = serializeObj(chunk);\n serialized = safeJsonDumps(\n sanitizeMediaPayload(serializedValue, \"output\"),\n );\n } catch {\n droppedStreamChunks += 1;\n return;\n }\n const encodedBytes = utf8ByteLength(\n serialized,\n DEFAULT_MAX_STREAM_CAPTURE_BYTES - retainedStreamBytes,\n );\n if (\n retainedStreamBytes + encodedBytes >\n DEFAULT_MAX_STREAM_CAPTURE_BYTES\n ) {\n droppedStreamChunks += 1;\n return;\n }\n\n const captured = captureMediaWithIndex(\n span,\n mediaPrefix(\"output\"),\n serializedValue,\n \"output\",\n streamMediaCount,\n );\n streamMediaCount += captured.count;\n streamChunks.push(captured.value);\n retainedStreamBytes += encodedBytes;\n const summary: Record<string, unknown> = {\n value_type: Array.isArray(chunk) ? \"array\" : typeof chunk,\n encoded_bytes: encodedBytes,\n };\n if (Array.isArray(chunk)) summary.items = chunk.length;\n else if (chunk && typeof chunk === \"object\")\n summary.keys = Object.keys(chunk).sort();\n span.addEvent(\"neatlogs.stream.chunk\", {\n \"neatlogs.stream.chunk.index\": index,\n \"neatlogs.stream.chunk.summary\": safeJsonDumps(summary),\n });\n };\n\n const finishStream = (cancelled = false): void => {\n setStreamCounts();\n if (!cancelled) {\n finishSuccess(streamCaptureValue());\n return;\n }\n if (finished) return;\n finished = true;\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n captureResult(streamCaptureValue());\n span.setStatus({ code: SpanStatusCode.UNSET });\n span.end();\n };\n\n const wrapAsyncIterator = (iterator: any): any => {\n let proxy: any;\n proxy = new Proxy(iterator, {\n get(target, property) {\n if (property === Symbol.asyncIterator) return () => proxy;\n if (property === \"next\") {\n return async (...nextArgs: any[]) => {\n try {\n const item = await withNeatlogsSpan(span, () =>\n target.next(...nextArgs),\n );\n if (item.done) finishStream(streamInterrupted);\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"return\") {\n return async (...returnArgs: any[]) => {\n try {\n streamInterrupted = true;\n const item = target.return\n ? await withNeatlogsSpan(span, () =>\n target.return(...returnArgs),\n )\n : { done: true, value: returnArgs[0] };\n if (item.done) finishStream(true);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"throw\") {\n return async (...throwArgs: any[]) => {\n try {\n const item = target.throw\n ? await withNeatlogsSpan(span, () =>\n target.throw(...throwArgs),\n )\n : Promise.reject(throwArgs[0]);\n if (item.done) finishStream();\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n return proxy;\n };\n\n const wrapAsyncIterable = (source: AsyncIterable<unknown>): any => {\n if (typeof (source as any).next === \"function\") {\n return wrapAsyncIterator(source);\n }\n return new Proxy(source as any, {\n get(target, property) {\n if (property === Symbol.asyncIterator) {\n return () => wrapAsyncIterator(target[Symbol.asyncIterator]());\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n };\n\n const wrapSyncIterator = (\n iterator: Iterator<unknown> & Iterable<unknown>,\n ): any => {\n let proxy: any;\n proxy = new Proxy(iterator, {\n get(target, property) {\n if (property === Symbol.iterator) return () => proxy;\n if (property === \"next\") {\n return (...nextArgs: any[]) => {\n try {\n const item = withNeatlogsSpan(span, () =>\n (\n target.next as (\n ...args: any[]\n ) => IteratorResult<unknown>\n )(...nextArgs),\n );\n if (item.done) finishStream(streamInterrupted);\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"return\") {\n return (...returnArgs: any[]) => {\n try {\n streamInterrupted = true;\n const item = target.return\n ? withNeatlogsSpan(span, () =>\n (\n target.return as (\n ...args: any[]\n ) => IteratorResult<unknown>\n )(...returnArgs),\n )\n : { done: true, value: returnArgs[0] };\n if (item.done) finishStream(true);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"throw\") {\n return (...throwArgs: any[]) => {\n try {\n if (!target.throw) throw throwArgs[0];\n const item = withNeatlogsSpan(span, () =>\n (\n target.throw as (\n ...args: any[]\n ) => IteratorResult<unknown>\n )(...throwArgs),\n );\n if (item.done) finishStream(streamInterrupted);\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n return proxy;\n };\n\n const wrapReader = (reader: any): any =>\n new Proxy(reader, {\n get(target, property) {\n if (property === \"read\") {\n return async (...readArgs: any[]) => {\n try {\n const item = await withNeatlogsSpan(span, () =>\n target.read(...readArgs),\n );\n if (item.done) finishStream();\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"cancel\") {\n return async (...cancelArgs: any[]) => {\n try {\n const result = await target.cancel(...cancelArgs);\n finishStream(true);\n return result;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"releaseLock\") {\n return (...releaseArgs: any[]) => {\n try {\n return target.releaseLock(...releaseArgs);\n } finally {\n finishStream(true);\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n const wrapReadableStream = (stream: any): any =>\n new Proxy(stream, {\n get(target, property) {\n if (property === \"getReader\") {\n return (...readerArgs: any[]) =>\n wrapReader(target.getReader(...readerArgs));\n }\n if (property === Symbol.asyncIterator) {\n const iteratorFactory = target[Symbol.asyncIterator];\n if (typeof iteratorFactory === \"function\") {\n return () => wrapAsyncIterator(iteratorFactory.call(target));\n }\n }\n if (property === \"cancel\") {\n return async (...cancelArgs: any[]) => {\n try {\n const result = await target.cancel(...cancelArgs);\n finishStream(true);\n return result;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"pipeTo\") {\n return async (...pipeArgs: any[]) => {\n try {\n const result = await target.pipeTo(...pipeArgs);\n finishStream();\n return result;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n try {\n // Set common attributes\n setCommonSpanAttrs(span, opts);\n\n // Session/end-user identity (root span only; skipped on a non-root child).\n applySessionAttributes(span, opts.sessionId, isRoot, {\n parentSessionId: opts.parentSessionId,\n sessionFeatureName: opts.sessionFeatureName,\n sessionEntryPoint: opts.sessionEntryPoint,\n });\n applyEndUserAttributes(\n span,\n opts.endUserId,\n opts.endUserMetadata,\n isRoot,\n );\n\n // Capture input\n if (captureInput && args.length > 0) {\n try {\n const inputValue =\n args.length === 1\n ? serializeObj(args[0])\n : args.map(serializeObj);\n span.setAttribute(\n \"input.value\",\n safeJsonDumps(\n captureMedia(\n span,\n mediaPrefix(\"input\"),\n inputValue,\n \"input\",\n ),\n ),\n );\n } catch (err) {\n logger.debug(`Failed to capture input: ${err}`);\n }\n }\n\n // Execute the function\n const result = fn(...args);\n\n const handleResult = (resolved: any): any => {\n // A promise may resolve to a stream. Classify the resolved value before\n // ending the span so async factories remain open through consumption.\n if (isReadableStreamLike(resolved))\n return wrapReadableStream(resolved);\n if (isAsyncIterable(resolved)) return wrapAsyncIterable(resolved);\n if (isSyncIterator(resolved)) return wrapSyncIterator(resolved);\n finishSuccess(resolved);\n return resolved;\n };\n\n // Handle any Promise/A+ thenable, not only native Promise instances.\n if (isPromiseLike(result)) {\n return Promise.resolve(result)\n .then(handleResult)\n .catch((error: any) => {\n finishError(error);\n throw error;\n });\n }\n\n return handleResult(result);\n } catch (error: any) {\n finishError(error);\n throw error;\n }\n });\n };\n\n // Preserve function name for debugging\n Object.defineProperty(wrapped, \"name\", {\n value: spanName,\n configurable: true,\n });\n\n return wrapped as any;\n}\n\n/** Extract named arguments from function call (best-effort). */\nfunction _extractBoundInputs(fn: Function, args: any[]): Record<string, any> {\n const result: Record<string, any> = {};\n // Parse function parameter names from toString()\n const fnStr = fn.toString();\n const match = fnStr.match(/\\(([^)]*)\\)/);\n if (match) {\n const paramNames = match[1]\n .split(\",\")\n .map((p) =>\n p\n .trim()\n .replace(/\\s*[:=].*$/, \"\")\n .replace(/^\\.\\.\\./, \"\"),\n )\n .filter(Boolean);\n for (let i = 0; i < Math.min(paramNames.length, args.length); i++) {\n result[paramNames[i]] = args[i];\n }\n }\n return result;\n}\n","/** Stable SDK-wide defaults shared by every NeatLogs entry point. */\nexport const DEFAULT_INGEST_ENDPOINT = 'https://ingest.neatlogs.com' as const;\nexport const DEFAULT_MAX_QUEUE_ITEMS = 2048;\nexport const DEFAULT_MAX_SEMANTIC_STREAM_EVENTS = 128;\nexport const DEFAULT_MAX_STREAM_CAPTURE_BYTES = 1024 * 1024;\nexport const DEFAULT_MAX_STREAM_CAPTURE_ITEMS = 1024;\n\n/** UTF-8 byte length without allocating a second buffer for hostile stream chunks. */\nexport function utf8ByteLength(\n value: string,\n limit = Number.MAX_SAFE_INTEGER,\n): number {\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code < 0x80) bytes += 1;\n else if (code < 0x800) bytes += 2;\n else if (code >= 0xd800 && code <= 0xdbff && i + 1 < value.length) {\n const next = value.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) {\n bytes += 4;\n i += 1;\n } else bytes += 3;\n } else bytes += 3;\n if (bytes > limit) return limit + 1;\n }\n return bytes;\n}\n\nexport function exportQueueCapacity(batchSize: number): number {\n return Math.max(DEFAULT_MAX_QUEUE_ITEMS, batchSize * 4);\n}\n","import { SpanStatusCode, type Span } from \"@opentelemetry/api\";\nimport { createHash } from \"node:crypto\";\nimport { captureMediaWithIndex, sanitizeMediaPayload } from \"./media.js\";\nimport {\n DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n DEFAULT_MAX_STREAM_CAPTURE_BYTES,\n DEFAULT_MAX_STREAM_CAPTURE_ITEMS,\n utf8ByteLength,\n} from \"../constants.js\";\n\ninterface ToolCallState {\n id: string;\n name: string;\n arguments: string;\n type: string;\n details: string;\n synthetic: boolean;\n incomplete: boolean;\n}\n\ninterface ChoiceState {\n role: string;\n content: string[];\n reasoning: string[];\n mediaCount: number;\n finishReason: string | null;\n toolCalls: Map<number, ToolCallState>;\n}\n\nfunction hash(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction stringify(value: unknown): string {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value ?? \"\");\n }\n}\n\n/** Incremental OpenAI-compatible choice/tool-fragment accumulator. */\nexport class ChoiceAccumulator {\n private readonly choices = new Map<number, ChoiceState>();\n private usage: any = null;\n private model = \"\";\n private responseId = \"\";\n private chunkCount = 0;\n private finalized = false;\n private capturedBytes = 0;\n private capturedItems = 0;\n private droppedBytes = 0;\n private droppedItems = 0;\n private toolCallCount = 0;\n private readonly incompleteReasons = new Set<string>();\n\n constructor(\n private readonly captureFidelity:\n | \"native\"\n | \"normalized\"\n | \"flattened\"\n | \"unknown\" = \"native\",\n ) {}\n\n addResponse(response: any, span?: Span): void {\n this.captureEnvelope(response);\n for (\n let position = 0;\n position < (response?.choices?.length ?? 0);\n position += 1\n ) {\n const choice = response.choices[position];\n const index = Number.isInteger(choice?.index) ? choice.index : position;\n if (!this.canRetainChoice(index)) continue;\n const message = choice?.message ?? {};\n const state = this.choice(index);\n if (message.role && String(message.role) !== state.role) {\n this.retainString(String(message.role), (value) => (state.role = value));\n }\n if (message.content != null) {\n const captured = span\n ? captureMediaWithIndex(\n span,\n `neatlogs.llm.output_messages.${index}`,\n message.content,\n \"output\",\n state.mediaCount,\n )\n : {\n value: sanitizeMediaPayload(message.content, \"output\"),\n count: 0,\n };\n state.mediaCount += captured.count;\n this.retainString(stringify(captured.value), (value) => state.content.push(value));\n }\n if (message.reasoning_content != null) {\n this.retainString(stringify(message.reasoning_content), (value) =>\n state.reasoning.push(value),\n );\n }\n this.addToolFragments(index, message.tool_calls);\n if (choice?.finish_reason != null) {\n this.retainString(String(choice.finish_reason), (value) => (state.finishReason = value));\n }\n }\n }\n\n addChunk(span: Span, chunk: any): void {\n const chunkIndex = this.chunkCount++;\n this.captureEnvelope(chunk);\n const summary: Array<Record<string, unknown>> = [];\n for (\n let position = 0;\n position < (chunk?.choices?.length ?? 0);\n position += 1\n ) {\n const choice = chunk.choices[position];\n const index = Number.isInteger(choice?.index) ? choice.index : position;\n if (!this.canRetainChoice(index)) continue;\n const delta = choice?.delta ?? {};\n const state = this.choice(index);\n if (delta.role && String(delta.role) !== state.role) {\n this.retainString(String(delta.role), (value) => (state.role = value));\n }\n const captured =\n delta.content == null\n ? { value: \"\", count: 0 }\n : captureMediaWithIndex(\n span,\n `neatlogs.llm.output_messages.${index}`,\n delta.content,\n \"output\",\n state.mediaCount,\n );\n state.mediaCount += captured.count;\n const content = delta.content == null ? \"\" : stringify(captured.value);\n const reasoning =\n delta.reasoning_content == null\n ? \"\"\n : stringify(delta.reasoning_content);\n const contentBytes = content\n ? this.retainString(content, (value) => state.content.push(value))\n : 0;\n const reasoningBytes = reasoning\n ? this.retainString(reasoning, (value) => state.reasoning.push(value))\n : 0;\n this.addToolFragments(index, delta.tool_calls);\n if (choice?.finish_reason != null) {\n this.retainString(String(choice.finish_reason), (value) => (state.finishReason = value));\n }\n summary.push({\n choice_index: index,\n content_bytes: contentBytes,\n reasoning_bytes: reasoningBytes,\n tool_fragments: Array.isArray(delta.tool_calls)\n ? delta.tool_calls.length\n : 0,\n finish_reason: choice?.finish_reason ?? null,\n });\n }\n if (\n chunkIndex < DEFAULT_MAX_SEMANTIC_STREAM_EVENTS &&\n (summary.length > 0 || chunk?.usage)\n ) {\n span.addEvent(\"neatlogs.stream.chunk\", {\n \"neatlogs.stream.chunk.index\": chunkIndex,\n \"neatlogs.stream.chunk.summary\": stringify({\n choices: summary,\n usage: !!chunk?.usage,\n }),\n });\n }\n }\n\n apply(span: Span): void {\n let flattenedToolIndex = 0;\n const indexes = [...this.choices.keys()].sort(\n (left, right) => left - right,\n );\n for (const choiceIndex of indexes) {\n const choice = this.choices.get(choiceIndex)!;\n const prefix = `neatlogs.llm.output_messages.${choiceIndex}`;\n span.setAttribute(`${prefix}.role`, choice.role || \"assistant\");\n const content = choice.content.join(\"\");\n if (content) span.setAttribute(`${prefix}.content`, content);\n const reasoning = choice.reasoning.join(\"\");\n if (reasoning) span.setAttribute(`${prefix}.thinking`, reasoning);\n if (choice.finishReason !== null) {\n span.setAttribute(\n `neatlogs.llm.choices.${choiceIndex}.finish_reason`,\n choice.finishReason,\n );\n if (choiceIndex === indexes[0]) {\n span.setAttribute(\"neatlogs.llm.finish_reason\", choice.finishReason);\n }\n }\n for (const toolIndex of [...choice.toolCalls.keys()].sort(\n (left, right) => left - right,\n )) {\n const tool = choice.toolCalls.get(toolIndex)!;\n if (!tool.id) {\n const context = span.spanContext();\n tool.id = `nl_${hash(\n `${context.traceId}:${context.spanId}:${choiceIndex}:${toolIndex}:${tool.name}:${hash(tool.arguments)}`,\n ).slice(0, 24)}`;\n tool.synthetic = true;\n }\n const toolPrefix = `neatlogs.llm.tool_calls.${flattenedToolIndex}`;\n span.setAttribute(`${toolPrefix}.id`, tool.id);\n if (tool.type) span.setAttribute(`${toolPrefix}.type`, tool.type);\n span.setAttribute(`${toolPrefix}.name`, tool.name);\n span.setAttribute(\n `${toolPrefix}.arguments`,\n tool.incomplete\n ? \"[incomplete: stream capture limit reached]\"\n : tool.arguments,\n );\n if (tool.details)\n span.setAttribute(`${toolPrefix}.details`, tool.details);\n span.setAttribute(`${toolPrefix}.choice_index`, choiceIndex);\n span.setAttribute(`${toolPrefix}.tool_call_index`, toolIndex);\n if (tool.synthetic)\n span.setAttribute(`${toolPrefix}.id_synthetic`, true);\n flattenedToolIndex += 1;\n }\n }\n if (this.model) span.setAttribute(\"neatlogs.llm.model_name\", this.model);\n if (this.responseId)\n span.setAttribute(\"neatlogs.llm.response_id\", this.responseId);\n this.applyUsage(span);\n span.setAttribute(\n \"neatlogs.capture_fidelity\",\n this.incompleteReasons.size > 0 ? \"truncated\" : this.captureFidelity,\n );\n if (this.chunkCount > 0 || this.incompleteReasons.size > 0) {\n span.setAttribute(\"neatlogs.stream.capture_bytes\", this.capturedBytes);\n span.setAttribute(\"neatlogs.stream.capture_items\", this.capturedItems);\n }\n if (this.incompleteReasons.size > 0) {\n span.setAttribute(\"neatlogs.stream.incomplete\", true);\n span.setAttribute(\n \"neatlogs.stream.incomplete_reason\",\n [...this.incompleteReasons].sort().join(\",\"),\n );\n span.setAttribute(\"neatlogs.stream.dropped_bytes\", this.droppedBytes);\n span.setAttribute(\"neatlogs.stream.dropped_bytes_is_lower_bound\", true);\n span.setAttribute(\"neatlogs.stream.dropped_items\", this.droppedItems);\n }\n if (this.chunkCount > 0) {\n span.setAttribute(\"neatlogs.stream.chunk_count\", this.chunkCount);\n if (this.chunkCount > DEFAULT_MAX_SEMANTIC_STREAM_EVENTS) {\n span.setAttribute(\n \"neatlogs.stream.events_dropped\",\n this.chunkCount - DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n );\n }\n }\n }\n\n finish(span: Span, interrupted = false): void {\n if (this.finalized) return;\n this.finalized = true;\n this.apply(span);\n if (interrupted) {\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n }\n\n fail(span: Span, error: unknown): void {\n if (this.finalized) return;\n this.finalized = true;\n this.apply(span);\n if ((error as any)?.name === \"AbortError\") {\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });\n if (error instanceof Error) span.recordException(error);\n }\n span.end();\n }\n\n private canRetainChoice(index: number): boolean {\n if (this.choices.has(index)) return true;\n if (this.choices.size < DEFAULT_MAX_STREAM_CAPTURE_ITEMS) return true;\n this.markDropped(\"item_limit_exceeded\", 0);\n return false;\n }\n\n private retainString(value: string, retain: (value: string) => void): number {\n const remaining = Math.max(0, DEFAULT_MAX_STREAM_CAPTURE_BYTES - this.capturedBytes);\n const bytes = utf8ByteLength(value, remaining);\n if (this.capturedItems >= DEFAULT_MAX_STREAM_CAPTURE_ITEMS) {\n this.markDropped(\"item_limit_exceeded\", bytes);\n return 0;\n }\n if (bytes > remaining) {\n this.markDropped(\"byte_limit_exceeded\", bytes);\n return 0;\n }\n retain(value);\n this.capturedItems += 1;\n this.capturedBytes += bytes;\n return bytes;\n }\n\n private markDropped(reason: string, bytes: number): void {\n this.droppedItems += 1;\n this.droppedBytes += bytes;\n this.incompleteReasons.add(reason);\n }\n\n private choice(index: number): ChoiceState {\n let choice = this.choices.get(index);\n if (!choice) {\n choice = {\n role: \"assistant\",\n content: [],\n reasoning: [],\n mediaCount: 0,\n finishReason: null,\n toolCalls: new Map(),\n };\n this.choices.set(index, choice);\n }\n return choice;\n }\n\n private addToolFragments(choiceIndex: number, fragments: any): void {\n if (!Array.isArray(fragments)) return;\n for (let position = 0; position < fragments.length; position += 1) {\n const fragment = fragments[position];\n const index = Number.isInteger(fragment?.index)\n ? fragment.index\n : position;\n const tools = this.choice(choiceIndex).toolCalls;\n let tool = tools.get(index);\n if (!tool) {\n if (this.toolCallCount >= DEFAULT_MAX_STREAM_CAPTURE_ITEMS) {\n this.markDropped(\"item_limit_exceeded\", 0);\n continue;\n }\n tool = {\n id: \"\",\n name: \"\",\n arguments: \"\",\n type: \"\",\n details: \"\",\n synthetic: false,\n incomplete: false,\n };\n tools.set(index, tool);\n this.toolCallCount += 1;\n }\n if (fragment?.id && String(fragment.id) !== tool.id) {\n this.retainString(String(fragment.id), (value) => (tool!.id = value));\n }\n if (fragment?.type && String(fragment.type) !== tool.type) {\n this.retainString(String(fragment.type), (value) => (tool!.type = value));\n }\n if (fragment?.function?.name && String(fragment.function.name) !== tool.name) {\n this.retainString(String(fragment.function.name), (value) => (tool!.name = value));\n }\n if (fragment?.function?.arguments != null) {\n const retained = this.retainString(\n stringify(fragment.function.arguments),\n (value) => (tool!.arguments += value),\n );\n if (retained === 0) tool.incomplete = true;\n }\n if (fragment && !fragment.function) {\n const retained = this.retainString(\n stringify(fragment),\n (value) => (tool!.details = value),\n );\n if (retained === 0) tool.incomplete = true;\n }\n }\n }\n\n private captureEnvelope(value: any): void {\n if (value?.usage) {\n const safeUsage = sanitizeMediaPayload(value.usage, \"output\");\n this.retainString(stringify(safeUsage), () => (this.usage = safeUsage));\n }\n if (value?.model && String(value.model) !== this.model) {\n this.retainString(String(value.model), (model) => (this.model = model));\n }\n if (value?.id && String(value.id) !== this.responseId) {\n this.retainString(String(value.id), (id) => (this.responseId = id));\n }\n }\n\n private applyUsage(span: Span): void {\n const usage = this.usage;\n if (!usage) return;\n span.setAttribute(\"neatlogs.llm.usage\", stringify(usage));\n if (usage.prompt_tokens != null)\n span.setAttribute(\"neatlogs.llm.token_count.prompt\", usage.prompt_tokens);\n if (usage.completion_tokens != null)\n span.setAttribute(\n \"neatlogs.llm.token_count.completion\",\n usage.completion_tokens,\n );\n if (usage.total_tokens != null)\n span.setAttribute(\"neatlogs.llm.token_count.total\", usage.total_tokens);\n if (usage.prompt_tokens_details?.cached_tokens != null) {\n span.setAttribute(\n \"neatlogs.llm.token_count.cache_read\",\n usage.prompt_tokens_details.cached_tokens,\n );\n }\n if (usage.completion_tokens_details?.reasoning_tokens != null) {\n span.setAttribute(\n \"neatlogs.llm.token_count.reasoning\",\n usage.completion_tokens_details.reasoning_tokens,\n );\n }\n }\n}\n"],"mappings":";AAmBA,SAAS,kBAAAA,uBAAiC;;;ACI1C;AAAA,EACE,SAASC;AAAA,OAKJ;;;ACdP;AAAA,EACE,SAASC;AAAA,EAET,oBAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,kBAAAC;AAAA,OAEK;;;ACtBP,SAAS,yBAAyB;AAYlC,IAAM,iBAAiB,IAAI,kBAAqC;AAwChE,IAAM,qBAAqB,IAAI,kBAAqC;;;AC7CpE,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACR;AAEA,IAAI,YAAsB;AAC1B,IAAI,YAAY;AAEhB,SAAS,sBAAgC;AACvC,QAAM,WAAW,QAAQ,IAAI,oBAAoB,YAAY;AAC7D,MAAI,YAAY,YAAY,YAAY;AACtC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,YAAY,oBAAoB;AAEzB,SAAS,YAAY;AAC1B,SAAO;AAAA,IACL,MAAM,YAAoB,MAAa;AACrC,UAAI,CAAC,aAAa,WAAW,SAAS,KAAK,WAAW,OAAO;AAC3D,gBAAQ,MAAM,cAAc,OAAO,IAAI,GAAG,IAAI;AAAA,MAChD;AAAA,IACF;AAAA,IACA,KAAK,YAAoB,MAAa;AACpC,UAAI,CAAC,aAAa,WAAW,SAAS,KAAK,WAAW,MAAM;AAC1D,gBAAQ,KAAK,cAAc,OAAO,IAAI,GAAG,IAAI;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,KAAK,YAAoB,MAAa;AACpC,UAAI,CAAC,aAAa,WAAW,SAAS,KAAK,WAAW,MAAM;AAC1D,gBAAQ,KAAK,cAAc,OAAO,IAAI,GAAG,IAAI;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,MAAM,YAAoB,MAAa;AACrC,UAAI,CAAC,aAAa,WAAW,SAAS,KAAK,WAAW,OAAO;AAC3D,gBAAQ,MAAM,cAAc,OAAO,IAAI,GAAG,IAAI;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACF;;;AC1CA,IAAM,SAAS,UAAU;;;ACczB,SAAS,qBAAAC,0BAAyB;AA6BlC,IAAM,cAAc,uBAAO,IAAI,2BAA2B;AAI1D,IAAM,KAAK;AACX,IAAM,WACJ,GAAG,WAAW,MAAM,GAAG,WAAW,IAAI,IAAIA,mBAAiC;AAEtE,SAAS,mBAAuC;AACrD,SAAO,SAAS,SAAS,GAAG;AAC9B;AAEO,SAAS,yBAA6C;AAC3D,SAAO,SAAS,SAAS,GAAG;AAC9B;AAEO,SAAS,4BAAgD;AAC9D,SAAO,SAAS,SAAS,GAAG;AAC9B;AAEO,SAAS,2BAA+C;AAC7D,SAAO,SAAS,SAAS,GAAG;AAC9B;AAEO,SAAS,mBAAuC;AACrD,SAAO,SAAS,SAAS,GAAG;AAC9B;AAEO,SAAS,yBAA0D;AACxE,SAAO,SAAS,SAAS,GAAG;AAC9B;;;AC1EA,SAAS,qBAAAC,0BAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,OAMJ;;;ACpBP,SAAS,qBAAAC,0BAAyB;AAUlC,IAAM,4BAA4B,uBAAO;AAAA,EACvC;AACF;AAIA,IAAM,iBAAiB;AACvB,IAAM,UACJ,eAAe,yBAAyB,MACvC,eAAe,yBAAyB,IACvC,IAAIA,mBAAwC;AAEzC,SAAS,kBAAoD;AAClE,SAAO,QAAQ,SAAS;AAC1B;;;ADEA,IAAM,yBAAyB,iBAAiB,oBAAoB;AASpE,IAAM,2BAA2B,uBAAO;AAAA,EACtC;AACF;AACA,IAAM,6BAA6B,uBAAO;AAAA,EACxC;AACF;AAQA,IAAMC,kBAAiB;AAKvB,IAAM,wBACJA,gBAAe,wBAAwB,MACtCA,gBAAe,wBAAwB,IAAI,IAAIC,mBAA2B;AAC7E,IAAM,gBACJD,gBAAe,0BAA0B,MACxCA,gBAAe,0BAA0B,IAAI;AAAA,EAC5C,UAAU;AACZ;AAIF,IAAM,gBAAwB;AAAA,EAC5B,YAAkB;AAChB,WAAO,UAAU,gBAAgB,oBAAoB;AAAA,EACvD;AAAA,EACA,gBACE,OACA,MACA,MACA,MACe;AACf,UAAM,KACJ,OAAO,SAAS,aACZ,OACA,OAAO,SAAS,aACd,OACA;AACR,WAAO,GAAI,UAAU,gBAAgB,oBAAoB,CAAC;AAAA,EAC5D;AACF;AAQO,SAAS,kBAAkB,MAAsB;AACtD,QAAM,SAAS,gBAAgB;AAC/B,MAAI,OAAQ,QAAO,OAAO,UAAU,IAAI;AACxC,SAAO,cAAc,UAAU,UAAU,IAAI,KAAK;AACpD;AAuFO,SAAS,2BAAoC;AAClD,SAAO,sBAAsB,SAAS,KAAK;AAC7C;AAGO,SAAS,wBAA0C;AACxD,SAAO,UAAU,QAAQ,yBAAyB,CAAC;AACrD;AAwBO,SAAS,yBAAyB,aAAgC;AACvE,SAAO,eAAe,yBAAyB;AACjD;AAqCO,SAAS,iBACd,MACA,IACA,aACG;AACH,QAAM,OAAO,eAAe,yBAAyB;AACrD,MAAI,MAAM,UAAU,QAAQ,MAAM,IAAI;AAGtC,MAAI,KAAK,SAAS,sBAAsB,MAAM,QAAW;AACvD,UAAM,IAAI,SAAS,wBAAwB,IAAI;AAAA,EACjD;AACA,SAAO,sBAAsB,IAAI,KAAK,EAAE;AAC1C;;;AE1OA,IAAME,UAAS,UAAU;AAElB,IAAM,kBAAkB;AACxB,IAAM,wBAAwB;AAG9B,SAAS,yBACd,UACoB;AACpB,MAAI,aAAa,UAAa,aAAa,KAAM,QAAO;AACxD,MAAI,OAAO,aAAa,SAAU,QAAO,YAAY;AACrD,MAAI;AACF,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC,QAAQ;AACN,WAAO,KAAK,UAAU,OAAO,QAAQ,CAAC;AAAA,EACxC;AACF;AAkBO,SAAS,uBACd,MACA,WACA,iBACA,SAAS,MACH;AAGN,QAAM,aAAa,SAAU,aAAa,iBAAiB,IAAK;AAChE,QAAM,eAAe,SAChB,mBAAmB,uBAAuB,IAC3C;AACJ,MAAI,CAAC,cAAc,CAAC,aAAc;AAClC,MAAI,CAAC,QAAQ;AACX,IAAAC,QAAO;AAAA,MACL;AAAA,IAEF;AACA;AAAA,EACF;AACA,MAAI,YAAY;AACd,SAAK,aAAa,iBAAiB,OAAO,UAAU,CAAC;AAAA,EACvD;AACA,QAAM,WAAW,yBAAyB,YAAY;AACtD,MAAI,UAAU;AACZ,SAAK,aAAa,uBAAuB,QAAQ;AAAA,EACnD;AACF;;;ACjEA,IAAMC,UAAS,UAAU;AAElB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAC9B,IAAM,2BAA2B;AACjC,IAAM,0BAA0B;AAQvC,SAAS,oBAAoB,OAAoC;AAC/D,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,WAAW;AACpB;AAEA,SAAS,qBAAqB,MAAY,KAAa,OAAqB;AAC1E,MAAI;AACF,SAAK,aAAa,KAAK,KAAK;AAAA,EAC9B,SAAS,OAAO;AACd,IAAAA,QAAO,MAAM,2BAA2B,GAAG,KAAK,KAAK,EAAE;AAAA,EACzD;AACF;AAOO,SAAS,uBACd,MACA,WACA,SAAS,MACT,UAAmC,CAAC,GAC9B;AAGN,MAAI,CAAC,QAAQ;AACX,QACE,aACA,QAAQ,mBACR,QAAQ,sBACR,QAAQ,mBACR;AACA,MAAAA,QAAO;AAAA,QACL;AAAA,MAEF;AAAA,IACF;AACA;AAAA,EACF;AAEA,QAAM,oBAAoB,aAAa,iBAAiB;AACxD,MAAI,mBAAmB;AACrB,QAAI;AACF,WAAK,aAAa,gBAAgB,OAAO,iBAAiB,CAAC;AAAA,IAC7D,SAAS,OAAO;AACd,MAAAA,QAAO,MAAM,2BAA2B,cAAc,KAAK,KAAK,EAAE;AAAA,IACpE;AAAA,EACF;AAEA,QAAM,kBAAkB;AAAA,IACtB,QAAQ,mBAAmB,uBAAuB;AAAA,EACpD;AACA,QAAM,sBAAsB,oBAAoB,iBAAiB;AACjE,MAAI,mBAAmB,oBAAoB,qBAAqB;AAC9D,yBAAqB,MAAM,uBAAuB,eAAe;AAAA,EACnE;AAEA,QAAM,qBAAqB;AAAA,IACzB,QAAQ,sBAAsB,0BAA0B;AAAA,EAC1D;AACA,MAAI,oBAAoB;AACtB,yBAAqB,MAAM,0BAA0B,kBAAkB;AAAA,EACzE;AAEA,QAAM,oBAAoB;AAAA,IACxB,QAAQ,qBAAqB,yBAAyB;AAAA,EACxD;AACA,MAAI,mBAAmB;AACrB,yBAAqB,MAAM,yBAAyB,iBAAiB;AAAA,EACvE;AACF;;;AC/GA,SAAS,cAAAC,mBAAkB;;;ACA3B,SAAS,kBAAkB;AA+CpB,IAAM,kCAAkC,KAAK,OAAO;AACpD,IAAM,gCAAgC,KAAK,OAAO;AAkGzD,IAAM,qBAAqB,KAAK;;;ADpIzB,IAAM,6BAA6B;AACnC,IAAM,kCAAkC;AAE/C,IAAM,4BAA4B;AAClC,IAAM,4BAA4B;AAClC,IAAM,kCAAkC,OAAO;AAC/C,IAAM,4BAA4B;AAClC,IAAM,gCAAgC;AACtC,IAAM,0BAA0B,KAAK,KAAM,gCAAgC,IAAK,CAAC,IAAI;AACrF,IAAM,2BAA2B;AAsDjC,IAAM,oBAAoB,uBAAO,IAAI,2BAA2B;AAChE,IAAM,gBAAgB;AACtB,IAAM,eACH,cAAc,iBAAiB,KAC/B;AAAA,EACC,eAAe,oBAAI,QAAQ;AAAA,EAC3B,oBAAoB,oBAAI,QAAQ;AAAA,EAChC,0BAA0B,oBAAI,QAAQ;AAAA,EACtC,oBAAoB,oBAAI,QAAQ;AAAA,EAChC,aAAa,oBAAI,QAAQ;AAAA,EACzB,0BAA0B,CAAC;AAC7B;AACF,cAAc,iBAAiB,IAAI;AAEnC,IAAM;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;AAEJ,SAAS,cAAc,MAAsB;AAC3C,SAAO,YAAY,IAAI,IAAI,KAAK;AAClC;AAGO,SAAS,sBAAsB,OAAe,QAAsB;AACzE,cAAY,IAAI,OAAO,cAAc,MAAM,CAAC;AAC9C;AAiBA,SAAS,kBAAkB,OAAuC;AAChE,QAAM,QAAQ,mBAAmB,IAAI,KAAK;AAC1C,MAAI,MAAO,QAAO;AAClB,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU,oBAAI,IAAyB;AAAA,EACzC;AACA,qBAAmB,IAAI,OAAO,KAAK;AACnC,SAAO;AACT;AAEA,SAAS,sBAAsB,SAAoC;AACjE,MAAI,QAAQ,SAAU;AACtB,UAAQ,WAAW;AACnB,UAAQ,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,MAAM,QAAQ,QAAQ,KAAK;AACrE,UAAQ,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,MAAM,QAAQ,QAAQ,KAAK;AACrE,UAAQ,MAAM,SAAS,OAAO,OAAO;AACvC;AAEA,IAAM,mBACJ,OAAO,yBAAyB,cAC5B,SACA,IAAI,qBAA0C,qBAAqB;AAEzE,SAAS,cAAc,MAAkC;AACvD,SAAO,cAAc,IAAI;AACzB,QAAM,QAAQ,cAAc,IAAI,IAAI;AACpC,MAAI,MAAO,QAAO;AAClB,QAAM,QAAQ;AAAA,IACZ,mBAAmB,IAAI,IAAI,KAAK,gBAAgB,KAAK;AAAA,EACvD;AACA,QAAM,UAA+B;AAAA,IACnC,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,MAAM,IAAI,QAAQ,IAAI;AAAA,EACxB;AACA,QAAM,kBAAkB,CAAC;AACzB,QAAM,SAAS,EAAE,OAAO,CAAC,GAAG,SAAS,gBAAgB;AACrD,QAAM,SAAS,IAAI,OAAO;AAC1B,gBAAc,IAAI,MAAM,MAAM;AAC9B,oBAAkB,SAAS,MAAM,SAAS,eAAe;AACzD,SAAO;AACT;AAeA,SAAS,YAAY,QAA6B;AAChD,QAAM,WAAW,GAAG,OAAO,MAAM,IAAI,OAAO,SAAS,IAAI,OAAO,OAAO;AACvE,SAAO,oBAAoBC,YAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AAC7F;AAEA,SAAS,UAAU,UAAkB,WAAW,IAAY;AAC1D,QAAM,QAAQ,SACX,YAAY,EACZ,QAAQ,sBAAsB,EAAE,EAChC,QAAQ,SAAS,EAAE;AACtB,MAAI,UAAU,wBAAyB,QAAO;AAC9C,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,CAAC,SAAS,SAAS,SAAS,UAAU,EAAE,SAAS,KAAK,EAAG,QAAO;AACpE,MAAI,SAAS,WAAW,QAAQ,EAAG,QAAO;AAC1C,MAAI,SAAS,WAAW,QAAQ,EAAG,QAAO;AAC1C,MAAI,SAAS,WAAW,QAAQ,EAAG,QAAO;AAC1C,MAAI,aAAa,qBAAqB,SAAS,WAAW,OAAO,GAAG;AAClE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,aAAa,UAAU,IAAI,KAAK;AACtC,SAAO,eAAe,UAAU,KAAK;AACvC;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,WAAW,MAAM,KAAK,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC,GAAG,YAAY,KAAK;AACjE,SAAO,aAAa,cAChB,eACA,aAAa,cACX,eACA;AACR;AAQA,SAAS,oBACP,UACA,UACA,UACA,SACA,QACA,QACiB;AACjB,QAAM,cAAcA,YAAW,QAAQ,EACpC,OAAO,SAAS,MAAM,GAAG,IAAI,CAAC,EAC9B,OAAO,OAAO,SAAS,MAAM,CAAC,EAC9B,OAAO,KAAK;AACf,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,MACN,IAAI,qBAAqB,YAAY,MAAM,GAAG,EAAE,CAAC;AAAA,MACjD,MAAM,UAAU,UAAU,QAAQ;AAAA,MAClC;AAAA,MACA,WAAW,YAAY;AAAA,MACvB;AAAA,MACA,OAAO;AAAA,MACP,cAAc,2BAA2B,MAAM;AAAA,IACjD;AAAA,EACF;AACF;AAEA,SAAS,aAAa,OAAe,cAAc,MAA4B;AAC7E,QAAM,aAAa,MAAM,QAAQ,QAAQ,EAAE;AAC3C,MAAI,CAAC,yBAAyB,KAAK,UAAU,KAAK,WAAW,SAAS,MAAM,GAAG;AAC7E,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,UAAU,WAAW,SAAS,IAAI,IAAI,IAAI,WAAW,SAAS,GAAG,IAAI,IAAI;AAC/E,UAAM,iBAAiB,KAAK,MAAO,WAAW,SAAS,IAAK,CAAC,IAAI;AACjE,QAAI,eAAe,kBAAkB,+BAA+B;AAClE,YAAM,QAAQ,OAAO,KAAK,YAAY,QAAQ;AAC9C,UAAI,MAAM,SAAS,QAAQ,EAAE,QAAQ,OAAO,EAAE,MAAM,WAAW,QAAQ,OAAO,EAAE,GAAG;AACjF,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL;AAAA,QACA,YAAY,MAAM;AAAA,QAClB,QAAQA,YAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAAA,MACzD;AAAA,IACF;AACA,UAAM,SAASA,YAAW,QAAQ;AAClC,QAAI,aAAa;AACjB,UAAM,kBAAkB,KAAK;AAC7B,aAAS,SAAS,GAAG,SAAS,WAAW,QAAQ,UAAU,iBAAiB;AAC1E,YAAM,QAAQ,OAAO,KAAK,WAAW,MAAM,QAAQ,SAAS,eAAe,GAAG,QAAQ;AACtF,aAAO,OAAO,KAAK;AACnB,oBAAc,MAAM;AAAA,IACtB;AACA,QAAI,eAAe,eAAgB,QAAO;AAC1C,WAAO,EAAE,YAAY,QAAQ,OAAO,OAAO,KAAK,EAAE;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aACP,OACA,UACA,UACA,SACA,cAAc,MACU;AACxB,aAAW,kBAAkB,QAAQ;AACrC,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,eAAW;AACX,QAAI,UAAU,MAAM,KAAK;AACzB,QAAI,UAAU,KAAK,OAAO,GAAG;AAC3B,YAAM,QAAQ,QAAQ,QAAQ,GAAG;AACjC,UAAI,QAAQ,EAAG,QAAO;AACtB,YAAM,SAAS,QAAQ,MAAM,GAAG,KAAK;AACrC,UAAI,CAAC,OAAO,YAAY,EAAE,MAAM,GAAG,EAAE,SAAS,QAAQ,EAAG,QAAO;AAChE,iBAAW,kBAAkB,OAAO,MAAM,KAAK,CAAC,EAAE,CAAC,KAAK,QAAQ;AAChE,gBAAU,QAAQ,MAAM,QAAQ,CAAC;AAAA,IACnC;AACA,QAAI,QAAQ,SAAS,yBAAyB;AAC5C,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,UAAU,aAAa,SAAS,WAAW;AACjD,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,YAAQ,QAAQ;AAChB,iBAAa,QAAQ;AACrB,aAAS,QAAQ;AAAA,EACnB,OAAO;AACL,qBAAiB;AACjB,iBAAa,MAAM;AACnB,aAASA,YAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD,QAAI,eAAe,cAAc,+BAA+B;AAC9D,cAAQ,WAAW,KAAK,KAAK;AAAA,IAC/B;AAAA,EACF;AACA,QAAM,WACJ,aAAa,8BAA8B,cAAc;AAC3D,QAAM,WAAW,aAAa;AAC9B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,MACN,IAAI,YAAY,OAAO,MAAM,GAAG,EAAE,CAAC;AAAA,MACnC,MAAM,UAAU,UAAU,QAAQ;AAAA,MAClC,QAAQ;AAAA,MACR,WAAW,YAAY;AAAA,MACvB,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,OAAO,WAAW,WAAW,WAAW,mBAAmB;AAAA,MAC3D,GAAI,WACA,EAAE,cAAc,4CAA4C,IAC5D,WACE,EAAE,cAAc,gCAAgC,IAChD,CAAC;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,gBACP,WACA,UACA,UACA,SACwB;AACxB,aAAW,kBAAkB,QAAQ;AACrC,MAAI,UAAU,SAAS,2BAA2B;AAChD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,gBAAgB;AACpB,MAAI,iBAAiB;AACrB,QAAM,sBAAsB,UAAU,KAAK;AAC3C,QAAM,mBAAmB,oBAAoB,WAAW,IAAI;AAC5D,QAAM,oBACJ,CAAC,oBACD,CAAC,sBAAsB,KAAK,mBAAmB,MAC9C,oBAAoB,SAAS,GAAG,KAC/B,oBAAoB,SAAS,GAAG,KAChC,oBAAoB,SAAS,GAAG;AACpC,MAAI;AACF,UAAM,SAAS,oBAAoB,oBAC/B,IAAI,IAAI,qBAAqB,0BAA0B,IACvD,IAAI,IAAI,mBAAmB;AAC/B,QAAI,OAAO,SAAS,YAAY,MAAM,SAAS;AAC7C,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,eACJ,oBACA,qBACA,OAAO,aAAa,WACpB,OAAO,aAAa,YACpB,0BAA0B,KAAK,mBAAmB,KAClD,OAAO,SAAS,WAAW,GAAG;AAChC,QAAI,cAAc;AAChB,uBAAiB;AACjB,aAAO,WAAW;AAClB,aAAO,WAAW;AAClB,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,sBAAgB,mBACZ,KAAK,OAAO,IAAI,GAAG,OAAO,QAAQ,KAClC,oBACE,OAAO,WACP,OAAO,SAAS;AAAA,IACxB,WAAW,OAAO,UAAU;AAC1B,uBAAiB;AACjB,sBAAgB,OAAO;AAAA,IACzB;AAAA,EACF,QAAQ;AACN,QAAI,+BAA+B,KAAK,mBAAmB,GAAG;AAC5D,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAASA,YAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AAClE,SAAO;AAAA,IACL,UAAU;AAAA,IACV,QAAQ;AAAA,MACN,IAAI,YAAY,OAAO,MAAM,GAAG,EAAE,CAAC;AAAA,MACnC,MAAM,UAAU,UAAU,QAAQ;AAAA,MAClC,QAAQ,iBAAiB,QAAQ;AAAA,MACjC,WAAW,YAAY;AAAA,MACvB,WAAW;AAAA,MACX;AAAA,MACA,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,cACP,OACA,SACA,cAAc,MACQ;AACtB,QAAM,QAA2B,CAAC;AAClC,QAAM,iBAAiB,oBAAI,IAAY;AACvC,MAAI,gBAAgB;AACpB,MAAI,YAAY;AAChB,QAAM,SAAS,CAAC,SAAuC;AACrD,QAAI,CAAC,KAAM;AACX,QAAI,MAAM,UAAU,+BAA+B;AACjD,kBAAY;AACZ;AAAA,IACF;AACA,UAAM,MAAM,GAAG,KAAK,OAAO,EAAE,IAAI,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,aAAa,EAAE,IAAI,KAAK,OAAO,IAAI;AACzG,QAAI,eAAe,IAAI,GAAG,EAAG;AAC7B,mBAAe,IAAI,GAAG;AACtB,QAAI,KAAK,SAAS,KAAK,OAAO,UAAU,kBAAkB;AACxD,UAAI,gBAAgB,KAAK,MAAM,aAAa,+BAA+B;AACzE,aAAK,QAAQ;AACb,aAAK,OAAO,QAAQ;AACpB,aAAK,OAAO,eAAe;AAAA,MAC7B,OAAO;AACL,yBAAiB,KAAK,MAAM;AAAA,MAC9B;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AACA,QAAM,UAAU,oBAAI,QAAgB;AACpC,MAAI,eAAe;AACnB,QAAM,QAAQ,CAAC,MAAW,oBAAoB,IAAI,oBAAoB,IAAI,QAAQ,MAAY;AAC5F,QAAI,OAAO,SAAS,YAAY,aAAa,KAAK,IAAI,GAAG;AACvD,aAAO,aAAa,MAAM,mBAAmB,mBAAmB,SAAS,WAAW,CAAC;AACrF;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,QAAI,MAAM,UAAU,+BAA+B;AACjD,kBAAY;AACZ;AAAA,IACF;AACA,QAAI,QAAQ,6BAA6B,gBAAgB,2BAA2B;AAClF;AAAA,IACF;AACA,QAAI,QAAQ,IAAI,IAAI,EAAG;AACvB,YAAQ,IAAI,IAAI;AAChB,oBAAgB;AAChB,QAAI;AACF,UAAI,gBAAgB,WAAY;AAChC,UAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,mBAAW,QAAQ,MAAM;AACvB,cAAI,gBAAgB,0BAA2B;AAC/C,cAAI,sBAAsB,OAAO,SAAS,YAAY,gBAAgB,aAAa;AACjF,kBAAM,SACJ,OAAO,SAAS,YAAY,6BAA6B,KAAK,IAAI,IAC9D,gBAAgB,MAAM,mBAAmB,mBAAmB,OAAO,IACnE,aAAa,MAAM,mBAAmB,mBAAmB,SAAS,WAAW;AACnF,mBAAO,MAAM;AAAA,UACf,OAAO;AACL,kBAAM,MAAM,mBAAmB,mBAAmB,QAAQ,CAAC;AAAA,UAC7D;AAAA,QACF;AACA;AAAA,MACF;AACA,YAAM,UAA+B,OAAO,QAAQ,IAAI;AACxD,YAAM,OAAO,CAAC,QAAyB,QAAQ,KAAK,CAAC,CAAC,IAAI,MAAM,SAAS,GAAG,IAAI,CAAC;AACjF,YAAM,WAAW,OAAO,KAAK,MAAM,KAAK,iBAAiB;AACzD,YAAM,qBAAqB,kBAAkB,QAAQ;AACrD,YAAM,gBAAgB,sBAAsB,kBAAkB,iBAAiB;AAC/E,UAAI,WAAW;AAAA,QACb;AAAA,UACE,KAAK,WAAW,KACd,KAAK,UAAU,KACf,KAAK,YAAY,KACjB,KAAK,WAAW,KAChB;AAAA,QACJ;AAAA,MACF;AACA,YAAM,iBAAiB,KAAK,QAAQ,KAAK,KAAK,eAAe,KAAK,KAAK,cAAc;AACrF,UAAI,CAAC,YAAY,iBAAiB,OAAO,mBAAmB,UAAU;AACpE,cAAM,SAAS,eAAe,YAAY;AAC1C,mBAAW;AAAA,UACT,kBAAkB,cAAc,WAAW,QACvC,oBACA,GAAG,aAAa,IAAI,MAAM;AAAA,QAChC;AAAA,MACF;AACA,YAAM,WAAW,KAAK,WAAW;AACjC,UAAI,QAAQ;AACZ,UAAI,YAAY,OAAO,aAAa,UAAU;AAC5C,YAAI;AACF,kBAAS,SAAqC;AAAA,QAChD,QAAQ;AACN,kBAAQ;AAAA,QACV;AAAA,MACF;AACA,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAM,SAAS,aAAa,KAAK,KAAK,IAClC,aAAa,OAAO,UAAU,SAAS,SAAS,WAAW,IAC3D,gBAAgB,OAAO,UAAU,SAAS,OAAO;AACrD,eAAO,MAAM;AAAA,MACf;AACA,YAAM,QAAQ,KAAK,aAAa,KAAK,KAAK,YAAY;AACtD,UAAI,SAAS,OAAO,UAAU,YAAY,OAAQ,MAAc,SAAS,UAAU;AACjF,cAAM,SAAS,OAAQ,MAAc,UAAU,SAAS;AACxD,cAAM,SAAS;AAAA,UACZ,MAAc;AAAA,UACf,YAAY,SAAS,MAAM;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,MAAM;AAAA,MACf;AACA,YAAM,SAAS,KAAK,aAAa,KAAK,KAAK,YAAY;AACvD,UAAI,UAAU,OAAO,WAAW,YAAY,OAAQ,OAAe,SAAS,UAAU;AACpF,cAAM,SAAS;AAAA,UACZ,OAAe;AAAA,UAChB,OAAQ,OAAe,aAAc,OAAe,YAAY,QAAQ;AAAA,UACxE;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,MAAM;AAAA,MACf;AACA,YAAM,WAAW,KAAK,WAAW,KAAK,KAAK,UAAU;AACrD,UAAI,OAAO,aAAa,UAAU;AAChC,cAAM,SAAS;AAAA,UACb;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,UACA;AAAA,QACF;AACA,eAAO,MAAM;AAAA,MACf;AACA,YAAM,MACJ,KAAK,MAAM,KACX,KAAK,OAAO,KACZ,KAAK,UAAU,MACd,uBAAuB,UAAU,KAAK,QAAQ,IAAI;AACrD,WAAK,OAAO,QAAQ,YAAY,eAAe,gBAAgB,iBAAiB,WAAW;AACzF,cAAM,SAAS,aAAa,KAAK,UAAU,eAAe,SAAS,WAAW;AAC9E,eAAO,MAAM;AAAA,MACf;AACA,YAAM,YACJ,KAAK,SAAS,KAAK,KAAK,UAAU,KAAK,KAAK,UAAU,KAAK,KAAK,SAAS,KAAK,KAAK,KAAK;AAC1F,UACE,OAAO,cAAc,aACpB,CAAC,QAAQ,KAAK,EAAE,SAAS,kBAAkB,KAAK,CAAC,CAAC,iBAAiB,CAAC,CAAC,WACtE;AACA,cAAM,SAAS,aAAa,KAAK,SAAS,IACtC,aAAa,WAAW,UAAU,iBAAiB,YAAY,SAAS,WAAW,IACnF,gBAAgB,WAAW,UAAU,iBAAiB,YAAY,OAAO;AAC7E,eAAO,MAAM;AAAA,MACf;AACA,iBAAW,CAAC,KAAK,IAAI,KAAK,SAAS;AACjC,YAAI,gBAAgB,0BAA2B;AAC/C,cAAM,QAAQ,IACX,YAAY,EACZ,QAAQ,WAAW,EAAE,EACrB,QAAQ,MAAM,EAAE;AACnB,cAAM,gBAAgB,kBAAkB,KAAK,KAAK;AAClD,YAAI,gBAAgB;AACpB,YAAI,CAAC,iBAAiB,iBAAiB,OAAO,mBAAmB,UAAU;AACzE,gBAAM,SAAS,eAAe,YAAY;AAC1C,0BAAgB;AAAA,YACd,kBAAkB,cAAc,WAAW,QACvC,oBACA,GAAG,aAAa,IAAI,MAAM;AAAA,UAChC;AAAA,QACF;AACA,cAAM,MAAM,eAAe,eAAe,QAAQ,CAAC;AAAA,MACrD;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,QAAM,KAAK;AACX,SAAO,EAAE,OAAO,OAAO,UAAU;AACnC;AAEA,SAAS,YAAY,QAA8C;AACjE,SAAO;AAAA,IACL,gBAAgB,OAAO;AAAA,MACrB,OAAO,QAAQ,MAAM,EAAE;AAAA,QAAO,CAAC,CAAC,GAAG,MACjC;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,EAAE,SAAS,GAAG;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,cAAc,OAAgB,YAAwC;AAC7E,QAAM,eAAe,oBAAI,IAAqB;AAC9C,QAAM,oBAA8C,CAAC;AACrD,aAAW,QAAQ,YAAY;AAC7B,QAAI,CAAC,KAAK,SAAU;AACpB,QAAI,KAAK,OAAO,UAAU,oBAAoB,KAAK,OAAO,UAAU,UAAU;AAC5E,YAAM,cAAc,YAAY,KAAK,MAAM;AAC3C,UAAI,KAAK,SAAS,SAAS,iCAAiC;AAC1D,0BAAkB,KAAK,CAAC,KAAK,UAAU,WAAW,CAAC;AAAA,MACrD,OAAO;AACL,qBAAa,IAAI,KAAK,UAAU,WAAW;AAAA,MAC7C;AAAA,IACF,WAAW,OAAO,KAAK,OAAO,cAAc,UAAU;AACpD,UAAI,KAAK,SAAS,SAAS,iCAAiC;AAC1D,0BAAkB,KAAK,CAAC,KAAK,UAAU,KAAK,OAAO,SAAS,CAAC;AAAA,MAC/D,OAAO;AACL,qBAAa,IAAI,KAAK,UAAU,KAAK,OAAO,SAAS;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,oBAAI,QAAyB;AAC7C,MAAI,eAAe;AACnB,QAAM,QAAQ,CAAC,MAAW,QAAQ,MAAW;AAC3C,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,SACJ,KAAK,UAAU,kCAAkC,aAAa,IAAI,IAAI,IAAI;AAC5E,UAAI,WAAW,OAAW,QAAO;AACjC,aAAO,kBAAkB,KAAK,CAAC,CAAC,QAAQ,MAAM,aAAa,IAAI,IAAI,CAAC,KAAK;AAAA,IAC3E;AACA,QAAI,gBAAgB,YAAY;AAC9B,YAAM,QAAQ,WAAW;AAAA,QACvB,CAAC,SACC,KAAK,mBAAmB,QACvB,KAAK,OAAO,WAAW,KAAK,UAAU,KAAK,MAAM,eAAe,KAAK;AAAA,MAC1E;AACA,aAAO,SAAS,CAAC,kBAAkB,QAAQ,EAAE,SAAS,OAAO,MAAM,OAAO,KAAK,CAAC,IAC5E,YAAY,MAAM,MAAM,IACxB;AAAA,IACN;AACA,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAI,QAAQ,6BAA6B,gBAAgB,2BAA2B;AAClF,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,QAAI,MAAO,QAAO;AAClB,oBAAgB;AAChB,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,YAAMC,UAAoB,CAAC;AAC3B,cAAQ,IAAI,MAAMA,OAAM;AACxB,iBAAW,QAAQ,MAAM;AACvB,YAAI,gBAAgB,2BAA2B;AAC7C,UAAAA,QAAO,KAAK,wBAAwB;AACpC;AAAA,QACF;AACA,QAAAA,QAAO,KAAK,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,MACpC;AACA,aAAOA;AAAA,IACT;AACA,UAAM,SAAkC,CAAC;AACzC,YAAQ,IAAI,MAAM,MAAM;AACxB,QAAI;AACF,iBAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC9C,YAAI,gBAAgB,2BAA2B;AAC7C,iBAAO,qBAAqB;AAC5B;AAAA,QACF;AACA,eAAO,GAAG,IAAI,MAAM,MAAM,QAAQ,CAAC;AAAA,MACrC;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,SAAO,MAAM,KAAK;AACpB;AAeO,SAAS,qBAAqB,OAAgB,UAAU,WAAoB;AACjF,MAAI;AACF,UAAM,aAAa,cAAc,OAAO,SAAS,KAAK;AACtD,WAAO,WAAW,YACd,2BACA,cAAc,OAAO,WAAW,KAAK;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,sBACd,MAIA,QACA,OACA,SACA,mBAAmB,GACC;AACpB,MAAI,YAAY;AAChB,MAAI;AACF,gBAAY,OAAO,KAAK,gBAAgB,cAAc,KAAK,YAAY;AAAA,EACzE,QAAQ;AACN,SAAK,aAAa,GAAG,MAAM,wBAAwB,sBAAsB;AACzE,WAAO,EAAE,OAAO,0BAA0B,OAAO,EAAE;AAAA,EACrD;AACA,QAAM,UAAU,cAAc,IAAc;AAC5C,QAAM,OAAO,yBAAyB,IAAI,OAAO;AACjD,QAAM,mBAAmB,MAAM,aAAa;AAC5C,MAAI;AACJ,MAAI;AACF,gBAAY,cAAc,OAAO,SAAS,aAAa,gBAAgB;AAAA,EACzE,QAAQ;AACN,SAAK,aAAa,GAAG,MAAM,wBAAwB,sBAAsB;AACzE,WAAO,EAAE,OAAO,0BAA0B,OAAO,EAAE;AAAA,EACrD;AACA,YAAU,MAAM,QAAQ,CAAC,MAAM,UAAU;AACvC,UAAM,eAAe,GAAG,MAAM,UAAU,mBAAmB,KAAK;AAChE,QAAI,KAAK,OAAO,UAAU,kBAAkB;AAC1C,UAAI,CAAC,WAAW;AACd,aAAK,QAAQ;AACb,aAAK,OAAO,QAAQ;AACpB,aAAK,OAAO,eAAe;AAAA,MAC7B,WAAW,CAAC,kBAAkB;AAC5B,aAAK,QAAQ;AACb,aAAK,OAAO,QAAQ;AACpB,aAAK,OAAO,eAAe,uBAAuB,MAAM,UAAU,4BAA4B;AAAA,MAChG,WAAW,CAAC,KAAK,OAAO;AACtB,aAAK,OAAO,QAAQ;AACpB,aAAK,OAAO,eAAe;AAAA,MAC7B,OAAO;AACL,cAAM,QAAQ,YAAY,KAAK,MAAM;AACrC,aAAK,OAAO,eAAe;AAC3B,cAAM,SAAS,cAAc,OAAO;AACpC,cAAM,YAAY,OAAO,MAAM;AAAA,UAC7B,CAAC,cACC,UAAU,WAAW,KAAK,OAAO,UAAU,UAAU,aAAa,KAAK,OAAO;AAAA,QAClF;AACA,YAAI,WAAW;AACb,cAAI,CAAC,UAAU,SAAS,SAAS,YAAY,GAAG;AAC9C,sBAAU,SAAS,KAAK,YAAY;AAAA,UACtC;AAAA,QACF,WAAW,OAAO,QAAQ,MAAM,SAAS,iCAAiC;AACxE,eAAK,QAAQ;AACb,iBAAO,KAAK,OAAO;AACnB,eAAK,OAAO,QAAQ;AACpB,eAAK,OAAO,eAAe;AAAA,QAC7B,WACE,OAAO,QAAQ,MAAM,QAAQ,KAAK,MAAM,aACxC,+BACA;AACA,eAAK,QAAQ;AACb,iBAAO,KAAK,OAAO;AACnB,eAAK,OAAO,QAAQ;AACpB,eAAK,OAAO,eAAe;AAAA,QAC7B,OAAO;AACL,iBAAO,QAAQ,MAAM,SAAS,KAAK,MAAM;AACzC,iBAAO,QAAQ,MAAM,SAAS;AAC9B,iBAAO,QAAQ,SAAS,KAAK,MAAM;AACnC,iBAAO,QAAQ,SAAS;AACxB,iBAAO,MAAM,KAAK;AAAA,YAChB,OAAO,KAAK;AAAA,YACZ,UAAU,CAAC,YAAY;AAAA,YACvB,QAAQ,EAAE,GAAG,KAAK,OAAO;AAAA,YACzB;AAAA,YACA,QAAQ,OAAO,KAAK,OAAO,MAAM;AAAA,YACjC,YAAY,OAAO,KAAK,OAAO,WAAW;AAAA,YAC1C,UAAU,OAAO,KAAK,OAAO,SAAS;AAAA,UACxC,CAAC;AAAA,QACH;AACA,YAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,wBAAc,OAAO,IAAc;AACnC,4BAAkB,WAAW,OAAO,eAAe;AACnD,gCAAsB,OAAO,OAAO;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AACA,WAAO,QAAQ,KAAK,MAAM,EAAE;AAAA,MAAQ,CAAC,CAAC,KAAK,KAAK,MAC9C,KAAK,aAAa,GAAG,YAAY,IAAI,GAAG,IAAI,KAAK;AAAA,IACnD;AAAA,EACF,CAAC;AACD,SAAO;AAAA,IACL,OAAO,UAAU,YACb,2BACA,cAAc,OAAO,UAAU,KAAK;AAAA,IACxC,OAAO,UAAU,MAAM;AAAA,EACzB;AACF;AAGO,SAAS,aACd,MAIA,QACA,OACA,SACS;AACT,SAAO,sBAAsB,MAAM,QAAQ,OAAO,OAAO,EAAE;AAC7D;;;AE12BA;AAAA,EACE,gBAAAC;AAAA,EACA;AAAA,OAEK;;;ACNA,IAAM,qCAAqC;AAC3C,IAAM,mCAAmC,OAAO;AAChD,IAAM,mCAAmC;AAGzC,SAAS,eACd,OACA,QAAQ,OAAO,kBACP;AACR,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,WAAW,CAAC;AAC/B,QAAI,OAAO,IAAM,UAAS;AAAA,aACjB,OAAO,KAAO,UAAS;AAAA,aACvB,QAAQ,SAAU,QAAQ,SAAU,IAAI,IAAI,MAAM,QAAQ;AACjE,YAAM,OAAO,MAAM,WAAW,IAAI,CAAC;AACnC,UAAI,QAAQ,SAAU,QAAQ,OAAQ;AACpC,iBAAS;AACT,aAAK;AAAA,MACP,MAAO,UAAS;AAAA,IAClB,MAAO,UAAS;AAChB,QAAI,QAAQ,MAAO,QAAO,QAAQ;AAAA,EACpC;AACA,SAAO;AACT;;;ADIA,IAAMC,UAAS,UAAU;;;AXWzB,IAAMC,UAAS,UAAU;AAMzB,IAAI,iBAAsC,CAAC;AAapC,SAAS,mBAAwC;AACtD,SAAO,EAAE,GAAG,eAAe;AAC7B;AAMO,IAAM,uBAAuBC,kBAAiB,2BAA2B;AACzE,IAAM,sBAAsBA,kBAAiB,0BAA0B;AACvE,IAAM,qBAAqBA,kBAAiB,yBAAyB;AACrE,IAAM,2BAA2BA,kBAAiB,+BAA+B;AACjF,IAAM,4BAA4BA,kBAAiB,gCAAgC;;;AD7B1F,IAAM,aAAa,oBAAI,IAAI,CAAC,YAAY,SAAS,SAAS,UAAU,CAAC;AAErE,SAAS,kBAA2B;AAClC,QAAM,OAAO,QAAQ,IAAI,sBAAsB,IAAI,KAAK,EAAE,YAAY;AACtE,SAAO,CAAC,CAAC,SAAS,KAAK,MAAM,KAAK,EAAE,SAAS,GAAG;AAClD;AAEA,SAAS,0BAAkC;AACzC,QAAM,aAAa,gBAAgB,GAAG;AACtC,MAAI,OAAO,eAAe,YAAY,WAAW,KAAK,EAAG,QAAO;AAChE,MAAI;AACF,UAAM,OAAO,iBAAiB,GAAG;AACjC,QAAI,OAAO,SAAS,YAAY,KAAK,KAAK,EAAG,QAAO;AAAA,EACtD,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAQA,SAAS,sBAAsB,MAAkB;AAC/C,yBAAuB,MAAM,QAAW,IAAI;AAC5C,yBAAuB,MAAM,QAAW,QAAW,IAAI;AACzD;AAOA,SAAS,iBAAiB,OAAa,MAAkB;AACvD,MAAI,QAAQ;AACZ,QAAM,QAAQ,IAAI,MAAM,OAAO;AAAA,IAC7B,IAAI,QAAQ,MAAM,WAAW;AAC3B,UAAI,SAAS,OAAO;AAClB,eAAO,IAAI,SAAsB;AAC/B,cAAI,MAAO;AACX,kBAAQ;AACR,cAAI;AACF,YAAC,OAAO,IAA8B,GAAG,IAAI;AAAA,UAC/C,UAAE;AACA,gBAAI;AACF,mBAAK,IAAI;AAAA,YACX,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,MAAM;AAC9C,aAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;AAAA,IAC5D;AAAA,EACF,CAAC;AACD,wBAAsB,OAAO,KAAK;AAClC,SAAO;AACT;AAqDA,IAAM,iBAAN,MAAuC;AAAA,EACrC,YAA6B,SAAiB;AAAjB;AAAA,EAAkB;AAAA,EAAlB;AAAA,EAE7B,UAAU,MAAc,SAAuB,SAAyB;AACtE,UAAM,OAAO;AAAA,MACV,SAAS,aACR,oBACF,KAAK;AAAA,IACP,EAAE,YAAY;AAGd,UAAM,MAAM,WAAW,yBAAyB;AAChD,UAAM,SAAS,sBAAsB;AACrC,UAAM,YACJ,gBAAgB,KAChB,CAAC,WAAW,IAAI,IAAI,KACpB,EAAE,WAAW,UAAa,OAAO,YAAY;AAE/C,QAAI,CAAC,WAAW;AACd,aAAO,KAAK,QAAQ,UAAU,MAAM,SAAS,GAAG;AAAA,IAClD;AAEA,UAAM,OAAO,KAAK,QAAQ;AAAA,MACxB,wBAAwB;AAAA,MACxB,EAAE,YAAY,EAAE,sBAAsB,YAAY,sBAAsB,KAAK,EAAE;AAAA,MAC/E;AAAA,IACF;AACA,0BAAsB,IAAI;AAC1B,UAAM,WAAWC,WAAU,QAAQ,KAAK,IAAI;AAC5C,UAAM,QAAQ,KAAK,QAAQ,UAAU,MAAM,SAAS,QAAQ;AAC5D,WAAO,iBAAiB,OAAO,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA,EAIA,mBAAmB,IAAI,SACpB,KAAK,QAAQ;AAAA,IACZ,GAAG;AAAA,EACL;AACJ;AAQO,SAAS,kBAAkB,MAAsB;AAEtD,SAAO,IAAI,eAAe,kBAAkB,IAAI,CAAC;AACnD;;;Ac/MA,SAAS,kBAAAC,uBAAiC;AAC1C,SAAS,cAAAC,mBAAkB;AA4B3B,SAAS,KAAK,OAAuB;AACnC,SAAOC,YAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEA,SAAS,UAAU,OAAwB;AACzC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,SAAS,EAAE;AAAA,EAC3B;AACF;AAGO,IAAM,oBAAN,MAAwB;AAAA,EAc7B,YACmB,kBAID,UAChB;AALiB;AAAA,EAKhB;AAAA,EALgB;AAAA,EAdF,UAAU,oBAAI,IAAyB;AAAA,EAChD,QAAa;AAAA,EACb,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,gBAAgB;AAAA,EACP,oBAAoB,oBAAI,IAAY;AAAA,EAUrD,YAAY,UAAe,MAAmB;AAC5C,SAAK,gBAAgB,QAAQ;AAC7B,aACM,WAAW,GACf,YAAY,UAAU,SAAS,UAAU,IACzC,YAAY,GACZ;AACA,YAAM,SAAS,SAAS,QAAQ,QAAQ;AACxC,YAAM,QAAQ,OAAO,UAAU,QAAQ,KAAK,IAAI,OAAO,QAAQ;AAC/D,UAAI,CAAC,KAAK,gBAAgB,KAAK,EAAG;AAClC,YAAM,UAAU,QAAQ,WAAW,CAAC;AACpC,YAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,UAAI,QAAQ,QAAQ,OAAO,QAAQ,IAAI,MAAM,MAAM,MAAM;AACvD,aAAK,aAAa,OAAO,QAAQ,IAAI,GAAG,CAAC,UAAW,MAAM,OAAO,KAAM;AAAA,MACzE;AACA,UAAI,QAAQ,WAAW,MAAM;AAC3B,cAAM,WAAW,OACb;AAAA,UACE;AAAA,UACA,gCAAgC,KAAK;AAAA,UACrC,QAAQ;AAAA,UACR;AAAA,UACA,MAAM;AAAA,QACR,IACA;AAAA,UACE,OAAO,qBAAqB,QAAQ,SAAS,QAAQ;AAAA,UACrD,OAAO;AAAA,QACT;AACJ,cAAM,cAAc,SAAS;AAC7B,aAAK,aAAa,UAAU,SAAS,KAAK,GAAG,CAAC,UAAU,MAAM,QAAQ,KAAK,KAAK,CAAC;AAAA,MACnF;AACA,UAAI,QAAQ,qBAAqB,MAAM;AACrC,aAAK;AAAA,UAAa,UAAU,QAAQ,iBAAiB;AAAA,UAAG,CAAC,UACvD,MAAM,UAAU,KAAK,KAAK;AAAA,QAC5B;AAAA,MACF;AACA,WAAK,iBAAiB,OAAO,QAAQ,UAAU;AAC/C,UAAI,QAAQ,iBAAiB,MAAM;AACjC,aAAK,aAAa,OAAO,OAAO,aAAa,GAAG,CAAC,UAAW,MAAM,eAAe,KAAM;AAAA,MACzF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,MAAY,OAAkB;AACrC,UAAM,aAAa,KAAK;AACxB,SAAK,gBAAgB,KAAK;AAC1B,UAAM,UAA0C,CAAC;AACjD,aACM,WAAW,GACf,YAAY,OAAO,SAAS,UAAU,IACtC,YAAY,GACZ;AACA,YAAM,SAAS,MAAM,QAAQ,QAAQ;AACrC,YAAM,QAAQ,OAAO,UAAU,QAAQ,KAAK,IAAI,OAAO,QAAQ;AAC/D,UAAI,CAAC,KAAK,gBAAgB,KAAK,EAAG;AAClC,YAAM,QAAQ,QAAQ,SAAS,CAAC;AAChC,YAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,UAAI,MAAM,QAAQ,OAAO,MAAM,IAAI,MAAM,MAAM,MAAM;AACnD,aAAK,aAAa,OAAO,MAAM,IAAI,GAAG,CAAC,UAAW,MAAM,OAAO,KAAM;AAAA,MACvE;AACA,YAAM,WACJ,MAAM,WAAW,OACb,EAAE,OAAO,IAAI,OAAO,EAAE,IACtB;AAAA,QACE;AAAA,QACA,gCAAgC,KAAK;AAAA,QACrC,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,MACR;AACN,YAAM,cAAc,SAAS;AAC7B,YAAM,UAAU,MAAM,WAAW,OAAO,KAAK,UAAU,SAAS,KAAK;AACrE,YAAM,YACJ,MAAM,qBAAqB,OACvB,KACA,UAAU,MAAM,iBAAiB;AACvC,YAAM,eAAe,UACjB,KAAK,aAAa,SAAS,CAAC,UAAU,MAAM,QAAQ,KAAK,KAAK,CAAC,IAC/D;AACJ,YAAM,iBAAiB,YACnB,KAAK,aAAa,WAAW,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,CAAC,IACnE;AACJ,WAAK,iBAAiB,OAAO,MAAM,UAAU;AAC7C,UAAI,QAAQ,iBAAiB,MAAM;AACjC,aAAK,aAAa,OAAO,OAAO,aAAa,GAAG,CAAC,UAAW,MAAM,eAAe,KAAM;AAAA,MACzF;AACA,cAAQ,KAAK;AAAA,QACX,cAAc;AAAA,QACd,eAAe;AAAA,QACf,iBAAiB;AAAA,QACjB,gBAAgB,MAAM,QAAQ,MAAM,UAAU,IAC1C,MAAM,WAAW,SACjB;AAAA,QACJ,eAAe,QAAQ,iBAAiB;AAAA,MAC1C,CAAC;AAAA,IACH;AACA,QACE,aAAa,uCACZ,QAAQ,SAAS,KAAK,OAAO,QAC9B;AACA,WAAK,SAAS,yBAAyB;AAAA,QACrC,+BAA+B;AAAA,QAC/B,iCAAiC,UAAU;AAAA,UACzC,SAAS;AAAA,UACT,OAAO,CAAC,CAAC,OAAO;AAAA,QAClB,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,MAAkB;AACtB,QAAI,qBAAqB;AACzB,UAAM,UAAU,CAAC,GAAG,KAAK,QAAQ,KAAK,CAAC,EAAE;AAAA,MACvC,CAAC,MAAM,UAAU,OAAO;AAAA,IAC1B;AACA,eAAW,eAAe,SAAS;AACjC,YAAM,SAAS,KAAK,QAAQ,IAAI,WAAW;AAC3C,YAAM,SAAS,gCAAgC,WAAW;AAC1D,WAAK,aAAa,GAAG,MAAM,SAAS,OAAO,QAAQ,WAAW;AAC9D,YAAM,UAAU,OAAO,QAAQ,KAAK,EAAE;AACtC,UAAI,QAAS,MAAK,aAAa,GAAG,MAAM,YAAY,OAAO;AAC3D,YAAM,YAAY,OAAO,UAAU,KAAK,EAAE;AAC1C,UAAI,UAAW,MAAK,aAAa,GAAG,MAAM,aAAa,SAAS;AAChE,UAAI,OAAO,iBAAiB,MAAM;AAChC,aAAK;AAAA,UACH,wBAAwB,WAAW;AAAA,UACnC,OAAO;AAAA,QACT;AACA,YAAI,gBAAgB,QAAQ,CAAC,GAAG;AAC9B,eAAK,aAAa,8BAA8B,OAAO,YAAY;AAAA,QACrE;AAAA,MACF;AACA,iBAAW,aAAa,CAAC,GAAG,OAAO,UAAU,KAAK,CAAC,EAAE;AAAA,QACnD,CAAC,MAAM,UAAU,OAAO;AAAA,MAC1B,GAAG;AACD,cAAM,OAAO,OAAO,UAAU,IAAI,SAAS;AAC3C,YAAI,CAAC,KAAK,IAAI;AACZ,gBAAM,UAAU,KAAK,YAAY;AACjC,eAAK,KAAK,MAAM;AAAA,YACd,GAAG,QAAQ,OAAO,IAAI,QAAQ,MAAM,IAAI,WAAW,IAAI,SAAS,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AAAA,UACvG,EAAE,MAAM,GAAG,EAAE,CAAC;AACd,eAAK,YAAY;AAAA,QACnB;AACA,cAAM,aAAa,2BAA2B,kBAAkB;AAChE,aAAK,aAAa,GAAG,UAAU,OAAO,KAAK,EAAE;AAC7C,YAAI,KAAK,KAAM,MAAK,aAAa,GAAG,UAAU,SAAS,KAAK,IAAI;AAChE,aAAK,aAAa,GAAG,UAAU,SAAS,KAAK,IAAI;AACjD,aAAK;AAAA,UACH,GAAG,UAAU;AAAA,UACb,KAAK,aACD,+CACA,KAAK;AAAA,QACX;AACA,YAAI,KAAK;AACP,eAAK,aAAa,GAAG,UAAU,YAAY,KAAK,OAAO;AACzD,aAAK,aAAa,GAAG,UAAU,iBAAiB,WAAW;AAC3D,aAAK,aAAa,GAAG,UAAU,oBAAoB,SAAS;AAC5D,YAAI,KAAK;AACP,eAAK,aAAa,GAAG,UAAU,iBAAiB,IAAI;AACtD,8BAAsB;AAAA,MACxB;AAAA,IACF;AACA,QAAI,KAAK,MAAO,MAAK,aAAa,2BAA2B,KAAK,KAAK;AACvE,QAAI,KAAK;AACP,WAAK,aAAa,4BAA4B,KAAK,UAAU;AAC/D,SAAK,WAAW,IAAI;AACpB,SAAK;AAAA,MACH;AAAA,MACA,KAAK,kBAAkB,OAAO,IAAI,cAAc,KAAK;AAAA,IACvD;AACA,QAAI,KAAK,aAAa,KAAK,KAAK,kBAAkB,OAAO,GAAG;AAC1D,WAAK,aAAa,iCAAiC,KAAK,aAAa;AACrE,WAAK,aAAa,iCAAiC,KAAK,aAAa;AAAA,IACvE;AACA,QAAI,KAAK,kBAAkB,OAAO,GAAG;AACnC,WAAK,aAAa,8BAA8B,IAAI;AACpD,WAAK;AAAA,QACH;AAAA,QACA,CAAC,GAAG,KAAK,iBAAiB,EAAE,KAAK,EAAE,KAAK,GAAG;AAAA,MAC7C;AACA,WAAK,aAAa,iCAAiC,KAAK,YAAY;AACpE,WAAK,aAAa,gDAAgD,IAAI;AACtE,WAAK,aAAa,iCAAiC,KAAK,YAAY;AAAA,IACtE;AACA,QAAI,KAAK,aAAa,GAAG;AACvB,WAAK,aAAa,+BAA+B,KAAK,UAAU;AAChE,UAAI,KAAK,aAAa,oCAAoC;AACxD,aAAK;AAAA,UACH;AAAA,UACA,KAAK,aAAa;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,MAAY,cAAc,OAAa;AAC5C,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,MAAM,IAAI;AACf,QAAI,aAAa;AACf,WAAK,aAAa,6BAA6B,IAAI;AACnD,WAAK,UAAU,EAAE,MAAMC,gBAAe,MAAM,CAAC;AAAA,IAC/C,OAAO;AACL,WAAK,UAAU,EAAE,MAAMA,gBAAe,GAAG,CAAC;AAAA,IAC5C;AACA,SAAK,IAAI;AAAA,EACX;AAAA,EAEA,KAAK,MAAY,OAAsB;AACrC,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,MAAM,IAAI;AACf,QAAK,OAAe,SAAS,cAAc;AACzC,WAAK,aAAa,6BAA6B,IAAI;AACnD,WAAK,UAAU,EAAE,MAAMA,gBAAe,MAAM,CAAC;AAAA,IAC/C,OAAO;AACL,WAAK,UAAU,EAAE,MAAMA,gBAAe,OAAO,SAAS,OAAO,KAAK,EAAE,CAAC;AACrE,UAAI,iBAAiB,MAAO,MAAK,gBAAgB,KAAK;AAAA,IACxD;AACA,SAAK,IAAI;AAAA,EACX;AAAA,EAEQ,gBAAgB,OAAwB;AAC9C,QAAI,KAAK,QAAQ,IAAI,KAAK,EAAG,QAAO;AACpC,QAAI,KAAK,QAAQ,OAAO,iCAAkC,QAAO;AACjE,SAAK,YAAY,uBAAuB,CAAC;AACzC,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,OAAe,QAAyC;AAC3E,UAAM,YAAY,KAAK,IAAI,GAAG,mCAAmC,KAAK,aAAa;AACnF,UAAM,QAAQ,eAAe,OAAO,SAAS;AAC7C,QAAI,KAAK,iBAAiB,kCAAkC;AAC1D,WAAK,YAAY,uBAAuB,KAAK;AAC7C,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,WAAW;AACrB,WAAK,YAAY,uBAAuB,KAAK;AAC7C,aAAO;AAAA,IACT;AACA,WAAO,KAAK;AACZ,SAAK,iBAAiB;AACtB,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT;AAAA,EAEQ,YAAY,QAAgB,OAAqB;AACvD,SAAK,gBAAgB;AACrB,SAAK,gBAAgB;AACrB,SAAK,kBAAkB,IAAI,MAAM;AAAA,EACnC;AAAA,EAEQ,OAAO,OAA4B;AACzC,QAAI,SAAS,KAAK,QAAQ,IAAI,KAAK;AACnC,QAAI,CAAC,QAAQ;AACX,eAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS,CAAC;AAAA,QACV,WAAW,CAAC;AAAA,QACZ,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,WAAW,oBAAI,IAAI;AAAA,MACrB;AACA,WAAK,QAAQ,IAAI,OAAO,MAAM;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,aAAqB,WAAsB;AAClE,QAAI,CAAC,MAAM,QAAQ,SAAS,EAAG;AAC/B,aAAS,WAAW,GAAG,WAAW,UAAU,QAAQ,YAAY,GAAG;AACjE,YAAM,WAAW,UAAU,QAAQ;AACnC,YAAM,QAAQ,OAAO,UAAU,UAAU,KAAK,IAC1C,SAAS,QACT;AACJ,YAAM,QAAQ,KAAK,OAAO,WAAW,EAAE;AACvC,UAAI,OAAO,MAAM,IAAI,KAAK;AAC1B,UAAI,CAAC,MAAM;AACT,YAAI,KAAK,iBAAiB,kCAAkC;AAC1D,eAAK,YAAY,uBAAuB,CAAC;AACzC;AAAA,QACF;AACA,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,WAAW;AAAA,UACX,MAAM;AAAA,UACN,SAAS;AAAA,UACT,WAAW;AAAA,UACX,YAAY;AAAA,QACd;AACA,cAAM,IAAI,OAAO,IAAI;AACrB,aAAK,iBAAiB;AAAA,MACxB;AACA,UAAI,UAAU,MAAM,OAAO,SAAS,EAAE,MAAM,KAAK,IAAI;AACnD,aAAK,aAAa,OAAO,SAAS,EAAE,GAAG,CAAC,UAAW,KAAM,KAAK,KAAM;AAAA,MACtE;AACA,UAAI,UAAU,QAAQ,OAAO,SAAS,IAAI,MAAM,KAAK,MAAM;AACzD,aAAK,aAAa,OAAO,SAAS,IAAI,GAAG,CAAC,UAAW,KAAM,OAAO,KAAM;AAAA,MAC1E;AACA,UAAI,UAAU,UAAU,QAAQ,OAAO,SAAS,SAAS,IAAI,MAAM,KAAK,MAAM;AAC5E,aAAK,aAAa,OAAO,SAAS,SAAS,IAAI,GAAG,CAAC,UAAW,KAAM,OAAO,KAAM;AAAA,MACnF;AACA,UAAI,UAAU,UAAU,aAAa,MAAM;AACzC,cAAM,WAAW,KAAK;AAAA,UACpB,UAAU,SAAS,SAAS,SAAS;AAAA,UACrC,CAAC,UAAW,KAAM,aAAa;AAAA,QACjC;AACA,YAAI,aAAa,EAAG,MAAK,aAAa;AAAA,MACxC;AACA,UAAI,YAAY,CAAC,SAAS,UAAU;AAClC,cAAM,WAAW,KAAK;AAAA,UACpB,UAAU,QAAQ;AAAA,UAClB,CAAC,UAAW,KAAM,UAAU;AAAA,QAC9B;AACA,YAAI,aAAa,EAAG,MAAK,aAAa;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,OAAkB;AACxC,QAAI,OAAO,OAAO;AAChB,YAAM,YAAY,qBAAqB,MAAM,OAAO,QAAQ;AAC5D,WAAK,aAAa,UAAU,SAAS,GAAG,MAAO,KAAK,QAAQ,SAAU;AAAA,IACxE;AACA,QAAI,OAAO,SAAS,OAAO,MAAM,KAAK,MAAM,KAAK,OAAO;AACtD,WAAK,aAAa,OAAO,MAAM,KAAK,GAAG,CAAC,UAAW,KAAK,QAAQ,KAAM;AAAA,IACxE;AACA,QAAI,OAAO,MAAM,OAAO,MAAM,EAAE,MAAM,KAAK,YAAY;AACrD,WAAK,aAAa,OAAO,MAAM,EAAE,GAAG,CAAC,OAAQ,KAAK,aAAa,EAAG;AAAA,IACpE;AAAA,EACF;AAAA,EAEQ,WAAW,MAAkB;AACnC,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAO;AACZ,SAAK,aAAa,sBAAsB,UAAU,KAAK,CAAC;AACxD,QAAI,MAAM,iBAAiB;AACzB,WAAK,aAAa,mCAAmC,MAAM,aAAa;AAC1E,QAAI,MAAM,qBAAqB;AAC7B,WAAK;AAAA,QACH;AAAA,QACA,MAAM;AAAA,MACR;AACF,QAAI,MAAM,gBAAgB;AACxB,WAAK,aAAa,kCAAkC,MAAM,YAAY;AACxE,QAAI,MAAM,uBAAuB,iBAAiB,MAAM;AACtD,WAAK;AAAA,QACH;AAAA,QACA,MAAM,sBAAsB;AAAA,MAC9B;AAAA,IACF;AACA,QAAI,MAAM,2BAA2B,oBAAoB,MAAM;AAC7D,WAAK;AAAA,QACH;AAAA,QACA,MAAM,0BAA0B;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AACF;;;Af3YA,IAAM,cAAc;AACpB,IAAM,WAAW;AACjB,IAAM,SAAS;AAER,SAAS,gBAAkC,QAAc;AAC9D,SAAO,cAAc,QAAQ,CAAC,CAAC;AACjC;AAUO,SAAS,UACd,MACA,IACmC;AACnC,SAAO,eAAe,WAAW,MAA+B;AAC9D,UAAM,SAAS,kBAAkB,WAAW;AAC5C,UAAM,OAAO,OAAO;AAAA,MAClB,QAAQ,IAAI;AAAA,MACZ;AAAA,QACE,YAAY;AAAA,UACV,sBAAsB;AAAA,UACtB,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,MACA,yBAAyB;AAAA,IAC3B;AACA,SAAK;AAAA,MACH;AAAA,MACA,cAAc,aAAa,MAAM,uBAAuB,MAAM,OAAO,CAAC;AAAA,IACxE;AACA,WAAO,iBAAiB,MAAM,YAAY;AACxC,UAAI;AACF,cAAM,SAAS,MAAM,GAAG,IAAI;AAC5B,aAAK;AAAA,UACH;AAAA,UACA,cAAc,aAAa,MAAM,wBAAwB,QAAQ,QAAQ,CAAC;AAAA,QAC5E;AACA,aAAK,UAAU,EAAE,MAAMC,gBAAe,GAAG,CAAC;AAC1C,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,oBAAY,MAAM,GAAG;AACrB,cAAM;AAAA,MACR,UAAE;AACA,aAAK,IAAI;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,cAAc,QAAa,MAAqB;AACvD,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,KAAK,MAAM,UAAU;AACvB,YAAM,QAAQ,QAAQ,IAAI,KAAK,MAAM,QAAQ;AAC7C,UAAI,OAAO,SAAS,YAAY,OAAO,IAAI,EAAE,WAAW,GAAG,EAAG,QAAO;AAErE,YAAM,cAAc,CAAC,GAAG,MAAM,OAAO,IAAI,CAAC;AAC1C,YAAM,UAAU,YAAY,KAAK,GAAG;AAEpC,UAAI,YAAY,6BAA6B,OAAO,UAAU,YAAY;AACxE,eAAO,4BAA4B,MAAM,KAAK,GAAG,CAAC;AAAA,MACpD;AACA,UAAI,YAAY,sBAAsB,OAAO,UAAU,YAAY;AACjE,eAAO,sBAAsB,MAAM,KAAK,GAAG,CAAC;AAAA,MAC9C;AAEA,UAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,YAAY,WAAW,GAAG;AAC3F,eAAO,cAAc,OAAO,WAAW;AAAA,MACzC;AAEA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEA,SAAS,YAAY,MAAyB;AAC5C,MAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,QAAM,MAAM,KAAK,KAAK,SAAS,CAAC;AAChC,SAAO,CAAC,QAAQ,eAAe,aAAa,MAAM,EAAE,SAAS,GAAG;AAClE;AAMA,SAAS,4BAA4B,UAAmC;AACtE,SAAO,SAAU,SAAc,MAAkB;AAC/C,UAAM,SAAS,kBAAkB,WAAW;AAC5C,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,WAAkB,MAAM,YAAY,CAAC;AAC3C,UAAM,WAAW,MAAM,WAAW;AAElC,UAAM,OAAO,OAAO,UAAU,wCAAwC;AAAA,MACpE,YAAY;AAAA,QACV,sBAAsB;AAAA,QACtB,yBAAyB;AAAA,QACzB,uBAAuB;AAAA,QACvB,2BAA2B;AAAA,QAC3B,6BAA6B;AAAA,MAC/B;AAAA,IACF,GAAG,yBAAyB,CAAC;AAE7B,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,MAAM,SAAS,CAAC;AACtB,WAAK,aAAa,+BAA+B,CAAC,SAAS,IAAI,QAAQ,EAAE;AACzE,UAAI,OAAO,IAAI,YAAY,UAAU;AACnC,aAAK,aAAa,+BAA+B,CAAC,YAAY,IAAI,OAAO;AAAA,MAC3E,WAAW,IAAI,SAAS;AACtB,cAAM,WAAW;AAAA,UACf;AAAA,UACA,+BAA+B,CAAC;AAAA,UAChC,IAAI;AAAA,UACJ;AAAA,QACF;AACA,aAAK,aAAa,+BAA+B,CAAC,YAAY,cAAc,QAAQ,CAAC;AAAA,MACvF;AACA,UAAI,IAAI,cAAc;AACpB,aAAK,aAAa,+BAA+B,CAAC,iBAAiB,IAAI,YAAY;AAAA,MACrF;AAAA,IACF;AAEA,QAAI,MAAM,OAAO;AACf,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;AAC1C,cAAM,KAAK,KAAK,MAAM,CAAC,GAAG,YAAY,CAAC;AACvC,aAAK,aAAa,sBAAsB,CAAC,SAAS,GAAG,QAAQ,EAAE;AAC/D,YAAI,GAAG,YAAa,MAAK,aAAa,sBAAsB,CAAC,gBAAgB,GAAG,WAAW;AAC3F,YAAI,GAAG,WAAY,MAAK,aAAa,sBAAsB,CAAC,iBAAiB,cAAc,GAAG,UAAU,CAAC;AAAA,MAC3G;AAAA,IACF;AAEA,wBAAoB,MAAM,IAAI;AAE9B,QAAI,UAAU;AACZ,aAAO,EAAE,GAAG,KAAK;AACjB,YAAM,aAAa,KAAK,kBAAkB,CAAC;AAC3C,UAAI,CAAC,WAAW,eAAe;AAC7B,aAAK,iBAAiB,EAAE,GAAG,YAAY,eAAe,KAAK;AAAA,MAC7D;AAAA,IACF;AAEA,UAAM,UAAU,iBAAiB,MAAM,MAAM,SAAS,MAAM,GAAG,IAAI,CAAC;AAEpE,WAAO,QAAQ;AAAA,MACb,CAAC,aAAkB;AACjB,YAAI,UAAU;AACZ,iBAAO,wBAAwB,UAAU,IAAI;AAAA,QAC/C;AACA,6BAAqB,MAAM,QAAQ;AACnC,eAAO;AAAA,MACT;AAAA,MACA,CAAC,QAAa;AACZ,oBAAY,MAAM,GAAG;AACrB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,sBAAsB,UAAmC;AAChE,SAAO,SAAU,SAAc,MAAkB;AAC/C,UAAM,SAAS,kBAAkB,WAAW;AAC5C,UAAM,QAAQ,MAAM,SAAS;AAE7B,UAAM,OAAO,OAAO,UAAU,iCAAiC;AAAA,MAC7D,YAAY;AAAA,QACV,sBAAsB;AAAA,QACtB,yBAAyB;AAAA,QACzB,uBAAuB;AAAA,QACvB,2BAA2B;AAAA,QAC3B,eAAe;AAAA,MACjB;AAAA,IACF,GAAG,yBAAyB,CAAC;AAE7B,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,IACF;AACA,SAAK,aAAa,eAAe,cAAc,iBAAiB,EAAE,CAAC;AAEnE,UAAM,UAAU,iBAAiB,MAAM,MAAM,SAAS,MAAM,GAAG,IAAI,CAAC;AAEpE,WAAO,QAAQ;AAAA,MACb,CAAC,aAAkB;AACjB,cAAM,iBAAiB;AAAA,UACrB;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV;AAAA,QACF;AACA,YAAI,UAAU,aAAa;AACzB,eAAK,aAAa,uCAAuC,WAAW;AACpE,eAAK,aAAa,0CAA0C,SAAS,WAAW;AAAA,QAClF,WAAW,UAAU,QAAQ;AAC3B,eAAK,aAAa,gBAAgB,cAAc,cAAc,CAAC;AAAA,QACjE;AACA,YAAI,UAAU,MAAO,MAAK,aAAa,2BAA2B,SAAS,KAAK;AAChF,YAAI,UAAU,OAAO;AACnB,cAAI,SAAS,MAAM,gBAAgB,KAAM,MAAK,aAAa,mCAAmC,SAAS,MAAM,YAAY;AACzH,cAAI,SAAS,MAAM,iBAAiB,KAAM,MAAK,aAAa,uCAAuC,SAAS,MAAM,aAAa;AAAA,QACjI;AACA,aAAK,UAAU,EAAE,MAAMA,gBAAe,GAAG,CAAC;AAC1C,aAAK,IAAI;AACT,eAAO;AAAA,MACT;AAAA,MACA,CAAC,QAAa;AACZ,oBAAY,MAAM,GAAG;AACrB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,wBAAwB,QAAa,MAAiB;AAC7D,QAAM,cAAc,IAAI,kBAAkB;AAC1C,QAAM,WAAW,yBAAyB,IAAI;AAC9C,MAAI,wBAAwB;AAC5B,QAAM,wBAAwB,SAAS,OAAO,aAAa,GAAG,KAAK,MAAM;AAEzE,MAAI,CAAC,uBAAuB;AAC1B,gBAAY,YAAY,QAAQ,IAAI;AACpC,gBAAY,OAAO,QAAQ;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,OAAO,OAAO,eAAe,MAAM,CAAC;AAC3D,SAAO,OAAO,SAAS,MAAM;AAE7B,UAAQ,OAAO,aAAa,IAAI,WAAY;AAC1C,UAAM,WAAW,sBAAsB;AACvC,WAAO;AAAA,MACL,MAAM,OAAqC;AACzC,YAAI;AACF,gBAAM,SAAS,MAAM,SAAS,KAAK;AACnC,cAAI,OAAO,MAAM;AACf,wBAAY,OAAO,UAAU,qBAAqB;AAClD,mBAAO;AAAA,UACT;AACA,sBAAY,SAAS,MAAM,OAAO,KAAK;AACvC,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,sBAAY,KAAK,UAAU,GAAG;AAC9B,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,MAAM,OAAO,OAA2C;AACtD,gCAAwB;AACxB,cAAM,SAAS,OAAO,SAAS,SAAS,KAAK,KAAK,EAAE,MAAM,MAAM,OAAO,OAAU;AACjF,YAAI,OAAO,KAAM,aAAY,OAAO,UAAU,IAAI;AAAA,YAC7C,aAAY,SAAS,MAAM,OAAO,KAAK;AAC5C,eAAO;AAAA,MACT;AAAA,MACA,MAAM,MAAM,KAAyC;AACnD,oBAAY,KAAK,UAAU,GAAG;AAC9B,YAAI,SAAS,MAAO,QAAO,SAAS,MAAM,GAAG;AAC7C,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,qBAAqB,MAAY,UAAqB;AAC7D,QAAM,UAAU,UAAU,WAAW,CAAC;AACtC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,UAAU,QAAQ,CAAC,GAAG;AAC5B,QAAI,CAAC,QAAS;AAEd,SAAK,aAAa,gCAAgC,CAAC,SAAS,WAAW;AACvE,QAAI,QAAQ,SAAS;AACnB,YAAM,WAAW;AAAA,QACf;AAAA,QACA,gCAAgC,CAAC;AAAA,QACjC,QAAQ;AAAA,QACR;AAAA,MACF;AACA,WAAK;AAAA,QACH,gCAAgC,CAAC;AAAA,QACjC,OAAO,aAAa,WAAW,WAAW,cAAc,QAAQ;AAAA,MAClE;AAAA,IACF;AACA,QAAI,QAAQ,YAAY;AACtB,eAAS,IAAI,GAAG,IAAI,QAAQ,WAAW,QAAQ,KAAK;AAClD,cAAM,KAAK,QAAQ,WAAW,CAAC;AAC/B,aAAK,aAAa,2BAA2B,CAAC,OAAO,GAAG,MAAM,EAAE;AAChE,aAAK,aAAa,2BAA2B,CAAC,SAAS,GAAG,UAAU,QAAQ,EAAE;AAC9E,aAAK;AAAA,UACH,2BAA2B,CAAC;AAAA,UAC5B;AAAA,YACE;AAAA,YACA,2BAA2B,CAAC;AAAA,YAC5B,GAAG,UAAU,aAAa;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,QAAQ,CAAC,EAAE,eAAe;AAC5B,WAAK,aAAa,8BAA8B,QAAQ,CAAC,EAAE,aAAa;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,QAAQ,UAAU;AACxB,MAAI,OAAO;AACT,QAAI,MAAM,iBAAiB,KAAM,MAAK,aAAa,mCAAmC,MAAM,aAAa;AACzG,QAAI,MAAM,qBAAqB,KAAM,MAAK,aAAa,uCAAuC,MAAM,iBAAiB;AACrH,QAAI,MAAM,gBAAgB,KAAM,MAAK,aAAa,kCAAkC,MAAM,YAAY;AACtG,QAAI,MAAM,uBAAuB,iBAAiB,MAAM;AACtD,WAAK,aAAa,uCAAuC,MAAM,sBAAsB,aAAa;AAAA,IACpG;AACA,QAAI,MAAM,2BAA2B,oBAAoB,MAAM;AAC7D,WAAK,aAAa,sCAAsC,MAAM,0BAA0B,gBAAgB;AAAA,IAC1G;AAAA,EACF;AAEA,MAAI,UAAU,MAAO,MAAK,aAAa,2BAA2B,SAAS,KAAK;AAEhF,OAAK,UAAU,EAAE,MAAMA,gBAAe,GAAG,CAAC;AAC1C,OAAK,IAAI;AACX;AAMA,SAAS,2BACP,MACA,QACA,OACQ;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,cAAc,aAAa,MAAM,QAAQ,OAAO,QAAQ,CAAC;AAAA,EAClE;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;AAAA,EAC3B,QAAQ;AACN,SAAK,aAAa,8BAA8B,IAAI;AACpD,SAAK,aAAa,qCAAqC,wBAAwB;AAC/E,WAAO;AAAA,EACT;AACA,QAAM,WAAW,cAAc,MAAM;AACrC,QAAM,YAAY,cAAc,aAAa,MAAM,QAAQ,QAAQ,QAAQ,CAAC;AAC5E,SAAO,aAAa,YAAY,QAAQ;AAC1C;AAEA,SAAS,yBAAyB,MAAkB;AAClD,SAAO,IAAI,MAAM,MAAM;AAAA,IACrB,IAAI,QAAQ,UAAU;AACpB,UAAI,aAAa,gBAAgB;AAC/B,eAAO,CAAC,MAAcC,WACpB,OAAO;AAAA,UACL;AAAA,UACA,8CAA8C,KAAK,IAAI,IACnD,2BAA2B,QAAQ,MAAMA,MAAK,IAC7CA;AAAA,QACP;AAAA,MACJ;AACA,YAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,MAAM;AAClD,aAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;AAAA,IAC5D;AAAA,EACF,CAAC;AACH;AAEA,SAAS,oBAAoB,MAAY,MAAiB;AACxD,MAAI,MAAM,eAAe,KAAM,MAAK,aAAa,4BAA4B,KAAK,WAAW;AAC7F,MAAI,MAAM,SAAS,KAAM,MAAK,aAAa,sBAAsB,KAAK,KAAK;AAC3E,MAAI,MAAM,cAAc,KAAM,MAAK,aAAa,2BAA2B,KAAK,UAAU;AAC1F,MAAI,MAAM,qBAAqB,KAAM,MAAK,aAAa,kCAAkC,KAAK,iBAAiB;AAC/G,MAAI,MAAM,oBAAoB,KAAM,MAAK,aAAa,iCAAiC,KAAK,gBAAgB;AAC5G,MAAI,MAAM,KAAM,MAAK,aAAa,+BAA+B,cAAc,KAAK,IAAI,CAAC;AAC3F;AAEA,SAAS,cAAc,OAAwB;AAC7C,MAAI;AACF,WAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,MAAY,KAAoB;AACnD,MAAI,eAAe,OAAO;AACxB,SAAK,UAAU,EAAE,MAAMD,gBAAe,OAAO,SAAS,IAAI,QAAQ,CAAC;AACnE,SAAK,gBAAgB,GAAG;AAAA,EAC1B,OAAO;AACL,SAAK,UAAU,EAAE,MAAMA,gBAAe,OAAO,SAAS,OAAO,GAAG,EAAE,CAAC;AAAA,EACrE;AACA,OAAK,IAAI;AACX;","names":["SpanStatusCode","otelTrace","otelTrace","createContextKey","ROOT_CONTEXT","SpanStatusCode","AsyncLocalStorage","AsyncLocalStorage","AsyncLocalStorage","neatlogsGlobal","AsyncLocalStorage","logger","logger","logger","createHash","createHash","result","ROOT_CONTEXT","logger","logger","createContextKey","otelTrace","SpanStatusCode","createHash","createHash","SpanStatusCode","SpanStatusCode","value"]}
1
+ {"version":3,"sources":["../src/azure-openai.ts","../src/core/auto-root.ts","../src/core/context.ts","../src/prompt/template.ts","../src/core/logger.ts","../src/core/mask.ts","../src/core/identity.ts","../src/core/provider.ts","../src/core/active-client.ts","../src/core/end-user.ts","../src/core/session.ts","../src/core/media.ts","../src/core/upload-authority.ts","../src/decorators/base.ts","../src/constants.ts","../src/core/choice-accumulator.ts"],"sourcesContent":["/**\n * Neatlogs Azure OpenAI wrapper — ES6 Proxy-based.\n *\n * Azure OpenAI is accessed through the `openai` SDK's `AzureOpenAI` client,\n * which exposes the same `chat.completions.create()` shape as `OpenAI`. This\n * wrapper traces those calls with provider `azure` so Azure traffic is\n * distinguished from direct OpenAI traffic.\n *\n * Usage:\n * import { wrapAzureOpenAI } from 'neatlogs/azure-openai';\n * import { AzureOpenAI } from 'openai';\n * const client = wrapAzureOpenAI(new AzureOpenAI({ endpoint, apiKey, apiVersion }));\n * await client.chat.completions.create({ model: 'gpt-4o', messages: [...] });\n *\n * Intercepts:\n * - client.chat.completions.create() — non-streaming and streaming\n * - client.responses.create() — Responses API\n */\n\nimport { SpanStatusCode, type Span } from '@opentelemetry/api';\nimport { getProviderTracer } from './core/auto-root.js';\nimport { captureMedia } from './core/media.js';\nimport { ChoiceAccumulator } from './core/choice-accumulator.js';\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n withNeatlogsSpan,\n} from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.azure_openai';\nconst PROVIDER = 'azure';\nconst SYSTEM = 'azure';\n\nexport function wrapAzureOpenAI<T extends object>(client: T): T {\n return wrapNamespace(client, []) as T;\n}\n\n/**\n * Wrap a tool/function implementation to emit TOOL spans when executed.\n *\n * Usage:\n * const getWeather = traceTool('get_weather', async (args: { city: string }) => {\n * return `Weather in ${args.city}: sunny`;\n * });\n */\nexport function traceTool<TArgs = any, TResult = any>(\n name: string,\n fn: (args: TArgs) => TResult | Promise<TResult>,\n): (args: TArgs) => Promise<TResult> {\n return async function tracedTool(args: TArgs): Promise<TResult> {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const span = tracer.startSpan(\n `tool.${name}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n },\n },\n getNeatlogsParentContext(),\n );\n span.setAttribute(\n 'input.value',\n safeStringify(captureMedia(span, 'neatlogs.tool.input', args, 'input')),\n );\n return withNeatlogsSpan(span, async () => {\n try {\n const result = await fn(args);\n span.setAttribute(\n 'output.value',\n safeStringify(captureMedia(span, 'neatlogs.tool.output', result, 'output')),\n );\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n } finally {\n span.end();\n }\n });\n };\n}\n\nfunction wrapNamespace(target: any, path: string[]): any {\n return new Proxy(target, {\n get(obj, prop, receiver) {\n const value = Reflect.get(obj, prop, receiver);\n if (typeof prop === 'symbol' || String(prop).startsWith('_')) return value;\n\n const currentPath = [...path, String(prop)];\n const pathStr = currentPath.join('.');\n\n if (pathStr === 'chat.completions.create' && typeof value === 'function') {\n return tracedChatCompletionsCreate(value.bind(obj));\n }\n if (pathStr === 'responses.create' && typeof value === 'function') {\n return tracedResponsesCreate(value.bind(obj));\n }\n\n if (value && typeof value === 'object' && !Array.isArray(value) && isNamespace(currentPath)) {\n return wrapNamespace(value, currentPath);\n }\n\n return value;\n },\n });\n}\n\nfunction isNamespace(path: string[]): boolean {\n if (path.length > 3) return false;\n const key = path[path.length - 1];\n return ['chat', 'completions', 'responses', 'beta'].includes(key);\n}\n\n// ---------------------------------------------------------------------------\n// chat.completions.create — non-streaming + streaming\n// ---------------------------------------------------------------------------\n\nfunction tracedChatCompletionsCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n const isStream = opts?.stream === true;\n\n const span = tracer.startSpan('azure_openai.chat.completions.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': PROVIDER,\n 'neatlogs.llm.system': SYSTEM,\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': isStream,\n },\n }, getNeatlogsParentContext());\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n span.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg.role ?? '');\n if (typeof msg.content === 'string') {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, msg.content);\n } else if (msg.content) {\n const captured = captureMedia(\n span,\n `neatlogs.llm.input_messages.${i}`,\n msg.content,\n 'input',\n );\n span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, safeStringify(captured));\n }\n if (msg.tool_call_id) {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.tool_call_id`, msg.tool_call_id);\n }\n }\n\n if (opts?.tools) {\n for (let i = 0; i < opts.tools.length; i++) {\n const fn = opts.tools[i]?.function ?? {};\n span.setAttribute(`neatlogs.llm.tools.${i}.name`, fn.name ?? '');\n if (fn.description) span.setAttribute(`neatlogs.llm.tools.${i}.description`, fn.description);\n if (fn.parameters) span.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify(fn.parameters));\n }\n }\n\n setInvocationParams(span, opts);\n\n if (isStream) {\n opts = { ...opts };\n const streamOpts = opts.stream_options ?? {};\n if (!streamOpts.include_usage) {\n opts.stream_options = { ...streamOpts, include_usage: true };\n }\n }\n\n const promise = withNeatlogsSpan(span, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n if (isStream) {\n return wrapAsyncIterableStream(response, span);\n }\n finalizeChatResponse(span, response);\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// responses.create\n// ---------------------------------------------------------------------------\n\nfunction tracedResponsesCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n\n const span = tracer.startSpan('azure_openai.responses.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': PROVIDER,\n 'neatlogs.llm.system': SYSTEM,\n 'neatlogs.llm.model_name': model,\n 'input.value': '',\n },\n }, getNeatlogsParentContext());\n\n const capturedInput = captureMedia(\n span,\n 'neatlogs.llm.input_messages.0',\n opts?.input,\n 'input',\n );\n span.setAttribute('input.value', safeStringify(capturedInput ?? ''));\n\n const promise = withNeatlogsSpan(span, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n const capturedOutput = captureMedia(\n span,\n 'neatlogs.llm.output_messages.0',\n response?.output,\n 'output',\n );\n if (response?.output_text) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', response.output_text);\n } else if (response?.output) {\n span.setAttribute('output.value', safeStringify(capturedOutput));\n }\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n if (response?.usage) {\n if (response.usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', response.usage.input_tokens);\n if (response.usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', response.usage.output_tokens);\n }\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming support\n// ---------------------------------------------------------------------------\n\nfunction wrapAsyncIterableStream(stream: any, span: Span): any {\n const accumulator = new ChoiceAccumulator();\n const safeSpan = mediaSafeAccumulatorSpan(span);\n let cancellationRequested = false;\n const originalAsyncIterator = stream?.[Symbol.asyncIterator]?.bind(stream);\n\n if (!originalAsyncIterator) {\n accumulator.addResponse(stream, span);\n accumulator.finish(safeSpan);\n return stream;\n }\n\n const wrapped = Object.create(Object.getPrototypeOf(stream));\n Object.assign(wrapped, stream);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await iterator.next();\n if (result.done) {\n accumulator.finish(safeSpan, cancellationRequested);\n return result;\n }\n accumulator.addChunk(span, result.value);\n return result;\n } catch (err) {\n accumulator.fail(safeSpan, err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n cancellationRequested = true;\n const result = await (iterator.return?.(value) ?? { done: true, value: undefined });\n if (result.done) accumulator.finish(safeSpan, true);\n else accumulator.addChunk(span, result.value);\n return result;\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n accumulator.fail(safeSpan, err);\n if (iterator.throw) return iterator.throw(err);\n throw err;\n },\n };\n };\n\n return wrapped;\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming response finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeChatResponse(span: Span, response: any): void {\n const choices = response?.choices ?? [];\n for (let i = 0; i < choices.length; i++) {\n const message = choices[i]?.message;\n if (!message) continue;\n\n span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, 'assistant');\n if (message.content) {\n const captured = captureMedia(\n span,\n `neatlogs.llm.output_messages.${i}`,\n message.content,\n 'output',\n );\n span.setAttribute(\n `neatlogs.llm.output_messages.${i}.content`,\n typeof captured === 'string' ? captured : safeStringify(captured),\n );\n }\n if (message.tool_calls) {\n for (let j = 0; j < message.tool_calls.length; j++) {\n const tc = message.tool_calls[j];\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.function?.name ?? '');\n span.setAttribute(\n `neatlogs.llm.tool_calls.${j}.arguments`,\n sanitizedJsonToolArguments(\n span,\n `neatlogs.llm.tool_calls.${j}.arguments`,\n tc.function?.arguments ?? '{}',\n ),\n );\n }\n }\n if (choices[i].finish_reason) {\n span.setAttribute('neatlogs.llm.finish_reason', choices[i].finish_reason);\n }\n }\n\n const usage = response?.usage;\n if (usage) {\n if (usage.prompt_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.prompt_tokens);\n if (usage.completion_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.completion_tokens);\n if (usage.total_tokens != null) span.setAttribute('neatlogs.llm.token_count.total', usage.total_tokens);\n if (usage.prompt_tokens_details?.cached_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.prompt_tokens_details.cached_tokens);\n }\n if (usage.completion_tokens_details?.reasoning_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.reasoning', usage.completion_tokens_details.reasoning_tokens);\n }\n }\n\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction sanitizedJsonToolArguments(\n span: Span,\n prefix: string,\n value: unknown,\n): string {\n if (typeof value !== 'string') {\n return safeStringify(captureMedia(span, prefix, value, 'output'));\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n span.setAttribute('neatlogs.stream.incomplete', true);\n span.setAttribute('neatlogs.stream.incomplete_reason', 'invalid_tool_arguments');\n return '[incomplete: invalid tool arguments omitted]';\n }\n const original = safeStringify(parsed);\n const sanitized = safeStringify(captureMedia(span, prefix, parsed, 'output'));\n return original === sanitized ? value : sanitized;\n}\n\nfunction mediaSafeAccumulatorSpan(span: Span): Span {\n return new Proxy(span, {\n get(target, property) {\n if (property === 'setAttribute') {\n return (name: string, value: unknown) =>\n target.setAttribute(\n name,\n /^neatlogs\\.llm\\.tool_calls\\.\\d+\\.arguments$/.test(name)\n ? sanitizedJsonToolArguments(target, name, value)\n : (value as any),\n );\n }\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\nfunction setInvocationParams(span: Span, opts: any): void {\n if (opts?.temperature != null) span.setAttribute('neatlogs.llm.temperature', opts.temperature);\n if (opts?.top_p != null) span.setAttribute('neatlogs.llm.top_p', opts.top_p);\n if (opts?.max_tokens != null) span.setAttribute('neatlogs.llm.max_tokens', opts.max_tokens);\n if (opts?.frequency_penalty != null) span.setAttribute('neatlogs.llm.frequency_penalty', opts.frequency_penalty);\n if (opts?.presence_penalty != null) span.setAttribute('neatlogs.llm.presence_penalty', opts.presence_penalty);\n if (opts?.stop) span.setAttribute('neatlogs.llm.stop_sequences', safeStringify(opts.stop));\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction recordError(span: Span, err: unknown): void {\n if (err instanceof Error) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });\n span.recordException(err);\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });\n }\n span.end();\n}\n","/**\n * Auto-root for direct-provider wrappers.\n *\n * The backend only renders a trace once it contains a *parentless* span of a\n * root-eligible kind (WORKFLOW / CHAIN / AGENT / MCP_TOOL / LLM). Direct-provider\n * wrappers may emit either a root-eligible LLM span or a non-root span\n * (EMBEDDING / TOOL). A bare non-root call still needs a workflow parent so the\n * trace can finalize.\n *\n * `getProviderTracer(name)` returns a Tracer facade used *only* by those\n * direct-provider wrappers: when `startSpan` would otherwise produce a\n * parentless, non-root span, it transparently opens a WORKFLOW root (named\n * after the configured `workflowName`) and ends it when the provider span\n * ends. Every wrapper path — non-streaming, streaming finalize, error —\n * funnels through `span.end()`, so wrapping `end()` covers them all with no\n * changes to the wrappers' streaming code.\n *\n * Framework wrappers (langchain, mastra, openai-agents, ai-sdk, ...) keep\n * using `trace.getTracer(...)` directly — they already emit their own root and\n * thread context explicitly, so auto-root must never fire for them.\n */\n\nimport {\n trace as otelTrace,\n type Tracer,\n type Span,\n type Context,\n type SpanOptions,\n} from '@opentelemetry/api';\n\nimport { getSessionConfig } from './context.js';\nimport { applySessionAttributes } from './session.js';\nimport { applyEndUserAttributes } from './end-user.js';\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n getActiveNeatlogsSpan,\n withNeatlogsSpan,\n} from './provider.js';\nimport { getActiveClient } from './active-client.js';\nimport { aliasMediaCaptureSpan } from './media.js';\n\nconst boundAutoRoots = new WeakMap<object, Span>();\n\n// A parentless span of one of these kinds already satisfies the backend's\n// root requirement, so it must NOT be wrapped in another root.\nconst ROOT_KINDS = new Set(['workflow', 'chain', 'agent', 'mcp_tool', 'llm']);\n\nfunction autoRootEnabled(): boolean {\n const val = (process.env.NEATLOGS_AUTO_ROOT ?? '').trim().toLowerCase();\n return !['false', '0', 'no', 'off'].includes(val);\n}\n\nfunction resolveRootWorkflowName(): string {\n const clientName = getActiveClient()?.workflowName;\n if (typeof clientName === 'string' && clientName.trim()) return clientName;\n try {\n const name = getSessionConfig()?.workflowName;\n if (typeof name === 'string' && name.trim()) return name;\n } catch {\n /* ignore */\n }\n return 'workflow';\n}\n\n/**\n * Stamp request-scoped identify() identity onto a freshly-created auto-root.\n * The auto-root IS the trace root, and wrapper-only code has no user-controlled\n * root to put trace()/span() args on, so identity comes purely from the\n * identify() context (passed undefined per-call values fall back to it).\n */\nfunction stampRootIdentity(root: Span): void {\n applySessionAttributes(root, undefined, true);\n applyEndUserAttributes(root, undefined, undefined, true);\n}\n\n/**\n * Wrap a provider span so that ending it also ends the auto-created WORKFLOW\n * root. A Proxy keeps this transparent across OTel versions: every property /\n * method except `end` passes straight through to the child span.\n */\nexport function bindSpanToAutoRoot(child: Span, root: Span | undefined): Span {\n if (!root) return child;\n let ended = false;\n const proxy = new Proxy(child, {\n get(target, prop, _receiver) {\n if (prop === 'end') {\n return (...args: any[]): void => {\n if (ended) return;\n ended = true;\n try {\n (target.end as (...a: any[]) => void)(...args);\n } finally {\n try {\n root.end();\n } catch {\n /* ignore */\n }\n }\n };\n }\n const value = Reflect.get(target, prop, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n boundAutoRoots.set(proxy, root);\n aliasMediaCaptureSpan(proxy, child);\n return proxy;\n}\n\n/**\n * Self-root helper for callback/event-driven handlers (LangChain, etc.).\n *\n * A callback handler builds its own parent context per run-id rather than going\n * through {@link AutoRootTracer}. When a run BEGINS with a non-root kind (a bare\n * `llm.invoke()` fires the LLM callback with no chain above it; a standalone\n * tool/retriever similarly), the span it creates is parentless and non-root, so\n * the backend can't anchor the trace and it never renders. Call this when there\n * is no LangChain parent: if the kind is non-root and nothing is actively\n * recording in `baseCtx`, it opens a WORKFLOW root and returns it plus the\n * child context to create the span in. The caller MUST end the returned\n * `root` after the child span ends (see {@link endAutoRoot}).\n *\n * Returns `{ root, ctx }` when a root was opened, or `{ root: undefined, ctx: baseCtx }`\n * when no auto-root is needed (root-eligible kind, already-recording parent, or\n * disabled).\n */\nexport function maybeOpenAutoRoot(\n tracer: Tracer,\n kind: string,\n baseCtx: Context,\n): { root: Span | undefined; ctx: Context } {\n const k = String(kind ?? '').toLowerCase();\n // A foreign provider's span must never count as our parent.\n const existing = getActiveNeatlogsSpan();\n if (\n !autoRootEnabled() ||\n ROOT_KINDS.has(k) ||\n (existing !== undefined && existing.isRecording())\n ) {\n return { root: undefined, ctx: baseCtx };\n }\n const root = tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n baseCtx,\n );\n stampRootIdentity(root);\n return { root, ctx: otelTrace.setSpan(baseCtx, root) };\n}\n\n/** End an auto-root opened by {@link maybeOpenAutoRoot}. Safe on undefined. */\nexport function endAutoRoot(root: Span | undefined): void {\n if (!root) return;\n try {\n root.end();\n } catch {\n /* ignore */\n }\n}\n\nclass AutoRootTracer implements Tracer {\n constructor(private readonly _tracer: Tracer) {}\n\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const kind = String(\n (options?.attributes as Record<string, unknown> | undefined)?.[\n 'neatlogs.span.kind'\n ] ?? '',\n ).toLowerCase();\n\n // Parent from our private context, never the foreign global.\n const hasExplicitContext = context !== undefined;\n const ctx = context ?? getNeatlogsParentContext();\n const parent = getActiveNeatlogsSpan();\n const isParentless =\n !hasExplicitContext && !(parent !== undefined && parent.isRecording());\n const needsRoot =\n autoRootEnabled() &&\n !ROOT_KINDS.has(kind) &&\n isParentless;\n\n if (!needsRoot) {\n const span = this._tracer.startSpan(name, options, ctx);\n if (isParentless && ROOT_KINDS.has(kind)) stampRootIdentity(span);\n return span;\n }\n\n const root = this._tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n ctx,\n );\n stampRootIdentity(root);\n const childCtx = otelTrace.setSpan(ctx, root);\n const child = this._tracer.startSpan(name, options, childCtx);\n return bindSpanToAutoRoot(child, root);\n }\n\n startActiveSpan = ((name: string, ...args: any[]) => {\n const callback = args.at(-1);\n if (typeof callback !== 'function') {\n throw new TypeError('startActiveSpan requires a callback');\n }\n const options = args.length >= 2 ? args[0] : undefined;\n const parentContext =\n args.length >= 3 ? args[1] : getNeatlogsParentContext();\n const span = args.length >= 3\n ? this.startSpan(name, options, parentContext)\n : this.startSpan(name, options);\n return withNeatlogsSpan(\n span,\n () => callback(span),\n parentContext,\n boundAutoRoots.get(span as object),\n );\n }) as Tracer['startActiveSpan'];\n}\n\n/**\n * Tracer for direct-provider wrappers (wrapOpenAI, wrapAnthropic, ...).\n * Identical to `trace.getTracer(name)` but adds transparent auto-root so a\n * bare `wrapOpenAI(new OpenAI())` renders a trace without a manual `span()` /\n * `trace()` wrapper. Do NOT use for framework wrappers.\n */\nexport function getProviderTracer(name: string): Tracer {\n // Resolve from the private provider.\n return new AutoRootTracer(getNeatlogsTracer(name));\n}\n","/**\n * Context wrapper for manual span creation.\n *\n * Provides the `trace()` function — the TypeScript equivalent of the Python\n * `trace()` context manager. Creates an OTel span, sets prompt template/variable\n * context, and executes the user callback within the span.\n *\n * @example\n * ```typescript\n * await trace({ name: 'my-trace' }, async (span) => {\n * // user code runs here with the span active\n * });\n * ```\n */\n\nimport {\n trace as otelTrace,\n context as otelContext,\n createContextKey,\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span,\n} from '@opentelemetry/api';\nimport { PromptContext, UserPromptContext, PromptTemplate, UserPromptTemplate } from '../prompt/template.js';\nimport { registerMask } from './mask.js';\nimport { applyEndUserAttributes } from './end-user.js';\nimport { applySessionAttributes } from './session.js';\nimport { currentSessionId } from './identity.js';\nimport { captureMedia } from './media.js';\nimport { bindSpanToAutoRoot, maybeOpenAutoRoot } from './auto-root.js';\nimport { NeatlogsConfigurationError } from '../errors.js';\n\nimport { getLogger } from './logger.js';\nimport { safeJsonDumps, serializeObj } from '../decorators/base.js';\nimport type { TraceOptions, MaskFunction } from '../types.js';\nimport {\n getActiveNeatlogsSpan,\n getNeatlogsActiveContext,\n getNeatlogsParentContext,\n getNeatlogsRootSpan,\n getNeatlogsTracer,\n withNeatlogsSpan,\n} from './provider.js';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Module-level session config (set by init.ts via _setSessionConfig)\n// ---------------------------------------------------------------------------\n\nlet _sessionConfig: Record<string, any> = {};\n\n/**\n * Set the session configuration. Called by init() during SDK setup.\n * @internal\n */\nexport function _setSessionConfig(config: Record<string, any>): void {\n _sessionConfig = config;\n}\n\n/**\n * Get a copy of the current session configuration.\n */\nexport function getSessionConfig(): Record<string, any> {\n return { ..._sessionConfig };\n}\n\n// ---------------------------------------------------------------------------\n// OTel context keys for prompt data propagation\n// ---------------------------------------------------------------------------\n\nexport const PROMPT_VARIABLES_KEY = createContextKey('neatlogs.prompt_variables');\nexport const PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.prompt_template');\nexport const PROMPT_VERSION_KEY = createContextKey('neatlogs.prompt_version');\nexport const USER_PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.user_prompt_template');\nexport const USER_PROMPT_VARIABLES_KEY = createContextKey('neatlogs.user_prompt_variables');\n\n// ---------------------------------------------------------------------------\n// Known TraceOptions keys (not forwarded as extra span attributes)\n// ---------------------------------------------------------------------------\n\nconst KNOWN_OPTION_KEYS = new Set([\n 'name',\n 'kind',\n 'input',\n 'promptTemplate',\n 'promptVariables',\n 'userPromptTemplate',\n 'userPromptVariables',\n 'version',\n 'mask',\n 'attributes',\n 'sessionId',\n 'parentSessionId',\n 'sessionFeatureName',\n 'sessionEntryPoint',\n 'endUserId',\n 'endUserMetadata',\n]);\n\nfunction assertNoHttpSpanKind(\n kind: unknown,\n attributes: Record<string, any> | undefined,\n): void {\n const candidates = [\n kind,\n attributes?.['neatlogs.span.kind'],\n attributes?.['openinference.span.kind'],\n ];\n if (candidates.some((value) => String(value ?? '').trim().toUpperCase() === 'HTTP')) {\n throw new NeatlogsConfigurationError(\n 'UNSUPPORTED_SPAN_KIND',\n 'kind',\n 'HTTP spans are not supported. Trace the semantic LLM, TOOL, RETRIEVER, or other AI operation instead.',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Set standard and extra attributes on a span.\n * @internal\n */\nexport function _setSpanAttributes(\n span: Span,\n kind: string | undefined,\n attributes: Record<string, any>,\n): void {\n // trace() is also the public API for user-visible WORKFLOW, AGENT, TOOL,\n // EMBEDDING, and other spans. Marking every trace() span as internal causes\n // those spans (and an all-internal trace) to be filtered by ingestion. Only\n // the synthetic LLM wrapper span is internal by default; callers can still\n // opt any other span into internal handling through explicit attributes.\n if (kind?.toUpperCase() === 'LLM') {\n span.setAttribute('neatlogs.internal', true);\n }\n span.setAttribute('openinference.span.kind', kind ?? 'CHAIN');\n\n for (const [key, value] of Object.entries(attributes)) {\n span.setAttribute(key, value);\n }\n}\n\n/**\n * After the user callback completes, capture prompt variables that were\n * set by PromptTemplate.compile() / UserPromptTemplate.compile() and\n * record them as span attributes.\n * @internal\n */\nexport function _finalizePromptCapture(\n span: Span,\n isPromptTemplateObj: boolean,\n isUserPromptTemplateObj: boolean,\n): void {\n if (isPromptTemplateObj) {\n const capturedVars = PromptContext.getVariables();\n if (capturedVars) {\n span.setAttribute(\n 'llm.prompt_template_variables',\n JSON.stringify(capturedVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from PromptContext: ${Object.keys(capturedVars).join(', ')}`,\n );\n }\n }\n\n if (isUserPromptTemplateObj) {\n const capturedUserVars = UserPromptContext.getVariables();\n if (capturedUserVars) {\n span.setAttribute(\n 'llm.user_prompt_template_variables',\n JSON.stringify(capturedUserVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from UserPromptContext: ${Object.keys(capturedUserVars).join(', ')}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Explicit trace-level output\n// ---------------------------------------------------------------------------\n\n/**\n * Explicitly set the trace-level \"final output\" shown for the current trace.\n *\n * By default the trace's displayed output is derived from the root span's\n * captured output. Call this to declare it instead — useful when the turn's\n * meaningful result lives in a tool call or is a status object you don't want\n * shown (e.g. an agent that suspends awaiting user input). The value is stamped\n * as `neatlogs.trace.output` on the trace ROOT span, which the backend prefers\n * over the derived output.\n *\n * No-op when called outside an active trace. Never throws.\n *\n * @param value - The value to show as the trace's final output.\n *\n * @example\n * ```typescript\n * await trace({ name: 'turn', sessionId }, async () => {\n * const plan = await proposePlan();\n * setTraceOutput(plan.title); // show the plan, not the raw status object\n * });\n * ```\n */\nexport function setTraceOutput(value: unknown): void {\n try {\n const span = getNeatlogsRootSpan() ?? getActiveNeatlogsSpan();\n if (!span) return;\n const captured = captureMedia(\n span,\n 'neatlogs.trace.output',\n serializeObj(value),\n 'output',\n );\n span.setAttribute('neatlogs.trace.output', safeJsonDumps(captured));\n } catch (err) {\n logger.warn(`[setTraceOutput] failed to set trace output: ${err}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main trace() function\n// ---------------------------------------------------------------------------\n\n/**\n * Async callback wrapper for manual span creation with prompt tracking.\n *\n * Creates an OTel span, optionally sets prompt template/variable context,\n * and executes the provided callback within the span.\n *\n * **Session-Aware Trace Creation:**\n * - If a session id is resolved (per-call `sessionId` or the request-scoped\n * `identify()` context) AND no active parent span exists, this creates a NEW\n * root trace (for multi-turn conversations).\n * - Otherwise, creates a normal child span within the existing trace.\n *\n * @param options - Trace configuration options\n * @param fn - Callback that receives the active span\n * @returns The return value of the callback\n *\n * @example\n * ```typescript\n * // Basic usage\n * await trace({ name: 'my-pipeline' }, async (span) => {\n * await step1();\n * await step2();\n * });\n *\n * // With prompt template\n * const template = new PromptTemplate('Hello {{name}}');\n * await trace({ name: 'prompt', promptTemplate: template }, async (span) => {\n * const rendered = template.compile({ name: 'world' });\n * // ...\n * });\n * ```\n */\nexport async function trace<T>(\n options: TraceOptions,\n fn: (span: Span) => T | Promise<T>,\n): Promise<T> {\n const {\n name,\n kind,\n input,\n promptTemplate,\n promptVariables,\n userPromptTemplate,\n userPromptVariables,\n version,\n mask,\n sessionId: sessionIdOption,\n parentSessionId,\n sessionFeatureName,\n sessionEntryPoint,\n endUserId,\n endUserMetadata,\n attributes: explicitAttributes,\n ...extraOptions\n } = options;\n\n assertNoHttpSpanKind(kind, explicitAttributes);\n\n // Per-call sessionId wins, else the request-scoped identify() context.\n // Session identity is NOT process-global — init() never sets it.\n const sessionId = sessionIdOption ?? currentSessionId();\n\n // Determine whether we are inside an existing trace. Delayed work can retain\n // an already-ended intermediate span in AsyncLocalStorage. Prefer a live root\n // when one is available; if the root reference is unavailable, preserve the\n // ended parent context so the delayed span keeps the original trace id instead\n // of incorrectly opening a new standalone trace.\n const currentSpan = getActiveNeatlogsSpan();\n const rootSpan = getNeatlogsRootSpan();\n const currentSpanIsRecording = currentSpan?.isRecording() === true;\n const rootSpanIsRecording = rootSpan?.isRecording() === true;\n const shouldFallbackToRoot = !currentSpanIsRecording && rootSpanIsRecording;\n const hasInheritedTraceContext =\n currentSpan !== undefined || rootSpan !== undefined;\n const shouldCreateRootTrace =\n !!sessionId && !hasInheritedTraceContext && !rootSpanIsRecording;\n\n // ---------------------------------------------------------------------------\n // Process prompt templates\n // ---------------------------------------------------------------------------\n\n let templateString: string | undefined;\n let isPromptTemplateObj = false;\n\n if (promptTemplate !== undefined) {\n if (promptTemplate instanceof PromptTemplate) {\n isPromptTemplateObj = true;\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using PromptTemplate object with variables: ${promptTemplate.variables.join(', ')}`,\n );\n } else if (typeof promptTemplate === 'string') {\n templateString = promptTemplate;\n } else {\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n let userTemplateString: string | undefined;\n let isUserPromptTemplateObj = false;\n\n if (userPromptTemplate !== undefined) {\n if (userPromptTemplate instanceof UserPromptTemplate) {\n isUserPromptTemplateObj = true;\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using UserPromptTemplate object with variables: ${userPromptTemplate.variables.join(', ')}`,\n );\n } else if (typeof userPromptTemplate === 'string') {\n userTemplateString = userPromptTemplate;\n } else {\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Build OTel context with prompt values\n // ---------------------------------------------------------------------------\n\n // Start from the Neatlogs active context (private store in isolated mode), NOT\n // otelContext.active() — the latter is the foreign co-tenant's context, whose\n // active span must never become our parent. Prompt values threaded here then\n // propagate to descendant LLM spans through the private store.\n let ctx = getNeatlogsActiveContext();\n if (shouldFallbackToRoot) {\n ctx = otelTrace.setSpan(ctx, rootSpan!);\n }\n\n const variablesJson = promptVariables ? JSON.stringify(promptVariables) : undefined;\n const userVariablesJson = userPromptVariables ? JSON.stringify(userPromptVariables) : undefined;\n\n if (variablesJson) {\n ctx = ctx.setValue(PROMPT_VARIABLES_KEY, variablesJson);\n logger.debug(`[trace] Set neatlogs.prompt_variables in context: ${variablesJson}`);\n }\n if (templateString) {\n ctx = ctx.setValue(PROMPT_TEMPLATE_KEY, templateString);\n logger.debug(`[trace] Set neatlogs.prompt_template in context: ${templateString}`);\n }\n if (userVariablesJson) {\n ctx = ctx.setValue(USER_PROMPT_VARIABLES_KEY, userVariablesJson);\n logger.debug(`[trace] Set neatlogs.user_prompt_variables in context: ${userVariablesJson}`);\n }\n if (userTemplateString) {\n ctx = ctx.setValue(USER_PROMPT_TEMPLATE_KEY, userTemplateString);\n logger.debug(`[trace] Set neatlogs.user_prompt_template in context: ${userTemplateString}`);\n }\n if (version) {\n ctx = ctx.setValue(PROMPT_VERSION_KEY, version);\n logger.debug(`[trace] Set neatlogs.prompt_version in context: ${version}`);\n }\n\n // ---------------------------------------------------------------------------\n // Collect extra attributes (non-standard option keys)\n // ---------------------------------------------------------------------------\n\n const extraAttributes: Record<string, any> = { ...(explicitAttributes ?? {}) };\n for (const [key, value] of Object.entries(extraOptions)) {\n if (!KNOWN_OPTION_KEYS.has(key)) {\n extraAttributes[key] = value;\n }\n }\n\n // End-user belongs to the trace root only. This trace() call is a root when it\n // creates a new root trace, or when it isn't nested in an already-active trace.\n const isRootTrace = shouldCreateRootTrace || !hasInheritedTraceContext;\n\n // ---------------------------------------------------------------------------\n // Create span and execute callback\n // ---------------------------------------------------------------------------\n\n const tracer = getNeatlogsTracer('neatlogs.trace');\n\n const spanCallback = async (span: Span): Promise<T> => {\n _setSpanAttributes(span, kind, extraAttributes);\n\n // Session/end-user belong to the trace root only; skipped on a non-root span.\n const spanIsRoot = isRootTrace && !autoRoot.root;\n applySessionAttributes(span, sessionId, spanIsRoot, {\n parentSessionId,\n sessionFeatureName,\n sessionEntryPoint,\n });\n applyEndUserAttributes(span, endUserId, endUserMetadata, spanIsRoot);\n\n if (input !== undefined && input !== null) {\n const capturedInput = captureMedia(\n span,\n `neatlogs.${String(kind ?? 'workflow').toLowerCase()}.input`,\n serializeObj(input),\n 'input',\n );\n span.setAttribute('input.value', safeJsonDumps(capturedInput));\n }\n\n if (mask) {\n const maskId = registerMask(mask);\n span.setAttribute('neatlogs.mask_id', maskId);\n }\n\n try {\n const result: T = await fn(span);\n\n _finalizePromptCapture(span, isPromptTemplateObj, isUserPromptTemplateObj);\n\n if (result !== undefined && result !== null) {\n const spanAttrs = (span as any).attributes ?? (span as any)._attributes;\n const hasOutput = spanAttrs && (\n 'output.value' in spanAttrs || 'neatlogs.output.value' in spanAttrs\n );\n if (!hasOutput) {\n const capturedOutput = captureMedia(\n span,\n `neatlogs.${String(kind ?? 'workflow').toLowerCase()}.output`,\n serializeObj(result),\n 'output',\n );\n span.setAttribute('output.value', safeJsonDumps(capturedOutput));\n }\n }\n\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n } finally {\n if (isPromptTemplateObj) {\n PromptContext.clear();\n }\n if (isUserPromptTemplateObj) {\n UserPromptContext.clear();\n }\n span.end();\n }\n };\n\n const parentContext = shouldCreateRootTrace\n ? ROOT_CONTEXT\n : getNeatlogsParentContext(ctx);\n // Seed the span kind at CREATION, not just in the callback: the span processor\n // classifies LLM spans in onStart() (which fires from startSpan, before the\n // callback runs). Without this, a trace({ kind: 'LLM', promptTemplate }) span\n // isn't recognized as LLM in time and its prompt-template attributes are dropped.\n const startAttributes = kind ? { 'openinference.span.kind': kind } : undefined;\n const autoRoot = isRootTrace\n ? maybeOpenAutoRoot(tracer, String(kind ?? 'CHAIN'), parentContext)\n : { root: undefined, ctx: parentContext };\n if (autoRoot.root) {\n applySessionAttributes(autoRoot.root, sessionId, true, {\n parentSessionId,\n sessionFeatureName,\n sessionEntryPoint,\n });\n applyEndUserAttributes(autoRoot.root, endUserId, endUserMetadata, true);\n }\n const span = bindSpanToAutoRoot(\n tracer.startSpan(\n name,\n startAttributes ? { attributes: startAttributes } : {},\n autoRoot.ctx,\n ),\n autoRoot.root,\n );\n\n if (shouldCreateRootTrace) {\n logger.debug(`[trace] Creating NEW root trace '${name}' (sessionId=${sessionId})`);\n } else {\n logger.debug(`[trace] Creating child span '${name}'`);\n }\n\n return withNeatlogsSpan(\n span,\n () => spanCallback(span),\n autoRoot.ctx,\n autoRoot.root,\n );\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { PromptMessage } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Async-local context for system prompts\n// ---------------------------------------------------------------------------\n\ninterface PromptContextData {\n template: string;\n variables: Record<string, any>;\n}\n\nconst _promptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages system prompt metadata in async-local context for automatic tracing.\n */\nexport class PromptContext {\n /**\n * Store system prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n // enterWith mutates the current async context in-place\n _promptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve system prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _promptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve system prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _promptStorage.getStore()?.variables;\n }\n\n /**\n * Clear system prompt context.\n */\n static clear(): void {\n _promptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Async-local context for user prompts\n// ---------------------------------------------------------------------------\n\nconst _userPromptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages user/human prompt metadata in async-local context for automatic tracing.\n */\nexport class UserPromptContext {\n /**\n * Store user prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n _userPromptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve user prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _userPromptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve user prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _userPromptStorage.getStore()?.variables;\n }\n\n /**\n * Clear user prompt context.\n */\n static clear(): void {\n _userPromptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared context setter type for the base template class\n// ---------------------------------------------------------------------------\n\ninterface ContextSetter {\n set(template: string, variables: Record<string, any>): void;\n}\n\n// ---------------------------------------------------------------------------\n// BasePromptTemplate — shared logic for system and user prompts\n// ---------------------------------------------------------------------------\n\nconst VARIABLE_PATTERN = /\\{\\{(\\w+)\\}\\}/g;\n\n/**\n * Base class for prompt templates with `{{variable}}` placeholders.\n *\n * Extracts variables, compiles templates, and renders strings.\n * Subclasses only need to specify the context class and display name.\n */\nabstract class BasePromptTemplate {\n protected readonly _template: string | PromptMessage[];\n protected readonly _variables: string[];\n\n /**\n * @param template - Either a string with `{{variable}}` placeholders or\n * an array of `PromptMessage` objects whose `content` fields contain placeholders.\n */\n constructor(template: string | PromptMessage[]) {\n this._template = template;\n this._variables = this._extractVariables();\n }\n\n /** List of unique variable names found in this template. */\n get variables(): string[] {\n return this._variables;\n }\n\n /** The raw template (string or message array). */\n get template(): string | PromptMessage[] {\n return this._template;\n }\n\n /** The context class to store template/variables for automatic tracing. */\n protected abstract get _contextSetter(): ContextSetter;\n\n /** Display name for toString(). */\n protected abstract get _displayName(): string;\n\n /**\n * Compile the prompt template with the given variables.\n *\n * @param variables - Key/value pairs to substitute for `{{key}}` placeholders.\n * @returns The rendered string or rendered message array.\n * @throws {Error} If any required variables are missing.\n */\n compile(variables?: Record<string, any>): string | PromptMessage[] {\n const vars = variables ?? {};\n\n const missing = this._variables.filter((v) => !(v in vars));\n if (missing.length > 0) {\n throw new Error(\n `Missing required variables: ${missing.join(', ')}. Template requires: ${this._variables.join(', ')}`,\n );\n }\n\n this._contextSetter.set(\n typeof this._template === 'string'\n ? this._template\n : this._template.map((msg) => `${msg.role}: ${msg.content}`).join('\\n'),\n vars,\n );\n\n if (typeof this._template === 'string') {\n return this._renderString(this._template, vars);\n }\n\n return this._template.map((msg) => ({\n role: msg.role,\n content: this._renderString(msg.content, vars),\n }));\n }\n\n /**\n * Replace `{{key}}` placeholders in a string with the corresponding values.\n */\n _renderString(text: string, variables: Record<string, any>): string {\n let result = text;\n for (const [key, value] of Object.entries(variables)) {\n result = result.replaceAll(`{{${key}}}`, String(value));\n }\n return result;\n }\n\n toString(): string {\n if (typeof this._template === 'string') {\n return this._template.length > 50\n ? `${this._displayName}('${this._template.slice(0, 50)}...')`\n : `${this._displayName}('${this._template}')`;\n }\n return `${this._displayName}(${this._template.length} messages, variables=${JSON.stringify(this._variables)})`;\n }\n\n // ---- private ----\n\n private _extractVariables(): string[] {\n if (typeof this._template === 'string') {\n return [...new Set(Array.from(this._template.matchAll(VARIABLE_PATTERN), (m) => m[1]))];\n }\n\n const found: string[] = [];\n for (const msg of this._template) {\n if (msg.content) {\n for (const match of msg.content.matchAll(VARIABLE_PATTERN)) {\n found.push(match[1]);\n }\n }\n }\n return [...new Set(found)];\n }\n}\n\n// ---------------------------------------------------------------------------\n// PromptTemplate — system/AI instruction prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the system/AI instruction prompt with `{{variable}}` placeholders.\n */\nexport class PromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return PromptContext;\n }\n\n protected get _displayName(): string {\n return 'PromptTemplate';\n }\n}\n\n// ---------------------------------------------------------------------------\n// UserPromptTemplate — user/human turn prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the user/human turn prompt with `{{variable}}` placeholders.\n *\n * Identical to {@link PromptTemplate} but stores context in {@link UserPromptContext}.\n */\nexport class UserPromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return UserPromptContext;\n }\n\n protected get _displayName(): string {\n return 'UserPromptTemplate';\n }\n}\n","/**\n * Centralized logging configuration for Neatlogs SDK.\n * Provides structured logging with configurable levels.\n */\n\ntype LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n DEBUG: 0,\n INFO: 1,\n WARN: 2,\n ERROR: 3,\n NONE: 4,\n};\n\nlet _logLevel: LogLevel = 'INFO';\nlet _disabled = false;\n\nfunction _getConfiguredLevel(): LogLevel {\n const envLevel = process.env.NEATLOGS_LOG_LEVEL?.toUpperCase();\n if (envLevel && envLevel in LOG_LEVELS) {\n return envLevel as LogLevel;\n }\n return 'INFO';\n}\n\n// Initialize from env on first import\n_logLevel = _getConfiguredLevel();\n\nexport function getLogger() {\n return {\n debug(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.DEBUG) {\n console.debug(`[neatlogs] ${message}`, ...args);\n }\n },\n info(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.INFO) {\n console.info(`[neatlogs] ${message}`, ...args);\n }\n },\n warn(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.WARN) {\n console.warn(`[neatlogs] ${message}`, ...args);\n }\n },\n error(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.ERROR) {\n console.error(`[neatlogs] ${message}`, ...args);\n }\n },\n };\n}\n\nexport function setLogLevel(level: LogLevel): void {\n _logLevel = level;\n}\n\nexport function enableDebugLogging(): void {\n _logLevel = 'DEBUG';\n}\n\nexport function disableLogging(): void {\n _disabled = true;\n}\n\nexport function enableLogging(): void {\n _disabled = false;\n}\n","/**\n * PII masking support for Neatlogs spans.\n *\n * Users supply a callable that receives the full span dict and returns\n * the (possibly modified) span dict. Return null to drop the span entirely.\n */\n\nimport type { MaskFunction } from '../types.js';\nimport { getLogger } from './logger.js';\n\nconst logger = getLogger();\nexport const DEFAULT_MASK_TIMEOUT_MS = 5_000;\n\nexport interface ApplyMaskOptions {\n signalType?: 'span' | 'log';\n timeoutMs?: number;\n}\n\n/** Module-level registry: key -> mask function */\nconst _MASK_REGISTRY = new Map<string, MaskFunction>();\nconst _MASK_RESULTS = new WeakMap<object, Promise<Record<string, any> | null>>();\n\nlet _nextId = 0;\n\n/**\n * Register a mask function and return its lookup key.\n */\nexport function registerMask(fn: MaskFunction): string {\n const key = String(++_nextId);\n _MASK_REGISTRY.set(key, fn);\n return key;\n}\n\n/**\n * Apply the effective mask function to span data.\n *\n * Per-span mask (stored in attributes[\"neatlogs.mask_id\"]) takes\n * precedence over the global mask. Returns the (possibly modified) dict.\n */\nfunction effectiveMask(\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n): MaskFunction | null {\n const maskId = spanData?.attributes?.['neatlogs.mask_id'];\n let maskFn: MaskFunction | undefined | null = null;\n\n if (maskId) {\n maskFn = _MASK_REGISTRY.get(String(maskId)) ?? null;\n }\n\n if (!maskFn) {\n maskFn = globalMask ?? null;\n }\n\n return maskFn;\n}\n\nexport async function applyMask(\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n options: ApplyMaskOptions = {},\n): Promise<Record<string, any> | null> {\n const maskFn = effectiveMask(spanData, globalMask);\n if (!maskFn) return spanData;\n\n const signalType = options.signalType ?? 'span';\n const timeoutMs = options.timeoutMs ?? DEFAULT_MASK_TIMEOUT_MS;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n try {\n // Run user masking outside SpanProcessor.onEnd() so a slow callback does\n // not block application request completion.\n const maskPromise = Promise.resolve().then(() =>\n maskFn(spanData, { signal: controller.signal, signalType, timeoutMs }),\n );\n const timeoutPromise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n controller.abort();\n reject(new Error(`mask callback exceeded ${timeoutMs}ms`));\n }, timeoutMs);\n timer.unref?.();\n });\n const result = await Promise.race([maskPromise, timeoutPromise]);\n // null means \"drop this span entirely\"; undefined means \"keep original\"\n if (result === null) return null;\n return result !== undefined ? result : spanData;\n } catch (exc) {\n const signalName = spanData?.name ?? spanData?.signal_type ?? signalType;\n logger.error(\n `mask callable failed for ${signalType} '${signalName}': ${exc} — dropping it to prevent unmasked export.`,\n );\n return null;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n controller.abort();\n }\n}\n\n/** Schedule and retain one masking result for the exporter that receives span. */\nexport function scheduleMask(\n span: object,\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n): Promise<Record<string, any> | null> {\n const result = applyMask(spanData, globalMask, { signalType: 'span' });\n _MASK_RESULTS.set(span, result);\n return result;\n}\n\n/** Return the result scheduled by NeatlogsSpanProcessor for this snapshot. */\nexport function getScheduledMask(span: object): Promise<Record<string, any> | null> | undefined {\n return _MASK_RESULTS.get(span);\n}\n\n/**\n * Clear all registered masks. Used for testing.\n * @internal\n */\nexport function _clearMaskRegistry(): void {\n _MASK_REGISTRY.clear();\n _nextId = 0;\n}\n","/**\n * Request-scoped session & end-user identity.\n *\n * Session and end-user identity are PER-REQUEST, not process-global, so they are\n * never set on `init()`. The primary way to set them is at the trace root\n * (`trace({ sessionId })` / `span({ sessionId })`). For code that only uses\n * `wrap()` — where the trace root is created internally by the wrapper and the\n * caller has no root to put arguments on — use the `identify()` context:\n *\n * await neatlogs.identify(\n * { sessionId: 'chat_123', endUserId: 'user_456', endUserMetadata: { plan: 'pro' } },\n * async () => {\n * await wrappedClient.chat.completions.create(...); // auto-root reads this\n * },\n * );\n *\n * Resolution at the trace root (per field): explicit per-call argument wins, then\n * the `identify()` context, then nothing. Identity is stamped on the ROOT span\n * only; the backend rolls it up to the trace and its session.\n *\n * Backed by `AsyncLocalStorage` so it propagates across `await` boundaries (and\n * nested async tasks), exactly like OpenTelemetry's own context propagation.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nexport interface IdentityStore {\n sessionId?: string;\n parentSessionId?: string;\n sessionFeatureName?: string;\n sessionEntryPoint?: string;\n endUserId?: string;\n endUserMetadata?: Record<string, any>;\n}\n\nexport interface IdentifyOptions {\n sessionId?: string;\n /** Optional immediate parent session. */\n parentSessionId?: string;\n /** Product feature that initiated this request. */\n sessionFeatureName?: string;\n /** Application entry point that initiated this request. */\n sessionEntryPoint?: string;\n endUserId?: string;\n endUserMetadata?: Record<string, any>;\n}\n\n// tsup bundles each entry point (index, openai, azure-openai, ...) separately,\n// so a plain module-level `new AsyncLocalStorage()` would be DUPLICATED per\n// bundle — identify() (in index) would write to a different store than a\n// provider wrapper's auto-root (in e.g. azure-openai) reads, silently losing\n// identity on the wrapper-only path. Pin a single instance on globalThis so all\n// bundled copies (ESM + CJS) resolve to the same store.\nconst _GLOBAL_KEY = Symbol.for('neatlogs.identity.storage');\ntype _GlobalWithStore = typeof globalThis & {\n [_GLOBAL_KEY]?: AsyncLocalStorage<IdentityStore>;\n};\nconst _g = globalThis as _GlobalWithStore;\nconst _storage: AsyncLocalStorage<IdentityStore> =\n _g[_GLOBAL_KEY] ?? (_g[_GLOBAL_KEY] = new AsyncLocalStorage<IdentityStore>());\n\nexport function currentSessionId(): string | undefined {\n return _storage.getStore()?.sessionId;\n}\n\nexport function currentParentSessionId(): string | undefined {\n return _storage.getStore()?.parentSessionId;\n}\n\nexport function currentSessionFeatureName(): string | undefined {\n return _storage.getStore()?.sessionFeatureName;\n}\n\nexport function currentSessionEntryPoint(): string | undefined {\n return _storage.getStore()?.sessionEntryPoint;\n}\n\nexport function currentEndUserId(): string | undefined {\n return _storage.getStore()?.endUserId;\n}\n\nexport function currentEndUserMetadata(): Record<string, any> | undefined {\n return _storage.getStore()?.endUserMetadata;\n}\n\n/**\n * Bind session / end-user identity for the duration of `fn`.\n *\n * Only defined fields are set, so a nested `identify()` overrides one field\n * without clearing the others (it merges onto the enclosing store). The previous\n * store is automatically restored when `fn` returns / rejects. Returns whatever\n * `fn` returns (sync value or Promise).\n *\n * @example\n * ```typescript\n * await neatlogs.identify({ sessionId: 'chat_123' }, async () => {\n * await wrappedClient.chat.completions.create(...);\n * });\n * ```\n */\nexport function identify<T>(opts: IdentifyOptions, fn: () => T): T {\n const prev = _storage.getStore();\n const next: IdentityStore = { ...(prev ?? {}) };\n if (opts.sessionId !== undefined) next.sessionId = opts.sessionId;\n if (opts.parentSessionId !== undefined) next.parentSessionId = opts.parentSessionId;\n if (opts.sessionFeatureName !== undefined) next.sessionFeatureName = opts.sessionFeatureName;\n if (opts.sessionEntryPoint !== undefined) next.sessionEntryPoint = opts.sessionEntryPoint;\n if (opts.endUserId !== undefined) next.endUserId = opts.endUserId;\n if (opts.endUserMetadata !== undefined) next.endUserMetadata = opts.endUserMetadata;\n return _storage.run(next, fn);\n}\n","/**\n * Neatlogs-owned tracing state.\n *\n * Spans are created by the private Neatlogs provider and their parent is carried\n * in a private context key. The process-global OpenTelemetry span is\n * deliberately left untouched so other observability SDKs cannot export or\n * become parents of Neatlogs spans.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n ROOT_CONTEXT,\n INVALID_SPAN_CONTEXT,\n createContextKey,\n trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\nimport { getActiveClient } from './active-client.js';\n\n// Carries the trace-ROOT span down the private context so descendants can target\n// it (e.g. setTraceOutput). getActiveNeatlogsSpan() returns the innermost span,\n// which is not the root once nested; this key preserves the root reference.\nconst NEATLOGS_ROOT_SPAN_KEY = createContextKey('neatlogs.root_span');\n\n// Entry points are bundled independently (`neatlogs`, `neatlogs/openai`,\n// `neatlogs/ai`, `neatlogs/mastra`, … each in both CJS and ESM), so every piece\n// of shared tracing state — the private span store AND the resolved provider /\n// provider — must live on `globalThis` behind a `Symbol.for` key.\n// Otherwise `init()` (run from the `neatlogs` bundle) sets `_provider` in ITS\n// module copy while a wrapper imported from `neatlogs/openai` reads a different,\n// still-null copy and silently falls back to the foreign global provider.\nconst PRIVATE_SPAN_STORAGE_KEY = Symbol.for(\n 'neatlogs.private_span_async_local_storage',\n);\nconst PRIVATE_PROVIDER_STATE_KEY = Symbol.for(\n 'neatlogs.private_provider_state',\n);\ninterface PrivateProviderState {\n provider: TracerProvider | null;\n}\ntype NeatlogsGlobal = typeof globalThis & {\n [PRIVATE_SPAN_STORAGE_KEY]?: AsyncLocalStorage<Context>;\n [PRIVATE_PROVIDER_STATE_KEY]?: PrivateProviderState;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\n// Stores the full Neatlogs Context (parent span PLUS any threaded values such as\n// trace()'s prompt-template keys), not just the span. We never\n// activate the OTel global context, so this private store is the ONLY channel\n// through which those values propagate down to descendant spans.\nconst privateContextStorage =\n neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] ??\n (neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] = new AsyncLocalStorage<Context>());\nconst providerState: PrivateProviderState =\n neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] ??\n (neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] = {\n provider: null,\n });\n// Wrappers may be constructed or accidentally invoked before init(). They must\n// never fall back to a foreign process-global provider, so pre-init calls use a\n// local no-op tracer and safely emit no exported spans.\nconst preInitTracer: Tracer = {\n startSpan(): Span {\n return otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n _name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n const fn =\n typeof arg2 === 'function'\n ? arg2\n : typeof arg3 === 'function'\n ? arg3\n : arg4;\n return fn!(otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT)) as ReturnType<F>;\n },\n};\n\n/** @internal Configure the provider used by Neatlogs-created spans. */\nexport function _setNeatlogsProvider(provider: TracerProvider | null): void {\n providerState.provider = provider;\n}\n\n/** Resolve a tracer from the private provider when one is configured. */\nexport function getNeatlogsTracer(name: string): Tracer {\n const client = getActiveClient();\n if (client) return client.getTracer(name);\n return providerState.provider?.getTracer(name) ?? preInitTracer;\n}\n\n/** A reusable tracer facade that resolves the active Client on every call. */\nexport function getRoutingNeatlogsTracer(name: string): Tracer {\n return {\n startSpan(spanName: string, options?: SpanOptions, context?: Context): Span {\n return isolateTracer(getNeatlogsTracer(name)).startSpan(\n spanName,\n options,\n context,\n );\n },\n startActiveSpan: ((...args: any[]) =>\n (isolateTracer(getNeatlogsTracer(name)).startActiveSpan as (...inner: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'],\n };\n}\n\n/**\n * @internal The private Neatlogs provider, or null before\n * init(). Used by integrations that must repoint a self-instrumenting library's\n * captured provider onto ours.\n */\nexport function getNeatlogsProvider(): TracerProvider | null {\n const client = getActiveClient();\n if (client) return client.tracerProvider;\n return providerState.provider;\n}\n\n/** Run a Client callback without inheriting another pipeline's private span. */\nexport function runWithFreshNeatlogsContext<T>(fn: () => T): T {\n return privateContextStorage.run(ROOT_CONTEXT, fn);\n}\n\n/**\n * Wrap a tracer so that spans it creates parent from — and, for\n * `startActiveSpan`, activate on — the PRIVATE Neatlogs context instead of the\n * global one.\n *\n * We hand this to libraries that create their own spans off a tracer we give\n * them (the Vercel AI SDK's `experimental_telemetry.tracer`, which calls\n * `tracer.startActiveSpan()` internally). Without the facade the AI SDK's native\n * spans would parent from `context.active()` — the foreign co-tenant's context —\n * and `startActiveSpan` would push them onto the GLOBAL context, so a foreign\n * tracer's next span reads our native span as its parent. Both directions leak.\n *\n */\nexport function isolateTracer(tracer: Tracer): Tracer {\n const facade: Tracer = {\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const parent = context ?? getNeatlogsActiveContext();\n return tracer.startSpan(name, options, parent);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n // Normalize the 2/3/4-arg overloads of startActiveSpan.\n let options: SpanOptions | undefined;\n let context: Context | undefined;\n let fn: F;\n if (typeof arg2 === 'function') {\n fn = arg2 as F;\n } else if (typeof arg3 === 'function') {\n options = arg2 as SpanOptions;\n fn = arg3 as F;\n } else {\n options = arg2 as SpanOptions;\n context = arg3 as Context;\n fn = arg4 as F;\n }\n const parent = context ?? getNeatlogsActiveContext();\n const span = tracer.startSpan(name, options, parent);\n return withNeatlogsSpan(span, () => fn(span), parent) as ReturnType<F>;\n },\n };\n return facade;\n}\n\n/**\n * The base context to build new Neatlogs spans and values on. NEVER contains a\n * foreign provider's span: it reads our private store, or ROOT_CONTEXT when\n * nothing is active.\n */\nexport function getNeatlogsActiveContext(): Context {\n return privateContextStorage.getStore() ?? ROOT_CONTEXT;\n}\n\n/** Return only the active Neatlogs span (never a foreign provider's span). */\nexport function getActiveNeatlogsSpan(): Span | undefined {\n return otelTrace.getSpan(getNeatlogsActiveContext());\n}\n\n/**\n * Return the trace-ROOT Neatlogs span, or undefined when no trace is active.\n *\n * Unlike {@link getActiveNeatlogsSpan} (innermost), this is the outermost span\n * of the current trace — the one the backend derives trace-level output from\n * (`parent_span_id=''`). It is stashed on the private context the first time a\n * span becomes active, so nested calls still resolve to the root.\n */\nexport function getNeatlogsRootSpan(): Span | undefined {\n return getNeatlogsActiveContext().getValue(NEATLOGS_ROOT_SPAN_KEY) as\n | Span\n | undefined;\n}\n\n/**\n * Build a parent context that cannot contain a foreign provider's span.\n *\n * The active Neatlogs context already carries the parent span AND any values\n * the caller threaded in upstream (e.g. `trace()`'s prompt-template values), so\n * those values reach the span processor via `onStart(parentContext)`. An\n * explicit `baseContext` (a caller's own value-carrying context) is honored as-is.\n */\nexport function getNeatlogsParentContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * A base context for callers that thread parent linkage themselves\n * (callback/event handlers that keep their own run-id → span map: LangChain,\n * OpenAI-Agents, Claude Agent SDK).\n *\n * This is the ACTIVE Neatlogs context — the private store's\n * context if a Neatlogs `trace()`/`span()` encloses this call, else\n * ROOT_CONTEXT. So a handler's own root/entry span nests under an enclosing\n * Neatlogs trace (preserving its session + end-user id) when one exists, and\n * auto-roots cleanly when one doesn't. A foreign provider's active span can\n * never leak in as an ancestor because the global context is never read.\n */\nexport function getNeatlogsBaseContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * Build an execution context for a Neatlogs span while preserving the active\n * foreign context. The span rides our private context store.\n */\nexport function getNeatlogsExecutionContext(\n span: Span,\n baseContext: Context = ROOT_CONTEXT,\n): Context {\n return otelTrace.setSpan(baseContext, span);\n}\n\n/**\n * Run a callback with a Neatlogs span active under the appropriate policy.\n *\n * The stored context is `setSpan(base, span)` — carrying the span PLUS whatever\n * values `base` holds — so threaded values (prompt templates)\n * propagate to descendant spans through our private store instead of the global\n * OTel context we deliberately never touch.\n */\nexport function withNeatlogsSpan<T>(\n span: Span,\n fn: () => T,\n baseContext?: Context,\n rootSpan?: Span,\n): T {\n const base = baseContext ?? getNeatlogsActiveContext();\n let ctx = otelTrace.setSpan(base, span);\n // The first span activated in a context with no root recorded IS the root of\n // this trace; remember it so descendants (setTraceOutput) can target it.\n if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === undefined) {\n ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, rootSpan ?? span);\n }\n return privateContextStorage.run(ctx, fn);\n}\n\n/**\n * @internal Run with a private Neatlogs parent without treating that parent as\n * a locally-recording trace root. This is used for an extracted remote parent:\n * the first local recording span should remain the target for local trace-level\n * output, while still inheriting the remote trace/span IDs.\n */\nexport function withNeatlogsRemoteParent<T>(span: Span, fn: () => T): T {\n return privateContextStorage.run(otelTrace.setSpan(ROOT_CONTEXT, span), fn);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Tracer, TracerProvider } from '@opentelemetry/api';\n\nexport interface ActiveNeatlogsClient {\n readonly workflowName: string;\n readonly tracerProvider: TracerProvider;\n getTracer(scope: string): Tracer;\n getLogger(): any | null;\n}\n\nconst ACTIVE_CLIENT_STORAGE_KEY = Symbol.for(\n 'neatlogs.active_client_async_local_storage',\n);\ntype NeatlogsGlobal = typeof globalThis & {\n [ACTIVE_CLIENT_STORAGE_KEY]?: AsyncLocalStorage<ActiveNeatlogsClient>;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\nconst storage =\n neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] ??\n (neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] =\n new AsyncLocalStorage<ActiveNeatlogsClient>());\n\nexport function getActiveClient(): ActiveNeatlogsClient | undefined {\n return storage.getStore();\n}\n\nexport function runWithClient<T>(\n client: ActiveNeatlogsClient,\n fn: () => T,\n): T {\n return storage.run(client, fn);\n}\n","/**\n * End-user identity.\n *\n * The *end user* is the user of OUR CUSTOMER'S application — the person\n * interacting with the AI product our customer built. This is deliberately\n * distinct from the operator-level `userId` set in {@link init}, which identifies\n * whoever is running the SDK (a developer, a service account, a CI job).\n *\n * Model: **one end-user per trace.** Identity is declared once at a trace\n * boundary — `init()` (process-global default), `trace()` (per-request), or\n * `span()` (per root) — via new options on those existing constructs. There is\n * no separate `identify()` call and no nested per-span override: a trace belongs\n * to a single end-user.\n *\n * End-user identity belongs to the trace as a whole, so the SDK only stamps it\n * on the trace's ROOT span (any span kind, created via `trace()` / `span()`). A\n * non-root child span never carries it. The backend reads end-user from the root\n * span and rolls the value up to the trace (and its session) so\n * filtering/analytics are trace- and session-level, not per-span.\n *\n * Canonical span attributes:\n * `neatlogs.end_user.id` — the end-user identifier (string)\n * `neatlogs.end_user.metadata` — JSON object of arbitrary end-user fields\n */\n\nimport { type Span } from '@opentelemetry/api';\nimport { getLogger } from './logger.js';\nimport { currentEndUserId, currentEndUserMetadata } from './identity.js';\nimport { getActiveNeatlogsSpan } from './provider.js';\n\nconst logger = getLogger();\n\nexport const END_USER_ID_KEY = 'neatlogs.end_user.id';\nexport const END_USER_METADATA_KEY = 'neatlogs.end_user.metadata';\n\n/** Coerce end-user metadata to a JSON string. Returns undefined when empty. */\nexport function normalizeEndUserMetadata(\n metadata: string | Record<string, any> | undefined | null,\n): string | undefined {\n if (metadata === undefined || metadata === null) return undefined;\n if (typeof metadata === 'string') return metadata || undefined;\n try {\n return JSON.stringify(metadata);\n } catch {\n return JSON.stringify(String(metadata));\n }\n}\n\n/**\n * True when no recording span is currently active — i.e. a span created now\n * would be the trace root.\n */\nexport function isRootSpan(): boolean {\n // Resolve from the private span store in isolated mode (a foreign provider's\n // active span must not count as our parent), else the active OTel span.\n const current = getActiveNeatlogsSpan();\n return !(current && current.isRecording());\n}\n\n/**\n * Stamp end-user id/metadata onto a ROOT span (best-effort). When `isRoot` is\n * false the span is a child and the value is ignored — the backend reads\n * end-user from the root span and rolls it up to the trace and session.\n */\nexport function applyEndUserAttributes(\n span: Span,\n endUserId?: string,\n endUserMetadata?: string | Record<string, any> | null,\n isRoot = true,\n): void {\n // Resolution at the root (per field): explicit per-call arg wins, else the\n // identify() context (request-scoped). Non-root spans never carry identity.\n const resolvedId = isRoot ? (endUserId ?? currentEndUserId()) : endUserId;\n const resolvedMeta = isRoot\n ? (endUserMetadata ?? currentEndUserMetadata())\n : endUserMetadata;\n if (!resolvedId && !resolvedMeta) return;\n if (!isRoot) {\n logger.debug(\n '[end_user] Ignoring endUserId/endUserMetadata on a non-root span — ' +\n 'declare it on the trace root (top-level trace()/span()) or identify().',\n );\n return;\n }\n if (resolvedId) {\n span.setAttribute(END_USER_ID_KEY, String(resolvedId));\n }\n const metaJson = normalizeEndUserMetadata(resolvedMeta);\n if (metaJson) {\n span.setAttribute(END_USER_METADATA_KEY, metaJson);\n }\n}\n","/**\n * Session identity.\n *\n * A *session* is one conversation / thread / journey — it groups the multiple\n * traces of a multi-turn interaction (one turn = one trace). Identity is\n * declared once at a trace boundary — `init()` (process-global default),\n * `trace()` (per-turn), or `span()` (per root) — via the `sessionId` option on\n * those existing constructs. The SDK stamps only the trace's ROOT span; the\n * backend rolls the value up to group the traces of a session together.\n *\n * For single-turn workflows a session maps 1:1 to a trace: when no session is\n * declared the backend backfills `session_id = trace_id`.\n *\n * Canonical span attribute:\n * `neatlogs.session.id` — the session identifier (string)\n */\n\nimport { type Span } from '@opentelemetry/api';\nimport { getLogger } from './logger.js';\nimport {\n currentParentSessionId,\n currentSessionEntryPoint,\n currentSessionFeatureName,\n currentSessionId,\n} from './identity.js';\n\nconst logger = getLogger();\n\nexport const SESSION_ID_KEY = 'neatlogs.session.id';\nexport const PARENT_SESSION_ID_KEY = 'neatlogs.session.parent_id';\nexport const SESSION_FEATURE_NAME_KEY = 'neatlogs.session.feature.name';\nexport const SESSION_ENTRY_POINT_KEY = 'neatlogs.session.entry_point';\n\nexport interface SessionAttributeOptions {\n parentSessionId?: string;\n sessionFeatureName?: string;\n sessionEntryPoint?: string;\n}\n\nfunction cleanOptionalString(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const cleaned = value.trim();\n return cleaned || undefined;\n}\n\nfunction setAttributeFailOpen(span: Span, key: string, value: string): void {\n try {\n span.setAttribute(key, value);\n } catch (error) {\n logger.debug(`[session] Failed to set ${key}: ${error}`);\n }\n}\n\n/**\n * Stamp the session id onto a ROOT span (best-effort). When `isRoot` is false\n * the span is a child and the value is ignored — the backend reads session from\n * the root span and groups traces by it.\n */\nexport function applySessionAttributes(\n span: Span,\n sessionId?: string,\n isRoot = true,\n options: SessionAttributeOptions = {},\n): void {\n // Resolution at the root: explicit per-call arg wins, else the identify()\n // context (request-scoped). Non-root spans never carry session identity.\n if (!isRoot) {\n if (\n sessionId ||\n options.parentSessionId ||\n options.sessionFeatureName ||\n options.sessionEntryPoint\n ) {\n logger.debug(\n '[session] Ignoring session attributes on a non-root span — declare them on the ' +\n 'trace root (top-level trace()/span()) or identify().',\n );\n }\n return;\n }\n\n const resolvedSessionId = sessionId ?? currentSessionId();\n if (resolvedSessionId) {\n try {\n span.setAttribute(SESSION_ID_KEY, String(resolvedSessionId));\n } catch (error) {\n logger.debug(`[session] Failed to set ${SESSION_ID_KEY}: ${error}`);\n }\n }\n\n const parentSessionId = cleanOptionalString(\n options.parentSessionId ?? currentParentSessionId(),\n );\n const comparableSessionId = cleanOptionalString(resolvedSessionId);\n if (parentSessionId && parentSessionId !== comparableSessionId) {\n setAttributeFailOpen(span, PARENT_SESSION_ID_KEY, parentSessionId);\n }\n\n const sessionFeatureName = cleanOptionalString(\n options.sessionFeatureName ?? currentSessionFeatureName(),\n );\n if (sessionFeatureName) {\n setAttributeFailOpen(span, SESSION_FEATURE_NAME_KEY, sessionFeatureName);\n }\n\n const sessionEntryPoint = cleanOptionalString(\n options.sessionEntryPoint ?? currentSessionEntryPoint(),\n );\n if (sessionEntryPoint) {\n setAttributeFailOpen(span, SESSION_ENTRY_POINT_KEY, sessionEntryPoint);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { getActiveClient } from \"./active-client.js\";\nimport type { DeliveryDiagnostics } from \"./delivery-diagnostics.js\";\nimport {\n assertReadyUploadReceipt,\n DEFAULT_MAX_TYPED_MEDIA_BYTES,\n isSupportedTypedMediaMimeType,\n TelemetryUploadError,\n type UploadAuthority,\n type UploadPayload,\n} from \"./upload-authority.js\";\n\nexport type MediaRecord = Record<string, string | number>;\n\nexport const DEFAULT_INLINE_MEDIA_BYTES = 100_000;\nexport const DEFAULT_MAX_PENDING_MEDIA_ITEMS = 32;\nexport const DEFAULT_MEDIA_EXPORT_DEADLINE_MS = 15_000;\nconst MAX_MEDIA_TRAVERSAL_DEPTH = 32;\nconst MAX_MEDIA_TRAVERSAL_NODES = 4_096;\nconst MAX_MEDIA_REPLACEMENT_KEY_CHARS = 1024 * 1024;\nconst MAX_MEDIA_REFERENCE_CHARS = 8_192;\nconst MAX_CAPTURED_MEDIA_REFERENCES = 64;\nconst MAX_ENCODED_MEDIA_CHARS = Math.ceil((DEFAULT_MAX_TYPED_MEDIA_BYTES * 4) / 3) + 8;\nconst REDACTED_MEDIA_STRUCTURE = \"[REDACTED_MEDIA_STRUCTURE]\";\nconst MEDIA_PAYLOAD_SIGNAL =\n /(?:data\\s*:|\"(?:image_url|input_audio|inline_data|inlineData|file_data|fileData|file_url|file_uri|fileUri|b64_json|mime_type|mimeType|media_type|mediaType)\"\\s*:|\"type\"\\s*:\\s*\"(?:image|audio|video|document|file|input_file|image_generation_call)\")/i;\n\ninterface DiscoveredMedia {\n record: MediaRecord;\n original?: string;\n binaryOriginal?: Uint8Array;\n bytes?: Uint8Array;\n}\n\ninterface MediaDiscoveryResult {\n items: DiscoveredMedia[];\n truncated: boolean;\n}\n\ninterface PendingMedia {\n bytes: Uint8Array;\n prefixes: string[];\n record: MediaRecord;\n token: string;\n sha256: string;\n byteLength: number;\n mimeType: string;\n}\n\ninterface PendingMediaAccount {\n bytes: number;\n items: number;\n released: boolean;\n owner: PendingMediaOwnerState;\n span: WeakRef<object>;\n}\n\ninterface PendingMediaBucket {\n items: PendingMedia[];\n account: PendingMediaAccount;\n unregisterToken: object;\n}\ninterface PendingMediaOwnerState {\n bytes: number;\n items: number;\n accounts: Set<PendingMediaAccount>;\n}\n\ninterface MediaRuntimeState {\n pendingBySpan: WeakMap<object, PendingMediaBucket>;\n pendingMediaOwners: WeakMap<object, PendingMediaOwnerState>;\n mediaCaptureAvailability: WeakMap<object, { available: boolean; reason: string }>;\n mediaCaptureOwners: WeakMap<object, object>;\n spanAliases: WeakMap<object, object>;\n defaultPendingMediaOwner: object;\n}\n\nconst MEDIA_RUNTIME_KEY = Symbol.for(\"neatlogs.media-runtime.v1\");\nconst runtimeGlobal = globalThis as typeof globalThis & Record<symbol, unknown>;\nconst mediaRuntime =\n (runtimeGlobal[MEDIA_RUNTIME_KEY] as MediaRuntimeState | undefined) ??\n ({\n pendingBySpan: new WeakMap(),\n pendingMediaOwners: new WeakMap(),\n mediaCaptureAvailability: new WeakMap(),\n mediaCaptureOwners: new WeakMap(),\n spanAliases: new WeakMap(),\n defaultPendingMediaOwner: {},\n } satisfies MediaRuntimeState);\nruntimeGlobal[MEDIA_RUNTIME_KEY] = mediaRuntime;\n\nconst {\n pendingBySpan,\n pendingMediaOwners,\n mediaCaptureAvailability,\n mediaCaptureOwners,\n spanAliases,\n defaultPendingMediaOwner,\n} = mediaRuntime;\n\nfunction canonicalSpan(span: object): object {\n return spanAliases.get(span) ?? span;\n}\n\n/** Make a transparent span facade share media state with its SDK span. */\nexport function aliasMediaCaptureSpan(alias: object, target: object): void {\n spanAliases.set(alias, canonicalSpan(target));\n}\n\n/** Bind a recording SDK span to the upload gate selected by its pipeline. */\nexport function setMediaCaptureAvailability(\n span: object,\n available: boolean,\n reason = \"telemetry_uploads_disabled\",\n owner?: object,\n): void {\n const target = canonicalSpan(span);\n mediaCaptureAvailability.set(target, {\n available,\n reason: safeUnavailableReason(reason),\n });\n if (owner) mediaCaptureOwners.set(target, owner);\n}\n\nfunction pendingOwnerState(owner: object): PendingMediaOwnerState {\n const prior = pendingMediaOwners.get(owner);\n if (prior) return prior;\n const state = {\n bytes: 0,\n items: 0,\n accounts: new Set<PendingMediaAccount>(),\n };\n pendingMediaOwners.set(owner, state);\n return state;\n}\n\nfunction releasePendingAccount(account: PendingMediaAccount): void {\n if (account.released) return;\n account.released = true;\n account.owner.bytes = Math.max(0, account.owner.bytes - account.bytes);\n account.owner.items = Math.max(0, account.owner.items - account.items);\n account.owner.accounts.delete(account);\n}\n\nconst pendingFinalizer =\n typeof FinalizationRegistry === \"undefined\"\n ? undefined\n : new FinalizationRegistry<PendingMediaAccount>(releasePendingAccount);\n\nfunction pendingBucket(span: object): PendingMediaBucket {\n span = canonicalSpan(span);\n const prior = pendingBySpan.get(span);\n if (prior) return prior;\n const owner = pendingOwnerState(\n mediaCaptureOwners.get(span) ?? getActiveClient() ?? defaultPendingMediaOwner,\n );\n const account: PendingMediaAccount = {\n bytes: 0,\n items: 0,\n released: false,\n owner,\n span: new WeakRef(span),\n };\n const unregisterToken = {};\n const bucket = { items: [], account, unregisterToken };\n owner.accounts.add(account);\n pendingBySpan.set(span, bucket);\n pendingFinalizer?.register(span, account, unregisterToken);\n return bucket;\n}\n\nfunction detachPendingBucket(span: object): PendingMediaBucket | undefined {\n const alias = span;\n span = canonicalSpan(alias);\n spanAliases.delete(alias);\n const bucket = pendingBySpan.get(span);\n if (!bucket) return undefined;\n pendingBySpan.delete(span);\n mediaCaptureAvailability.delete(span);\n mediaCaptureOwners.delete(span);\n pendingFinalizer?.unregister(bucket.unregisterToken);\n return bucket;\n}\n\nfunction uploadToken(record: MediaRecord): string {\n const material = `${record.sha256}:${record.mime_type}:${record.purpose}`;\n return `nl_pending_media_${createHash(\"sha256\").update(material).digest(\"hex\").slice(0, 24)}`;\n}\n\nfunction mediaKind(mimeType: string, declared = \"\"): string {\n const value = declared\n .toLowerCase()\n .replace(/^(?:input|output)_/, \"\")\n .replace(/_url$/, \"\");\n if (value === \"image_generation_call\") return \"image\";\n if (value === \"file\") return \"document\";\n if ([\"image\", \"audio\", \"video\", \"document\"].includes(value)) return value;\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n if (mimeType.startsWith(\"video/\")) return \"video\";\n if (mimeType === \"application/pdf\" || mimeType.startsWith(\"text/\")) {\n return \"document\";\n }\n return \"media\";\n}\n\nfunction declaredMediaKind(value: string): string {\n const normalized = mediaKind(\"\", value);\n return normalized === \"media\" ? \"\" : normalized;\n}\n\nfunction canonicalMimeType(value: string): string {\n const mimeType = value.trim().split(\";\", 1)[0]?.toLowerCase() ?? \"\";\n return mimeType === \"image/jpg\"\n ? \"image/jpeg\"\n : mimeType === \"audio/mp3\"\n ? \"audio/mpeg\"\n : mimeType;\n}\n\ninterface DecodedBase64 {\n bytes?: Uint8Array;\n byteLength: number;\n sha256: string;\n}\n\nfunction rejectedMediaRecord(\n original: string,\n mimeType: string,\n declared: string,\n purpose: string,\n reason: string,\n source: \"inline\" | \"url\",\n): DiscoveredMedia {\n const fingerprint = createHash(\"sha256\")\n .update(original.slice(0, 4096))\n .update(String(original.length))\n .digest(\"hex\");\n return {\n original,\n record: {\n id: `nl_media_rejected_${fingerprint.slice(0, 24)}`,\n type: mediaKind(mimeType, declared),\n source,\n mime_type: mimeType || \"application/octet-stream\",\n purpose,\n state: \"failed\",\n safe_preview: `upload failed: validate/${reason}`,\n },\n };\n}\n\nfunction decodeBase64(value: string, retainBytes = true): DecodedBase64 | null {\n const normalized = value.replace(/\\s+/g, \"\");\n if (!/^[A-Za-z0-9+/]*={0,2}$/.test(normalized) || normalized.length % 4 === 1) {\n return null;\n }\n try {\n const padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n const estimatedBytes = Math.floor((normalized.length * 3) / 4) - padding;\n if (retainBytes && estimatedBytes <= DEFAULT_MAX_TYPED_MEDIA_BYTES) {\n const bytes = Buffer.from(normalized, \"base64\");\n if (bytes.toString(\"base64\").replace(/=+$/, \"\") !== normalized.replace(/=+$/, \"\")) {\n return null;\n }\n return {\n bytes,\n byteLength: bytes.byteLength,\n sha256: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n };\n }\n const digest = createHash(\"sha256\");\n let byteLength = 0;\n const chunkCharacters = 64 * 1024;\n for (let offset = 0; offset < normalized.length; offset += chunkCharacters) {\n const chunk = Buffer.from(normalized.slice(offset, offset + chunkCharacters), \"base64\");\n digest.update(chunk);\n byteLength += chunk.byteLength;\n }\n if (byteLength !== estimatedBytes) return null;\n return { byteLength, sha256: digest.digest(\"hex\") };\n } catch {\n return null;\n }\n}\n\nfunction inlineRecord(\n input: string | Uint8Array,\n mimeType: string,\n declared: string,\n purpose: string,\n retainBytes = true,\n): DiscoveredMedia | null {\n mimeType = canonicalMimeType(mimeType);\n let original: string | undefined;\n let binaryOriginal: Uint8Array | undefined;\n let bytes: Uint8Array | undefined;\n let byteLength: number;\n let sha256: string;\n if (typeof input === \"string\") {\n original = input;\n let encoded = input.trim();\n if (/^data:/i.test(encoded)) {\n const comma = encoded.indexOf(\",\");\n if (comma < 0) return null;\n const header = encoded.slice(5, comma);\n if (!header.toLowerCase().split(\";\").includes(\"base64\")) return null;\n mimeType = canonicalMimeType(header.split(\";\", 1)[0] || mimeType);\n encoded = encoded.slice(comma + 1);\n }\n if (encoded.length > MAX_ENCODED_MEDIA_CHARS) {\n return rejectedMediaRecord(\n original,\n mimeType,\n declared,\n purpose,\n \"payload_too_large\",\n \"inline\",\n );\n }\n const decoded = decodeBase64(encoded, retainBytes);\n if (!decoded) {\n return rejectedMediaRecord(\n original,\n mimeType,\n declared,\n purpose,\n \"invalid_inline_media\",\n \"inline\",\n );\n }\n bytes = decoded.bytes;\n byteLength = decoded.byteLength;\n sha256 = decoded.sha256;\n } else {\n binaryOriginal = input;\n byteLength = input.byteLength;\n sha256 = createHash(\"sha256\").update(input).digest(\"hex\");\n if (retainBytes && byteLength <= DEFAULT_MAX_TYPED_MEDIA_BYTES) {\n bytes = Uint8Array.from(input);\n }\n }\n const eligible =\n byteLength > DEFAULT_INLINE_MEDIA_BYTES && byteLength <= DEFAULT_MAX_TYPED_MEDIA_BYTES;\n const tooLarge = byteLength > DEFAULT_MAX_TYPED_MEDIA_BYTES;\n return {\n original,\n binaryOriginal,\n bytes,\n record: {\n id: `nl_media_${sha256.slice(0, 24)}`,\n type: mediaKind(mimeType, declared),\n source: \"inline\",\n mime_type: mimeType || \"application/octet-stream\",\n byte_length: byteLength,\n sha256,\n purpose,\n state: tooLarge ? \"failed\" : eligible ? \"pending-upload\" : \"inline\",\n ...(tooLarge\n ? { safe_preview: \"upload failed: validate/payload_too_large\" }\n : eligible\n ? { safe_preview: \"awaiting authenticated upload\" }\n : {}),\n },\n };\n}\n\nfunction referenceRecord(\n reference: string,\n mimeType: string,\n declared: string,\n purpose: string,\n): DiscoveredMedia | null {\n mimeType = canonicalMimeType(mimeType);\n if (reference.length > MAX_MEDIA_REFERENCE_CHARS) {\n return rejectedMediaRecord(\n reference,\n mimeType,\n declared,\n purpose,\n \"media_reference_too_large\",\n \"url\",\n );\n }\n let safeReference = reference;\n let isURLReference = false;\n const normalizedReference = reference.trim();\n const protocolRelative = normalizedReference.startsWith(\"//\");\n const relativeReference =\n !protocolRelative &&\n !/^[a-z][a-z\\d+.-]*:/i.test(normalizedReference) &&\n (normalizedReference.includes(\"/\") ||\n normalizedReference.includes(\"?\") ||\n normalizedReference.includes(\"#\"));\n try {\n const parsed = protocolRelative || relativeReference\n ? new URL(normalizedReference, \"https://neatlogs.invalid\")\n : new URL(normalizedReference);\n if (parsed.protocol.toLowerCase() === \"data:\") {\n return rejectedMediaRecord(\n reference,\n mimeType,\n declared,\n purpose,\n \"invalid_inline_media\",\n \"inline\",\n );\n }\n const hierarchical =\n protocolRelative ||\n relativeReference ||\n parsed.protocol === \"http:\" ||\n parsed.protocol === \"https:\" ||\n /^[a-z][a-z\\d+.-]*:\\/\\//i.test(normalizedReference) ||\n parsed.pathname.startsWith(\"/\");\n if (hierarchical) {\n isURLReference = true;\n parsed.username = \"\";\n parsed.password = \"\";\n parsed.search = \"\";\n parsed.hash = \"\";\n safeReference = protocolRelative\n ? `//${parsed.host}${parsed.pathname}`\n : relativeReference\n ? parsed.pathname\n : parsed.toString();\n } else if (parsed.protocol) {\n isURLReference = true;\n safeReference = parsed.protocol;\n }\n } catch {\n if (/^(?:\\/\\/|[a-z][a-z\\d+.-]*:)/i.test(normalizedReference)) {\n return rejectedMediaRecord(\n reference,\n mimeType,\n declared,\n purpose,\n \"invalid_media_reference\",\n \"url\",\n );\n }\n }\n const digest = createHash(\"sha256\").update(reference).digest(\"hex\");\n return {\n original: reference,\n record: {\n id: `nl_media_${digest.slice(0, 24)}`,\n type: mediaKind(mimeType, declared),\n source: isURLReference ? \"url\" : \"provider\",\n mime_type: mimeType || \"application/octet-stream\",\n reference: safeReference,\n purpose,\n state: \"available\",\n },\n };\n}\n\nfunction discoverMedia(\n value: unknown,\n purpose: string,\n retainBytes = true,\n): MediaDiscoveryResult {\n const found: DiscoveredMedia[] = [];\n const discoveredKeys = new Set<string>();\n let retainedBytes = 0;\n let truncated = false;\n const append = (item: DiscoveredMedia | null): void => {\n if (!item) return;\n if (found.length >= MAX_CAPTURED_MEDIA_REFERENCES) {\n truncated = true;\n return;\n }\n const key = `${item.record.id}:${item.record.mime_type}:${item.record.reference ?? \"\"}:${item.record.type}`;\n if (discoveredKeys.has(key)) return;\n discoveredKeys.add(key);\n if (item.bytes && item.record.state === \"pending-upload\") {\n if (retainedBytes + item.bytes.byteLength > DEFAULT_MAX_TYPED_MEDIA_BYTES) {\n item.bytes = undefined;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/pending_media_memory_limit\";\n } else {\n retainedBytes += item.bytes.byteLength;\n }\n }\n found.push(item);\n };\n const visited = new WeakSet<object>();\n let visitedNodes = 0;\n const visit = (node: any, inheritedDeclared = \"\", inheritedMimeType = \"\", depth = 0): void => {\n if (typeof node === \"string\" && /^\\s*data:/i.test(node)) {\n append(inlineRecord(node, inheritedMimeType, inheritedDeclared, purpose, retainBytes));\n return;\n }\n if (!node || typeof node !== \"object\") return;\n if (found.length >= MAX_CAPTURED_MEDIA_REFERENCES) {\n truncated = true;\n return;\n }\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return;\n }\n if (visited.has(node)) return;\n visited.add(node);\n visitedNodes += 1;\n try {\n if (node instanceof Uint8Array) return;\n if (Array.isArray(node)) {\n for (const item of node) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) break;\n if (inheritedDeclared && (typeof item === \"string\" || item instanceof Uint8Array)) {\n const record =\n typeof item === \"string\" && /^(?:https?:|s3:|gs:|\\/\\/)/i.test(item)\n ? referenceRecord(item, inheritedMimeType, inheritedDeclared, purpose)\n : inlineRecord(item, inheritedMimeType, inheritedDeclared, purpose, retainBytes);\n append(record);\n } else {\n visit(item, inheritedDeclared, inheritedMimeType, depth + 1);\n }\n }\n return;\n }\n const entries: [string, unknown][] = Object.entries(node);\n const read = (key: string): unknown => entries.find(([name]) => name === key)?.[1];\n const declared = String(read(\"type\") ?? inheritedDeclared);\n const normalizedDeclared = declaredMediaKind(declared);\n const mediaDeclared = normalizedDeclared || declaredMediaKind(inheritedDeclared);\n let mimeType = canonicalMimeType(\n String(\n read(\"mime_type\") ??\n read(\"mimeType\") ??\n read(\"media_type\") ??\n read(\"mediaType\") ??\n inheritedMimeType,\n ),\n );\n const declaredFormat = read(\"format\") ?? read(\"output_format\") ?? read(\"outputFormat\");\n if (!mimeType && mediaDeclared && typeof declaredFormat === \"string\") {\n const format = declaredFormat.toLowerCase();\n mimeType = canonicalMimeType(\n mediaDeclared === \"document\" && format === \"pdf\"\n ? \"application/pdf\"\n : `${mediaDeclared}/${format}`,\n );\n }\n const imageUrl = read(\"image_url\");\n let image = imageUrl;\n if (imageUrl && typeof imageUrl === \"object\") {\n try {\n image = (imageUrl as Record<string, unknown>).url;\n } catch {\n image = undefined;\n }\n }\n if (typeof image === \"string\") {\n const record = /^\\s*data:/i.test(image)\n ? inlineRecord(image, mimeType, \"image\", purpose, retainBytes)\n : referenceRecord(image, mimeType, \"image\", purpose);\n append(record);\n }\n const audio = read(\"input_audio\") ?? read(\"inputAudio\");\n if (audio && typeof audio === \"object\" && typeof (audio as any).data === \"string\") {\n const format = String((audio as any).format ?? \"unknown\");\n const record = inlineRecord(\n (audio as any).data,\n mimeType || `audio/${format}`,\n \"audio\",\n purpose,\n retainBytes,\n );\n append(record);\n }\n const inline = read(\"inline_data\") ?? read(\"inlineData\");\n if (inline && typeof inline === \"object\" && typeof (inline as any).data === \"string\") {\n const record = inlineRecord(\n (inline as any).data,\n String((inline as any).mime_type ?? (inline as any).mimeType ?? mimeType),\n mediaDeclared,\n purpose,\n retainBytes,\n );\n append(record);\n }\n const fileData = read(\"file_data\") ?? read(\"fileData\");\n if (typeof fileData === \"string\") {\n const record = inlineRecord(\n fileData,\n mimeType,\n mediaDeclared || \"document\",\n purpose,\n retainBytes,\n );\n append(record);\n }\n const raw =\n read(\"data\") ??\n read(\"bytes\") ??\n read(\"b64_json\") ??\n (normalizedDeclared === \"image\" ? read(\"result\") : undefined);\n if ((typeof raw === \"string\" || raw instanceof Uint8Array) && (mediaDeclared || mimeType)) {\n const record = inlineRecord(raw, mimeType, mediaDeclared, purpose, retainBytes);\n append(record);\n }\n const reference =\n read(\"file_id\") ?? read(\"file_url\") ?? read(\"file_uri\") ?? read(\"fileUri\") ?? read(\"url\");\n if (\n typeof reference === \"string\" &&\n ([\"file\", \"url\"].includes(normalizedDeclared) || !!mediaDeclared || !!mimeType)\n ) {\n const record = /^\\s*data:/i.test(reference)\n ? inlineRecord(reference, mimeType, mediaDeclared || \"document\", purpose, retainBytes)\n : referenceRecord(reference, mimeType, mediaDeclared || \"document\", purpose);\n append(record);\n }\n for (const [key, item] of entries) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) break;\n const keyed = key\n .toLowerCase()\n .replace(/^input_/, \"\")\n .replace(/s$/, \"\");\n const childDeclared = declaredMediaKind(keyed) || mediaDeclared;\n let childMimeType = mimeType;\n if (!childMimeType && childDeclared && typeof declaredFormat === \"string\") {\n const format = declaredFormat.toLowerCase();\n childMimeType = canonicalMimeType(\n childDeclared === \"document\" && format === \"pdf\"\n ? \"application/pdf\"\n : `${childDeclared}/${format}`,\n );\n }\n visit(item, childDeclared, childMimeType, depth + 1);\n }\n } catch {\n return;\n }\n };\n visit(value);\n return { items: found, truncated };\n}\n\nfunction placeholder(record: MediaRecord): Record<string, unknown> {\n return {\n neatlogs_media: Object.fromEntries(\n Object.entries(record).filter(([key]) =>\n [\n \"id\",\n \"type\",\n \"source\",\n \"mime_type\",\n \"byte_length\",\n \"sha256\",\n \"purpose\",\n \"state\",\n \"reference\",\n \"content_encoding\",\n \"safe_preview\",\n \"upload_token\",\n ].includes(key),\n ),\n ),\n };\n}\n\nfunction sanitizedCopy(value: unknown, discovered: DiscoveredMedia[]): unknown {\n const replacements = new Map<string, unknown>();\n const largeReplacements: Array<[string, unknown]> = [];\n for (const item of discovered) {\n if (!item.original) continue;\n if (item.record.state === \"pending-upload\" || item.record.state === \"failed\") {\n const replacement = placeholder(item.record);\n if (item.original.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n largeReplacements.push([item.original, replacement]);\n } else {\n replacements.set(item.original, replacement);\n }\n } else if (typeof item.record.reference === \"string\") {\n if (item.original.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n largeReplacements.push([item.original, item.record.reference]);\n } else {\n replacements.set(item.original, item.record.reference);\n }\n }\n }\n const visited = new WeakMap<object, unknown>();\n let visitedNodes = 0;\n const clone = (node: any, depth = 0): any => {\n if (typeof node === \"string\") {\n const direct =\n node.length <= MAX_MEDIA_REPLACEMENT_KEY_CHARS ? replacements.get(node) : undefined;\n if (direct !== undefined) return direct;\n return largeReplacements.find(([original]) => original === node)?.[1] ?? node;\n }\n if (node instanceof Uint8Array) {\n const match = discovered.find(\n (item) =>\n item.binaryOriginal === node ||\n (item.bytes?.buffer === node.buffer && item.bytes.byteOffset === node.byteOffset),\n );\n return match && [\"pending-upload\", \"failed\"].includes(String(match.record.state))\n ? placeholder(match.record)\n : node;\n }\n if (!node || typeof node !== \"object\") return node;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const prior = visited.get(node);\n if (prior) return \"[CIRCULAR]\";\n visitedNodes += 1;\n if (Array.isArray(node)) {\n const result: unknown[] = [];\n visited.set(node, result);\n for (const item of node) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n result.push(REDACTED_MEDIA_STRUCTURE);\n break;\n }\n result.push(clone(item, depth + 1));\n }\n return result;\n }\n const result: Record<string, unknown> = {};\n visited.set(node, result);\n try {\n for (const [key, item] of Object.entries(node)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n result.neatlogs_truncated = REDACTED_MEDIA_STRUCTURE;\n break;\n }\n result[key] = clone(item, depth + 1);\n }\n } catch {\n return REDACTED_MEDIA_STRUCTURE;\n }\n return result;\n };\n return clone(value);\n}\n\nexport function mediaReferences(value: unknown, purpose: string): MediaRecord[] {\n return discoverMedia(value, purpose, false).items.map(({ record }) =>\n record.state === \"pending-upload\"\n ? {\n ...record,\n state: \"failed\",\n safe_preview: \"upload unavailable: telemetry_uploads_disabled\",\n }\n : { ...record },\n );\n}\n\n/** Remove large inline bytes and URL credentials from a captured value. */\nexport function sanitizeMediaPayload(value: unknown, purpose = \"capture\"): unknown {\n try {\n const discovered = discoverMedia(value, purpose, false);\n return discovered.truncated\n ? REDACTED_MEDIA_STRUCTURE\n : sanitizedCopy(value, discovered.items);\n } catch {\n return REDACTED_MEDIA_STRUCTURE;\n }\n}\n\nexport interface CapturedMediaValue {\n value: unknown;\n count: number;\n}\n\n/** Capture typed media metadata from a stable index and return a telemetry-safe clone. */\nexport function captureMediaWithIndex(\n span: {\n setAttribute(name: string, value: string | number): unknown;\n isRecording?(): boolean;\n },\n prefix: string,\n value: unknown,\n purpose: string,\n mediaIndexOffset = 0,\n): CapturedMediaValue {\n let recording = false;\n try {\n recording = typeof span.isRecording !== \"function\" || span.isRecording();\n } catch {\n span.setAttribute(`${prefix}.media.capture_error`, \"media_capture_failed\");\n return { value: REDACTED_MEDIA_STRUCTURE, count: 0 };\n }\n const spanKey = canonicalSpan(span as object);\n const gate = mediaCaptureAvailability.get(spanKey);\n const uploadsAvailable = gate?.available ?? true;\n let discovery: MediaDiscoveryResult;\n try {\n discovery = discoverMedia(value, purpose, recording && uploadsAvailable);\n } catch {\n span.setAttribute(`${prefix}.media.capture_error`, \"media_capture_failed\");\n return { value: REDACTED_MEDIA_STRUCTURE, count: 0 };\n }\n discovery.items.forEach((item, index) => {\n const recordPrefix = `${prefix}.media.${mediaIndexOffset + index}`;\n if (item.record.state === \"pending-upload\") {\n if (!recording) {\n item.bytes = undefined;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload unavailable: span_not_recording\";\n } else if (!uploadsAvailable) {\n item.bytes = undefined;\n item.record.state = \"failed\";\n item.record.safe_preview = `upload unavailable: ${gate?.reason ?? \"telemetry_uploads_disabled\"}`;\n } else if (!item.bytes) {\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/staged_payload_missing\";\n } else {\n const token = uploadToken(item.record);\n item.record.upload_token = token;\n const bucket = pendingBucket(spanKey);\n const duplicate = bucket.items.find(\n (candidate) =>\n candidate.sha256 === item.record.sha256 && candidate.mimeType === item.record.mime_type,\n );\n if (duplicate) {\n if (!duplicate.prefixes.includes(recordPrefix)) {\n duplicate.prefixes.push(recordPrefix);\n }\n } else if (bucket.account.owner.items >= DEFAULT_MAX_PENDING_MEDIA_ITEMS) {\n item.bytes = undefined;\n delete item.record.upload_token;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/pending_media_item_limit\";\n } else if (\n bucket.account.owner.bytes + item.bytes.byteLength >\n DEFAULT_MAX_TYPED_MEDIA_BYTES\n ) {\n item.bytes = undefined;\n delete item.record.upload_token;\n item.record.state = \"failed\";\n item.record.safe_preview = \"upload failed: validate/pending_media_memory_limit\";\n } else {\n bucket.account.owner.bytes += item.bytes.byteLength;\n bucket.account.owner.items += 1;\n bucket.account.bytes += item.bytes.byteLength;\n bucket.account.items += 1;\n bucket.items.push({\n bytes: item.bytes,\n prefixes: [recordPrefix],\n record: { ...item.record },\n token,\n sha256: String(item.record.sha256),\n byteLength: Number(item.record.byte_length),\n mimeType: String(item.record.mime_type),\n });\n }\n if (bucket.items.length === 0) {\n pendingBySpan.delete(span as object);\n pendingFinalizer?.unregister(bucket.unregisterToken);\n releasePendingAccount(bucket.account);\n }\n }\n }\n Object.entries(item.record).forEach(([key, field]) =>\n span.setAttribute(`${recordPrefix}.${key}`, field),\n );\n });\n return {\n value: discovery.truncated\n ? REDACTED_MEDIA_STRUCTURE\n : sanitizedCopy(value, discovery.items),\n count: discovery.items.length,\n };\n}\n\n/** Capture typed media metadata and return a telemetry-safe clone of the value. */\nexport function captureMedia(\n span: {\n setAttribute(name: string, value: string | number): unknown;\n isRecording?(): boolean;\n },\n prefix: string,\n value: unknown,\n purpose: string,\n): unknown {\n return captureMediaWithIndex(span, prefix, value, purpose).value;\n}\n\nexport function setMediaAttributes(\n span: { setAttribute(name: string, value: string | number): unknown },\n prefix: string,\n value: unknown,\n purpose: string,\n): void {\n captureMedia(span, prefix, value, purpose);\n}\n\n/** Sanitize media that reached a completed span through a generic integration. */\nexport function captureMediaInSnapshot(\n span: object,\n snapshot: Record<string, any>,\n uploadsAvailable: boolean,\n unavailableReason = \"telemetry_uploads_disabled\",\n): void {\n setMediaCaptureAvailability(span, uploadsAvailable, unavailableReason);\n const spanAttributes =\n snapshot.attributes && typeof snapshot.attributes === \"object\"\n ? snapshot.attributes\n : (snapshot.attributes = {});\n const setter = {\n isRecording: () => true,\n setAttribute(name: string, value: string | number) {\n spanAttributes[name] = value;\n },\n };\n aliasMediaCaptureSpan(setter, span);\n let mediaIndex = 0;\n\n const sanitizeAttributes = (attributes: unknown): void => {\n if (!attributes || typeof attributes !== \"object\" || Array.isArray(attributes)) return;\n for (const [key, original] of Object.entries(attributes)) {\n let value: unknown = original;\n let parsedJson = false;\n if (typeof original === \"string\") {\n if (!MEDIA_PAYLOAD_SIGNAL.test(original)) continue;\n const trimmed = original.trimStart();\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n value = JSON.parse(original);\n parsedJson = true;\n } catch {\n if (/data\\s*:\\s*(?:image|audio|video|application\\/pdf)/i.test(original)) {\n (attributes as Record<string, unknown>)[key] = REDACTED_MEDIA_STRUCTURE;\n }\n continue;\n }\n }\n }\n\n const purpose = key.includes(\"output\") ? \"output\" : \"input\";\n let safeValue: unknown;\n let count = 0;\n const sanitizeOnly = mediaIndex >= MAX_CAPTURED_MEDIA_REFERENCES;\n if (sanitizeOnly) {\n safeValue = sanitizeMediaPayload(value, purpose);\n } else {\n const captured = captureMediaWithIndex(\n setter,\n \"neatlogs.content\",\n value,\n purpose,\n mediaIndex,\n );\n safeValue = captured.value;\n count = captured.count;\n }\n mediaIndex += count;\n if (count === 0 && !sanitizeOnly) continue;\n const replacement =\n typeof original === \"string\" && (parsedJson || typeof safeValue !== \"string\")\n ? JSON.stringify(safeValue)\n : safeValue;\n if (replacement === original) continue;\n (attributes as Record<string, unknown>)[key] = replacement;\n }\n };\n\n sanitizeAttributes(spanAttributes);\n sanitizeAttributes(snapshot.resource?.attributes ?? snapshot.resource);\n if (Array.isArray(snapshot.events)) {\n for (const event of snapshot.events) sanitizeAttributes(event?.attributes);\n }\n if (Array.isArray(snapshot.links)) {\n for (const link of snapshot.links) sanitizeAttributes(link?.attributes);\n }\n}\n\nfunction safeFailure(error: unknown): { stage: string; reason: string } {\n if (!(error instanceof TelemetryUploadError)) {\n return { stage: \"upload\", reason: \"unexpected_error\" };\n }\n return {\n stage: error.stage,\n reason: /^[a-zA-Z0-9_.-]{1,64}$/.test(error.reasonCode)\n ? error.reasonCode\n : \"unsafe_reason_code\",\n };\n}\n\nfunction safeUnavailableReason(value: string): string {\n return /^[a-zA-Z0-9_.-]{1,64}$/.test(value) ? value : \"upload_authority_unavailable\";\n}\n\nfunction rewritePendingPlaceholder(\n attributes: Record<string, any>,\n item: PendingMedia,\n update: MediaRecord,\n): void {\n const matches = (value: Record<string, any>): boolean =>\n value.upload_token === item.token ||\n (value.id === item.record.id && value.sha256 === item.sha256);\n const applyUpdate = (value: Record<string, any>): Record<string, any> => {\n const after = { ...value, ...update };\n delete after.upload_token;\n if (after.state === \"available\") delete after.safe_preview;\n return after;\n };\n const seen = new WeakSet<object>();\n let visitedNodes = 0;\n const rewrite = (value: any, depth = 0): any => {\n if (typeof value === \"string\") {\n if (!value.includes(item.token) && !value.includes(String(item.record.id))) {\n return value;\n }\n if (value.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const trimmed = value.trimStart();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return value;\n try {\n const parsed = JSON.parse(value);\n const rewritten = rewrite(parsed);\n return rewritten === parsed ? value : JSON.stringify(rewritten);\n } catch {\n return value;\n }\n }\n if (!value || typeof value !== \"object\") return value;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n if (seen.has(value)) return \"[CIRCULAR]\";\n seen.add(value);\n visitedNodes += 1;\n if (!Array.isArray(value) && matches(value)) return applyUpdate(value);\n let changed = false;\n const output = Array.isArray(value) ? [...value] : { ...value };\n for (const [key, child] of Object.entries(value)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const rewritten = rewrite(child, depth + 1);\n if (rewritten !== child) {\n (output as Record<string, any>)[key] = rewritten;\n changed = true;\n }\n }\n if (!Array.isArray(value)) {\n for (const prefix of item.prefixes) {\n const tokenKey = `${prefix}.upload_token`;\n const matchesFlat =\n value[tokenKey] === item.token ||\n (value[`${prefix}.id`] === item.record.id && value[`${prefix}.sha256`] === item.sha256);\n if (!matchesFlat) continue;\n delete (output as Record<string, any>)[tokenKey];\n for (const [field, fieldValue] of Object.entries(update)) {\n (output as Record<string, any>)[`${prefix}.${field}`] = fieldValue;\n }\n if (update.state === \"available\") {\n delete (output as Record<string, any>)[`${prefix}.safe_preview`];\n }\n changed = true;\n }\n }\n return changed ? output : value;\n };\n\n for (const [key, value] of Object.entries(attributes)) {\n attributes[key] = rewrite(value);\n }\n for (const prefix of item.prefixes) {\n const tokenKey = `${prefix}.upload_token`;\n const matchesFlat =\n attributes[tokenKey] === item.token ||\n (attributes[`${prefix}.id`] === item.record.id &&\n attributes[`${prefix}.sha256`] === item.sha256);\n if (!matchesFlat) continue;\n delete attributes[tokenKey];\n for (const [field, fieldValue] of Object.entries(update)) {\n attributes[`${prefix}.${field}`] = fieldValue;\n }\n if (update.state === \"available\") delete attributes[`${prefix}.safe_preview`];\n }\n}\n\nfunction mediaUploadTokens(value: unknown, requirePendingState: boolean): Set<string> {\n const tokens = new Set<string>();\n const seen = new WeakSet<object>();\n let visitedNodes = 0;\n const visit = (node: any, depth = 0): void => {\n if (typeof node === \"string\") {\n if (!node.includes(\"nl_pending_media_\")) return;\n if (!requirePendingState && /^nl_pending_media_[0-9a-f]{24}$/.test(node)) {\n tokens.add(node);\n }\n const trimmed = node.trimStart();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return;\n try {\n visit(JSON.parse(node), depth + 1);\n } catch {\n // Invalid JSON cannot authorize an out-of-band upload.\n }\n return;\n }\n if (!node || typeof node !== \"object\" || seen.has(node)) return;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return;\n }\n seen.add(node);\n visitedNodes += 1;\n if (\n !Array.isArray(node) &&\n (!requirePendingState || node.state === \"pending-upload\") &&\n typeof node.upload_token === \"string\"\n ) {\n tokens.add(node.upload_token);\n }\n if (!Array.isArray(node)) {\n for (const [key, token] of Object.entries(node)) {\n if (!key.endsWith(\".upload_token\") || typeof token !== \"string\") continue;\n const prefix = key.slice(0, -\".upload_token\".length);\n if (!requirePendingState || node[`${prefix}.state`] === \"pending-upload\") {\n tokens.add(token);\n }\n }\n }\n try {\n for (const child of Object.values(node)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) break;\n visit(child, depth + 1);\n }\n } catch {\n return;\n }\n };\n visit(value);\n return tokens;\n}\n\nfunction scrubUploadTokens(value: unknown): unknown {\n const seen = new WeakMap<object, unknown>();\n let visitedNodes = 0;\n const rewrite = (node: any, depth = 0): any => {\n if (typeof node === \"string\") {\n if (!node.includes(\"nl_pending_media_\")) return node;\n const trimmed = node.trimStart();\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n if (node.length > MAX_MEDIA_REPLACEMENT_KEY_CHARS) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n return JSON.stringify(rewrite(JSON.parse(node), depth + 1));\n } catch {\n // Fall through to remove any opaque token embedded in plain text.\n }\n }\n return node.replace(/nl_pending_media_[0-9a-f]{24}/g, \"[REDACTED_UPLOAD_TOKEN]\");\n }\n if (!node || typeof node !== \"object\") return node;\n if (depth > MAX_MEDIA_TRAVERSAL_DEPTH || visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n const prior = seen.get(node);\n if (prior) return \"[CIRCULAR]\";\n visitedNodes += 1;\n const output: any = Array.isArray(node) ? [] : {};\n seen.set(node, output);\n try {\n for (const [key, child] of Object.entries(node)) {\n if (visitedNodes >= MAX_MEDIA_TRAVERSAL_NODES) {\n return REDACTED_MEDIA_STRUCTURE;\n }\n if (key === \"upload_token\" || key.endsWith(\".upload_token\")) continue;\n output[key] = rewrite(child, depth + 1);\n }\n } catch {\n return REDACTED_MEDIA_STRUCTURE;\n }\n return output;\n };\n return rewrite(value);\n}\n\nasync function uploadWithinDeadline(\n authority: UploadAuthority,\n payload: UploadPayload,\n deadlineUnixMs: number,\n) {\n const remaining = deadlineUnixMs - Date.now();\n if (remaining <= 0) {\n throw new TelemetryUploadError(\"prepare\", \"media_export_deadline_exceeded\", true);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n authority.upload(payload, { deadlineUnixMs }),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(\n () => reject(new TelemetryUploadError(\"prepare\", \"media_export_deadline_exceeded\", true)),\n remaining,\n );\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/** Upload only media references retained by the accepted masked snapshot. */\nexport async function resolvePendingMediaUploads(\n span: object,\n attributes: Record<string, any>,\n authority: UploadAuthority,\n diagnostics?: DeliveryDiagnostics,\n): Promise<boolean> {\n const bucket = detachPendingBucket(span);\n const pending = bucket?.items ?? [];\n const deadlineUnixMs = Date.now() + DEFAULT_MEDIA_EXPORT_DEADLINE_MS;\n let failed = false;\n try {\n const retainedTokens = mediaUploadTokens(attributes, true);\n const presentTokens = mediaUploadTokens(attributes, false);\n for (const item of pending) {\n const retained = item.prefixes.filter(\n (prefix) =>\n attributes[`${prefix}.upload_token`] === item.token &&\n attributes[`${prefix}.state`] === \"pending-upload\",\n );\n const retainedCount = Math.max(retained.length, retainedTokens.has(item.token) ? 1 : 0);\n if (!retainedTokens.has(item.token)) {\n const tokenWasRetainedInInvalidState = presentTokens.has(item.token);\n if (tokenWasRetainedInInvalidState) {\n failed = true;\n diagnostics?.recordTypedMedia(\"failures\", Math.max(retainedCount, 1));\n diagnostics?.recordUploadFailure(\"validate\", \"masked_pending_state\");\n }\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: tokenWasRetainedInInvalidState\n ? \"upload failed: validate/masked_pending_state\"\n : \"upload skipped: media reference removed by mask\",\n });\n continue;\n }\n if (!authority.available) {\n const unavailableReason = safeUnavailableReason(authority.unavailableReason);\n failed = true;\n diagnostics?.recordTypedMedia(\"unavailable\", retainedCount);\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(\"prepare\", unavailableReason);\n const safePreview = `upload unavailable: ${unavailableReason}`;\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n continue;\n }\n if (item.byteLength > Math.min(authority.maxPayloadBytes, DEFAULT_MAX_TYPED_MEDIA_BYTES)) {\n failed = true;\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(\"validate\", \"payload_too_large\");\n const safePreview = \"upload failed: validate/payload_too_large\";\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n continue;\n }\n if (!isSupportedTypedMediaMimeType(item.mimeType)) {\n failed = true;\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(\"validate\", \"unsupported_mime_type\");\n const safePreview = \"upload failed: validate/unsupported_mime_type\";\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n continue;\n }\n const payload: UploadPayload = {\n content: item.bytes,\n purpose: \"typed_media\",\n sha256: item.sha256,\n byteLength: item.byteLength,\n mimeType: item.mimeType,\n contentEncoding: \"identity\",\n idempotencyKey: `nl-ts-v1:typed_media:${item.sha256}:${item.mimeType}`,\n payloadSchema: \"neatlogs.media.v1\",\n };\n try {\n const receipt = assertReadyUploadReceipt(\n await uploadWithinDeadline(authority, payload, deadlineUnixMs),\n payload,\n );\n diagnostics?.recordTypedMedia(\"uploads\", retainedCount);\n rewritePendingPlaceholder(attributes, item, {\n id: receipt.reference.id,\n reference: receipt.reference.id,\n source: \"uploaded\",\n sha256: receipt.reference.sha256,\n byte_length: receipt.reference.byteLength,\n mime_type: receipt.reference.mimeType,\n purpose: receipt.reference.purpose,\n state: \"available\",\n content_encoding: receipt.reference.contentEncoding,\n });\n } catch (error) {\n failed = true;\n const failure = safeFailure(error);\n diagnostics?.recordTypedMedia(\"failures\", retainedCount);\n diagnostics?.recordUploadFailure(failure.stage, failure.reason);\n const safePreview = `upload failed: ${failure.stage}/${failure.reason}`;\n rewritePendingPlaceholder(attributes, item, {\n state: \"failed\",\n safe_preview: safePreview,\n });\n }\n }\n } finally {\n if (bucket) releasePendingAccount(bucket.account);\n const scrubbed = scrubUploadTokens(attributes);\n for (const key of Object.keys(attributes)) delete attributes[key];\n if (scrubbed && typeof scrubbed === \"object\" && !Array.isArray(scrubbed)) {\n Object.assign(attributes, scrubbed);\n }\n }\n return !failed;\n}\n\n/** Drop out-of-band bytes when a span is filtered or never exported. */\nexport function discardPendingMedia(span: object): void {\n const bucket = detachPendingBucket(span);\n if (bucket) releasePendingAccount(bucket.account);\n}\n\n/** Release all staged bytes owned by one SDK pipeline during shutdown. */\nexport function discardPendingMediaOwner(owner?: object): void {\n const state = pendingMediaOwners.get(owner ?? defaultPendingMediaOwner);\n if (!state) return;\n for (const account of [...state.accounts]) {\n const span = account.span.deref();\n if (span) {\n const bucket = pendingBySpan.get(span);\n if (bucket?.account === account) {\n pendingBySpan.delete(span);\n pendingFinalizer?.unregister(bucket.unregisterToken);\n }\n }\n releasePendingAccount(account);\n }\n pendingMediaOwners.delete(owner ?? defaultPendingMediaOwner);\n}\n","import { createHash } from \"node:crypto\";\n\nexport type UploadPurpose = \"typed_media\" | \"otlp_overflow\";\nexport type UploadContentEncoding = \"identity\" | \"gzip\";\nexport type UploadPayloadSchema = \"otlp.traces.v1\" | \"neatlogs.media.v1\";\n\nexport interface UploadPayload {\n content: Uint8Array;\n purpose: UploadPurpose;\n sha256: string;\n byteLength: number;\n mimeType: string;\n contentEncoding: UploadContentEncoding;\n idempotencyKey: string;\n payloadSchema?: UploadPayloadSchema;\n}\n\nexport interface UploadReference {\n id: string;\n purpose: UploadPurpose;\n sha256: string;\n byteLength: number;\n mimeType: string;\n contentEncoding: UploadContentEncoding;\n state: \"ready\";\n}\n\nexport interface UploadReceipt {\n uploadId: string;\n state: \"ready\";\n reference: UploadReference;\n}\n\nexport interface UploadRequestOptions {\n /** Absolute wall-clock deadline shared by the caller's enclosing export. */\n deadlineUnixMs?: number;\n}\n\nexport interface UploadAuthority {\n readonly available: boolean;\n readonly unavailableReason: string;\n readonly maxPayloadBytes: number;\n upload(payload: UploadPayload, options?: UploadRequestOptions): Promise<UploadReceipt>;\n}\n\nexport type UploadAuthorityOption = boolean | UploadAuthority;\n\nexport const DEFAULT_MAX_OTLP_OVERFLOW_BYTES = 20 * 1024 * 1024;\nexport const DEFAULT_MAX_TYPED_MEDIA_BYTES = 25 * 1024 * 1024;\nexport const DEFAULT_MAX_UPLOAD_BYTES = DEFAULT_MAX_TYPED_MEDIA_BYTES;\nexport const UPLOADS_DISABLED_REASON = \"telemetry_uploads_disabled\";\n\nexport class TelemetryUploadError extends Error {\n constructor(\n readonly stage: \"prepare\" | \"put\" | \"complete\" | \"validate\",\n readonly reasonCode: string,\n readonly retryable: boolean,\n ) {\n super(`telemetry upload ${stage} failed: ${reasonCode}`);\n this.name = \"TelemetryUploadError\";\n }\n}\n\nexport class DisabledUploadAuthority implements UploadAuthority {\n readonly available = false;\n readonly unavailableReason: string;\n readonly maxPayloadBytes: number;\n\n constructor(reason = UPLOADS_DISABLED_REASON, maxPayloadBytes = DEFAULT_MAX_UPLOAD_BYTES) {\n this.unavailableReason = reason;\n this.maxPayloadBytes = maxPayloadBytes;\n }\n\n async upload(_payload: UploadPayload): Promise<UploadReceipt> {\n throw new TelemetryUploadError(\"prepare\", this.unavailableReason, false);\n }\n}\n\ninterface PrepareRequest {\n version: 1;\n purpose: UploadPurpose;\n sha256: string;\n byte_length: number;\n mime_type: string;\n content_encoding: UploadContentEncoding;\n idempotency_key: string;\n payload_schema?: UploadPayloadSchema;\n}\n\ninterface WireReference {\n id: string;\n purpose: UploadPurpose;\n sha256: string;\n byte_length: number;\n mime_type: string;\n content_encoding: UploadContentEncoding;\n state: string;\n}\n\ninterface PreparedUpload {\n uploadId: string;\n state: \"prepared\";\n uploadUrl: string;\n uploadHeaders: Record<string, string>;\n reference: WireReference;\n}\n\ninterface ResumableUpload {\n uploadId: string;\n state: \"uploaded\" | \"validating\";\n reference: WireReference;\n}\n\ntype PrepareResult = PreparedUpload | ResumableUpload | UploadReceipt;\n\ninterface WireDiagnostic {\n stage: string;\n reasonCode: string;\n retryable: boolean;\n}\n\nexport interface HttpUploadAuthorityOptions {\n baseUrl: string;\n apiKey: string;\n fetch?: typeof globalThis.fetch;\n maxPayloadBytes?: number;\n deadlineMs?: number;\n attemptTimeoutMs?: number;\n maxAttempts?: number;\n}\n\nconst SHA256_PATTERN = /^[0-9a-f]{64}$/;\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nconst MIME_TYPE_PATTERN = /^[a-z0-9][a-z0-9!#$&^_.+-]*\\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;\nconst TYPED_MEDIA_MIME_TYPES = new Set([\n \"application/pdf\",\n \"audio/flac\",\n \"audio/mpeg\",\n \"audio/ogg\",\n \"audio/wav\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n]);\nconst SAFE_REASON_PATTERN = /^[a-zA-Z0-9_.-]{1,64}$/;\nconst MAX_RESPONSE_BYTES = 64 * 1024;\nconst SENSITIVE_UPLOAD_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"proxy-authorization\",\n \"x-api-key\",\n]);\nconst HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\nexport function isSupportedTypedMediaMimeType(value: string): boolean {\n return TYPED_MEDIA_MIME_TYPES.has(value);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requiredString(\n value: unknown,\n field: string,\n stage: TelemetryUploadError[\"stage\"],\n): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TelemetryUploadError(stage, `invalid_${field}`, false);\n }\n return value;\n}\n\nfunction validateReference(\n value: unknown,\n payload: UploadPayload,\n expectedUploadId: string,\n expectedState: string,\n stage: TelemetryUploadError[\"stage\"],\n): WireReference {\n if (!isObject(value)) {\n throw new TelemetryUploadError(stage, \"invalid_reference\", false);\n }\n const reference: WireReference = {\n id: requiredString(value.id, \"reference_id\", stage),\n purpose: value.purpose as UploadPurpose,\n sha256: requiredString(value.sha256, \"reference_sha256\", stage),\n byte_length: value.byte_length as number,\n mime_type: requiredString(value.mime_type, \"reference_mime_type\", stage),\n content_encoding: value.content_encoding as UploadContentEncoding,\n state: requiredString(value.state, \"reference_state\", stage),\n };\n if (!UUID_PATTERN.test(reference.id)) {\n throw new TelemetryUploadError(stage, \"invalid_reference_id\", false);\n }\n if (\n reference.id !== expectedUploadId ||\n reference.purpose !== payload.purpose ||\n reference.sha256 !== payload.sha256 ||\n reference.byte_length !== payload.byteLength ||\n reference.mime_type !== payload.mimeType ||\n reference.content_encoding !== payload.contentEncoding ||\n reference.state !== expectedState\n ) {\n throw new TelemetryUploadError(stage, \"reference_mismatch\", false);\n }\n return reference;\n}\n\n/** Validate untrusted/injected authority output and return a credential-free clone. */\nexport function assertReadyUploadReceipt(value: unknown, payload: UploadPayload): UploadReceipt {\n if (!isObject(value)) {\n throw new TelemetryUploadError(\"complete\", \"invalid_receipt\", false);\n }\n const uploadId = requiredString(value.uploadId, \"upload_id\", \"complete\");\n if (!UUID_PATTERN.test(uploadId) || value.state !== \"ready\" || !isObject(value.reference)) {\n throw new TelemetryUploadError(\"complete\", \"invalid_receipt\", false);\n }\n const reference = value.reference;\n if (\n typeof reference.id !== \"string\" ||\n !UUID_PATTERN.test(reference.id) ||\n reference.id !== uploadId ||\n reference.state !== \"ready\" ||\n reference.purpose !== payload.purpose ||\n reference.sha256 !== payload.sha256 ||\n reference.byteLength !== payload.byteLength ||\n reference.mimeType !== payload.mimeType ||\n reference.contentEncoding !== payload.contentEncoding\n ) {\n throw new TelemetryUploadError(\"complete\", \"receipt_mismatch\", false);\n }\n return {\n uploadId,\n state: \"ready\",\n reference: {\n id: reference.id,\n purpose: reference.purpose as UploadPurpose,\n sha256: reference.sha256,\n byteLength: reference.byteLength,\n mimeType: reference.mimeType,\n contentEncoding: reference.contentEncoding as UploadContentEncoding,\n state: \"ready\",\n },\n };\n}\n\nfunction validatePayload(payload: UploadPayload, maxPayloadBytes: number): void {\n if (!(payload.content instanceof Uint8Array)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_content\", false);\n }\n if (!SHA256_PATTERN.test(payload.sha256)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_sha256\", false);\n }\n if (createHash(\"sha256\").update(payload.content).digest(\"hex\") !== payload.sha256) {\n throw new TelemetryUploadError(\"validate\", \"invalid_sha256\", false);\n }\n if (\n !Number.isSafeInteger(payload.byteLength) ||\n payload.byteLength <= 0 ||\n payload.byteLength !== payload.content.byteLength\n ) {\n throw new TelemetryUploadError(\"validate\", \"invalid_byte_length\", false);\n }\n if (!([\"typed_media\", \"otlp_overflow\"] as const).includes(payload.purpose)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_purpose\", false);\n }\n const backendLimit =\n payload.purpose === \"otlp_overflow\"\n ? DEFAULT_MAX_OTLP_OVERFLOW_BYTES\n : DEFAULT_MAX_TYPED_MEDIA_BYTES;\n if (payload.byteLength > Math.min(maxPayloadBytes, backendLimit)) {\n throw new TelemetryUploadError(\"validate\", \"payload_too_large\", false);\n }\n if (!MIME_TYPE_PATTERN.test(payload.mimeType) || payload.mimeType.length > 160) {\n throw new TelemetryUploadError(\"validate\", \"invalid_mime_type\", false);\n }\n if (!([\"identity\", \"gzip\"] as const).includes(payload.contentEncoding)) {\n throw new TelemetryUploadError(\"validate\", \"invalid_content_encoding\", false);\n }\n if (!payload.idempotencyKey || payload.idempotencyKey.length > 128) {\n throw new TelemetryUploadError(\"validate\", \"invalid_idempotency_key\", false);\n }\n if (payload.purpose === \"typed_media\") {\n if (!TYPED_MEDIA_MIME_TYPES.has(payload.mimeType)) {\n throw new TelemetryUploadError(\"validate\", \"unsupported_mime_type\", false);\n }\n if (payload.contentEncoding !== \"identity\") {\n throw new TelemetryUploadError(\"validate\", \"unsupported_content_encoding\", false);\n }\n if (payload.payloadSchema && payload.payloadSchema !== \"neatlogs.media.v1\") {\n throw new TelemetryUploadError(\"validate\", \"invalid_payload_schema\", false);\n }\n } else if (\n payload.mimeType !== \"application/x-protobuf\" ||\n payload.payloadSchema !== \"otlp.traces.v1\"\n ) {\n throw new TelemetryUploadError(\"validate\", \"invalid_overflow_contract\", false);\n }\n}\n\nfunction retryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\nasync function withinDeadline<T>(\n promise: Promise<T>,\n deadline: number,\n stage: TelemetryUploadError[\"stage\"],\n cancel?: () => Promise<void>,\n): Promise<T> {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n void cancel?.().catch(() => undefined);\n throw new TelemetryUploadError(stage, \"deadline_exceeded\", true);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(async () => {\n void cancel?.().catch(() => undefined);\n reject(new TelemetryUploadError(stage, \"deadline_exceeded\", true));\n }, remaining);\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nasync function boundedJson(\n response: Response,\n deadline: number,\n stage: TelemetryUploadError[\"stage\"],\n): Promise<unknown> {\n const declared = Number(response.headers.get(\"content-length\") ?? 0);\n if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {\n await response.body?.cancel().catch(() => undefined);\n throw new TelemetryUploadError(stage, \"response_too_large\", false);\n }\n const reader = response.body?.getReader();\n if (!reader) {\n const text = await withinDeadline(response.text(), deadline, stage);\n if (new TextEncoder().encode(text).byteLength > MAX_RESPONSE_BYTES) {\n throw new TelemetryUploadError(stage, \"response_too_large\", false);\n }\n try {\n return JSON.parse(text);\n } catch {\n throw new TelemetryUploadError(stage, \"invalid_json\", false);\n }\n }\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const { done, value } = await withinDeadline(reader.read(), deadline, stage, () =>\n reader.cancel(),\n );\n if (done) break;\n total += value.byteLength;\n if (total > MAX_RESPONSE_BYTES) {\n await reader.cancel();\n throw new TelemetryUploadError(stage, \"response_too_large\", false);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n try {\n return JSON.parse(new TextDecoder().decode(bytes));\n } catch {\n throw new TelemetryUploadError(stage, \"invalid_json\", false);\n }\n}\n\nfunction responseReason(value: unknown): string | undefined {\n return isObject(value) &&\n typeof value.reason_code === \"string\" &&\n SAFE_REASON_PATTERN.test(value.reason_code)\n ? value.reason_code\n : undefined;\n}\n\nfunction optionalDiagnostic(\n value: unknown,\n stage: TelemetryUploadError[\"stage\"],\n): WireDiagnostic | undefined {\n if (value === undefined) return undefined;\n if (\n !isObject(value) ||\n typeof value.stage !== \"string\" ||\n value.stage.length === 0 ||\n typeof value.reason_code !== \"string\" ||\n !SAFE_REASON_PATTERN.test(value.reason_code) ||\n typeof value.retryable !== \"boolean\"\n ) {\n throw new TelemetryUploadError(stage, \"invalid_diagnostic\", false);\n }\n return {\n stage: value.stage,\n reasonCode: value.reason_code,\n retryable: value.retryable,\n };\n}\n\n/** Authenticated implementation of the Phase 8 telemetry upload draft. */\nexport class HttpUploadAuthority implements UploadAuthority {\n readonly available = true;\n readonly unavailableReason = \"\";\n readonly maxPayloadBytes: number;\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n private readonly deadlineMs: number;\n private readonly attemptTimeoutMs: number;\n private readonly maxAttempts: number;\n\n constructor(options: HttpUploadAuthorityOptions) {\n this.baseUrl = new URL(options.baseUrl).origin;\n this.apiKey = options.apiKey.trim();\n if (!this.apiKey) throw new TypeError(\"apiKey is required for upload authority\");\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new TypeError(\"fetch is required for upload authority\");\n }\n this.fetchImpl = fetchImpl;\n this.maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_UPLOAD_BYTES;\n this.deadlineMs = options.deadlineMs ?? 15_000;\n this.attemptTimeoutMs = options.attemptTimeoutMs ?? 5_000;\n this.maxAttempts = options.maxAttempts ?? 2;\n if (!Number.isSafeInteger(this.maxPayloadBytes) || this.maxPayloadBytes <= 0) {\n throw new RangeError(\"maxPayloadBytes must be a positive safe integer\");\n }\n if (!Number.isFinite(this.deadlineMs) || this.deadlineMs <= 0) {\n throw new RangeError(\"deadlineMs must be positive\");\n }\n if (!Number.isFinite(this.attemptTimeoutMs) || this.attemptTimeoutMs <= 0) {\n throw new RangeError(\"attemptTimeoutMs must be positive\");\n }\n if (!Number.isSafeInteger(this.maxAttempts) || this.maxAttempts <= 0 || this.maxAttempts > 4) {\n throw new RangeError(\"maxAttempts must be an integer between 1 and 4\");\n }\n }\n\n async upload(payload: UploadPayload, options: UploadRequestOptions = {}): Promise<UploadReceipt> {\n validatePayload(payload, this.maxPayloadBytes);\n const deadline = Math.min(\n Date.now() + this.deadlineMs,\n options.deadlineUnixMs ?? Number.POSITIVE_INFINITY,\n );\n if (deadline <= Date.now()) {\n throw new TelemetryUploadError(\"prepare\", \"deadline_exceeded\", true);\n }\n const prepared = await this.prepare(payload, deadline);\n if (prepared.state === \"ready\") return prepared;\n if (prepared.state === \"prepared\") {\n await this.put(prepared, payload, deadline);\n }\n return this.complete(prepared, payload, deadline);\n }\n\n private async prepare(payload: UploadPayload, deadline: number): Promise<PrepareResult> {\n let lastError: TelemetryUploadError | undefined;\n for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {\n try {\n return await this.prepareAttempt(payload, deadline);\n } catch (error) {\n if (!(error instanceof TelemetryUploadError) || !error.retryable) throw error;\n lastError = error;\n }\n if (attempt + 1 < this.maxAttempts) {\n const delay = Math.min(100 * 2 ** attempt, Math.max(0, deadline - Date.now()));\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n throw lastError ?? new TelemetryUploadError(\"prepare\", \"request_failed\", true);\n }\n\n private async prepareAttempt(payload: UploadPayload, deadline: number): Promise<PrepareResult> {\n const body: PrepareRequest = {\n version: 1,\n purpose: payload.purpose,\n sha256: payload.sha256,\n byte_length: payload.byteLength,\n mime_type: payload.mimeType,\n content_encoding: payload.contentEncoding,\n idempotency_key: payload.idempotencyKey,\n ...(payload.payloadSchema ? { payload_schema: payload.payloadSchema } : {}),\n };\n const response = await this.request(\n \"prepare\",\n `${this.baseUrl}/v1/telemetry/uploads`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: JSON.stringify(body),\n credentials: \"omit\",\n redirect: \"error\",\n },\n deadline,\n new Set([200, 201, 202]),\n 1,\n );\n const data = await boundedJson(response, deadline, \"prepare\");\n if (!isObject(data)) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_response\", false);\n }\n const diagnostic = optionalDiagnostic(data.diagnostic, \"prepare\");\n const uploadId = requiredString(data.upload_id, \"upload_id\", \"prepare\");\n if (!UUID_PATTERN.test(uploadId)) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_id\", false);\n }\n if (response.status === 200 && data.state === \"ready\") {\n const reference = validateReference(data.reference, payload, uploadId, \"ready\", \"prepare\");\n return {\n uploadId,\n state: \"ready\",\n reference: {\n id: reference.id,\n purpose: reference.purpose,\n sha256: reference.sha256,\n byteLength: reference.byte_length,\n mimeType: reference.mime_type,\n contentEncoding: reference.content_encoding,\n state: \"ready\",\n },\n };\n }\n if (data.state === \"uploaded\" || data.state === \"validating\") {\n if (response.status !== 200 && response.status !== 202) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_response\", false);\n }\n const reference = validateReference(\n data.reference,\n payload,\n uploadId,\n String(data.state),\n \"prepare\",\n );\n return { uploadId, state: data.state, reference };\n }\n if (data.state === \"rejected\") {\n validateReference(data.reference, payload, uploadId, \"rejected\", \"prepare\");\n throw new TelemetryUploadError(\"prepare\", diagnostic?.reasonCode ?? \"upload_rejected\", false);\n }\n if (response.status !== 201 || data.state !== \"prepared\" || !isObject(data.upload)) {\n throw new TelemetryUploadError(\n \"prepare\",\n diagnostic?.reasonCode ?? \"invalid_response\",\n diagnostic?.retryable ?? false,\n );\n }\n const expiresAt = requiredString(data.expires_at, \"expires_at\", \"prepare\");\n const expires = Date.parse(expiresAt);\n if (!Number.isFinite(expires) || expires <= Date.now()) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_expiry\", false);\n }\n if (data.upload.method !== \"PUT\") {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_method\", false);\n }\n const uploadUrl = requiredString(data.upload.url, \"upload_url\", \"prepare\");\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(uploadUrl);\n } catch {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_url\", false);\n }\n if (parsedUrl.protocol !== \"https:\" || parsedUrl.username || parsedUrl.password) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_url\", false);\n }\n if (!isObject(data.upload.headers)) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_headers\", false);\n }\n const uploadHeaders: Record<string, string> = Object.create(null);\n for (const [key, value] of Object.entries(data.upload.headers)) {\n if (\n typeof value !== \"string\" ||\n !HEADER_NAME_PATTERN.test(key) ||\n /[\\r\\n]/.test(value) ||\n SENSITIVE_UPLOAD_HEADERS.has(key.toLowerCase())\n ) {\n throw new TelemetryUploadError(\"prepare\", \"invalid_upload_headers\", false);\n }\n uploadHeaders[key] = value;\n }\n const reference = validateReference(data.reference, payload, uploadId, \"prepared\", \"prepare\");\n return { uploadId, state: \"prepared\", uploadUrl, uploadHeaders, reference };\n }\n\n private async put(\n prepared: PreparedUpload,\n payload: UploadPayload,\n deadline: number,\n ): Promise<void> {\n const response = await this.request(\n \"put\",\n prepared.uploadUrl,\n {\n method: \"PUT\",\n headers: prepared.uploadHeaders,\n body: payload.content,\n credentials: \"omit\",\n redirect: \"error\",\n },\n deadline,\n );\n try {\n void response.body?.cancel().catch(() => undefined);\n } catch {\n // A successful object PUT is authoritative; cleanup must not trigger a retry.\n }\n }\n\n private async complete(\n prepared: PreparedUpload | ResumableUpload,\n payload: UploadPayload,\n deadline: number,\n ): Promise<UploadReceipt> {\n let lastError: TelemetryUploadError | undefined;\n for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {\n try {\n return await this.completeAttempt(prepared, payload, deadline);\n } catch (error) {\n if (!(error instanceof TelemetryUploadError) || !error.retryable) throw error;\n lastError = error;\n }\n if (attempt + 1 < this.maxAttempts) {\n const delay = Math.min(100 * 2 ** attempt, Math.max(0, deadline - Date.now()));\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n throw lastError ?? new TelemetryUploadError(\"complete\", \"request_failed\", true);\n }\n\n private async completeAttempt(\n prepared: PreparedUpload | ResumableUpload,\n payload: UploadPayload,\n deadline: number,\n ): Promise<UploadReceipt> {\n const response = await this.request(\n \"complete\",\n `${this.baseUrl}/v1/telemetry/uploads/${encodeURIComponent(prepared.uploadId)}/complete`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: JSON.stringify({\n sha256: payload.sha256,\n byte_length: payload.byteLength,\n }),\n credentials: \"omit\",\n redirect: \"error\",\n },\n deadline,\n new Set([200, 202]),\n 1,\n );\n const data = await boundedJson(response, deadline, \"complete\");\n if (!isObject(data)) {\n throw new TelemetryUploadError(\"complete\", \"invalid_response\", false);\n }\n const diagnostic = optionalDiagnostic(data.diagnostic, \"complete\");\n if (data.upload_id !== prepared.uploadId) {\n throw new TelemetryUploadError(\"complete\", \"upload_id_mismatch\", false);\n }\n if (![\"ready\", \"uploaded\", \"validating\", \"rejected\"].includes(String(data.state))) {\n throw new TelemetryUploadError(\"complete\", \"invalid_state\", false);\n }\n const reference = validateReference(\n data.reference,\n payload,\n prepared.uploadId,\n String(data.state),\n \"complete\",\n );\n if (data.state !== \"ready\") {\n const reason =\n diagnostic?.reasonCode ??\n (data.state === \"uploaded\" || data.state === \"validating\"\n ? \"validation_pending\"\n : \"upload_rejected\");\n throw new TelemetryUploadError(\n \"complete\",\n reason,\n diagnostic?.retryable ?? (data.state === \"uploaded\" || data.state === \"validating\"),\n );\n }\n return {\n uploadId: prepared.uploadId,\n state: \"ready\",\n reference: {\n id: reference.id,\n purpose: reference.purpose,\n sha256: reference.sha256,\n byteLength: reference.byte_length,\n mimeType: reference.mime_type,\n contentEncoding: reference.content_encoding,\n state: \"ready\",\n },\n };\n }\n\n private async request(\n stage: \"prepare\" | \"put\" | \"complete\",\n url: string,\n init: RequestInit,\n deadline: number,\n expectedStatuses?: ReadonlySet<number>,\n attemptLimit = this.maxAttempts,\n parseErrorBody = stage !== \"put\",\n ): Promise<Response> {\n let lastError: TelemetryUploadError | undefined;\n for (let attempt = 0; attempt < attemptLimit; attempt += 1) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new TelemetryUploadError(stage, \"deadline_exceeded\", true);\n }\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n Math.min(remaining, this.attemptTimeoutMs),\n );\n timer.unref?.();\n try {\n const response = await this.fetchImpl(url, {\n ...init,\n signal: controller.signal,\n });\n const accepted = expectedStatuses\n ? expectedStatuses.has(response.status)\n : response.status >= 200 && response.status < 300;\n if (accepted) return response;\n let retryable = retryableStatus(response.status);\n let reason = `http_${response.status}`;\n if (parseErrorBody) {\n try {\n const errorBody = await boundedJson(response, deadline, stage);\n reason = responseReason(errorBody) ?? reason;\n if (isObject(errorBody) && typeof errorBody.retryable === \"boolean\") {\n retryable = errorBody.retryable;\n }\n } catch {\n // HTTP status remains authoritative when an error body is malformed.\n }\n } else {\n await response.body?.cancel().catch(() => undefined);\n }\n lastError = new TelemetryUploadError(stage, reason, retryable);\n if (!retryable) throw lastError;\n } catch (error) {\n if (error instanceof TelemetryUploadError && !error.retryable) throw error;\n lastError =\n error instanceof TelemetryUploadError\n ? error\n : new TelemetryUploadError(\n stage,\n controller.signal.aborted ? \"timeout\" : \"network_error\",\n true,\n );\n } finally {\n clearTimeout(timer);\n }\n if (attempt + 1 < attemptLimit) {\n const delay = Math.min(100 * 2 ** attempt, Math.max(0, deadline - Date.now()));\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n throw lastError ?? new TelemetryUploadError(stage, \"request_failed\", true);\n }\n}\n\nexport function isUploadAuthority(value: unknown): value is UploadAuthority {\n return (\n isObject(value) &&\n typeof value.available === \"boolean\" &&\n typeof value.unavailableReason === \"string\" &&\n Number.isSafeInteger(value.maxPayloadBytes) &&\n (value.maxPayloadBytes as number) > 0 &&\n typeof value.upload === \"function\"\n );\n}\n\nexport function uploadsEnabledFromEnv(value: string | undefined): boolean {\n return [\"true\", \"1\", \"yes\"].includes((value ?? \"\").trim().toLowerCase());\n}\n\nexport function resolveUploadAuthority(\n option: UploadAuthorityOption | undefined,\n envValue: string | undefined,\n baseUrl: string,\n apiKey: string,\n): UploadAuthority {\n if (isUploadAuthority(option)) return option;\n const enabled = typeof option === \"boolean\" ? option : uploadsEnabledFromEnv(envValue);\n return enabled ? new HttpUploadAuthority({ baseUrl, apiKey }) : new DisabledUploadAuthority();\n}\n","/**\n * Core span decoration utilities.\n * Provides the low-level wrapping logic used by span() and Span().\n */\n\nimport {\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span as OtelSpan,\n} from \"@opentelemetry/api\";\nimport { getLogger } from \"../core/logger.js\";\nimport { registerMask } from \"../core/mask.js\";\nimport { applyEndUserAttributes, isRootSpan } from \"../core/end-user.js\";\nimport { applySessionAttributes } from \"../core/session.js\";\nimport {\n captureMedia,\n captureMediaWithIndex,\n sanitizeMediaPayload,\n} from \"../core/media.js\";\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n withNeatlogsSpan,\n} from \"../core/provider.js\";\nimport {\n bindSpanToAutoRoot,\n maybeOpenAutoRoot,\n} from \"../core/auto-root.js\";\nimport type { SpanOptions, MaskFunction } from \"../types.js\";\nimport {\n DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n DEFAULT_MAX_STREAM_CAPTURE_BYTES,\n utf8ByteLength,\n} from \"../constants.js\";\n\nconst logger = getLogger();\nconst TRACER_NAME = \"neatlogs\";\nconst STREAM_CAPTURE_TRUNCATED = \"[TRUNCATED_STREAM_OUTPUT]\";\n\n/** Safely serialize any value to JSON string. */\nexport function safeJsonDumps(value: any): string {\n try {\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === \"bigint\") return val.toString();\n if (val instanceof Error)\n return { message: val.message, name: val.name, stack: val.stack };\n if (typeof val === \"function\")\n return `[Function: ${val.name || \"anonymous\"}]`;\n return val;\n });\n } catch {\n return String(value);\n }\n}\n\n/** Serialize an object for span attributes. Handles toJSON(), plain objects, primitives. */\nexport function serializeObj(obj: any): any {\n if (obj === null || obj === undefined) return obj;\n // Preserve binary views until media capture can classify and sanitize them.\n // JSON.stringify retains the previous numeric-object representation for\n // ordinary, non-media Uint8Array values.\n if (obj instanceof Uint8Array) return obj;\n if (\n typeof obj === \"string\" ||\n typeof obj === \"number\" ||\n typeof obj === \"boolean\"\n )\n return obj;\n if (typeof obj.toJSON === \"function\") return obj.toJSON();\n if (Array.isArray(obj)) return obj.map(serializeObj);\n if (typeof obj === \"object\") {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = serializeObj(value);\n }\n return result;\n }\n return String(obj);\n}\n\n/** Set common span attributes from options. */\nexport function setCommonSpanAttrs(span: OtelSpan, opts: SpanOptions): void {\n if (opts.kind) {\n span.setAttribute(\"openinference.span.kind\", opts.kind);\n }\n if (opts.internal) {\n span.setAttribute(\"neatlogs.internal\", true);\n }\n if (opts.description) {\n span.setAttribute(\"neatlogs.description\", opts.description);\n }\n if (opts.mask) {\n const maskId = registerMask(opts.mask);\n span.setAttribute(\"neatlogs.mask_id\", maskId);\n }\n}\n\nexport interface DecorateSpanOptions extends SpanOptions {\n /** Override the span name (defaults to function name). */\n spanName?: string;\n /** Post-process callback after the function returns. */\n postprocessResult?: (\n span: OtelSpan,\n result: any,\n boundInputs: Record<string, any>,\n ) => void;\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { [Symbol.asyncIterator]?: unknown })[\n Symbol.asyncIterator\n ] === \"function\"\n );\n}\n\nfunction isSyncIterator(\n value: unknown,\n): value is Iterator<unknown> & Iterable<unknown> {\n return (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { next?: unknown }).next === \"function\" &&\n typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] ===\n \"function\"\n );\n}\n\nfunction isReadableStreamLike(value: unknown): value is {\n getReader: (...args: any[]) => any;\n cancel: (...args: any[]) => Promise<unknown>;\n} {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as { getReader?: unknown }).getReader === \"function\" &&\n typeof (value as { cancel?: unknown }).cancel === \"function\"\n );\n}\n\n/**\n * Core wrapper factory that creates an instrumented version of a function.\n * This is the low-level building block used by span() and Span().\n */\nexport function decorateSpan<TArgs extends any[], TReturn>(\n opts: DecorateSpanOptions,\n fn: (...args: TArgs) => TReturn,\n): (...args: TArgs) => TReturn {\n const spanName = opts.spanName ?? opts.name ?? (fn.name || \"anonymous\");\n const captureInput = opts.captureInput !== false;\n const captureOutput = opts.captureOutput !== false;\n const mediaPrefix = (direction: \"input\" | \"output\"): string => {\n const kind = String(opts.kind ?? \"span\").trim().toLowerCase() || \"span\";\n return `neatlogs.${kind}.${direction}`;\n };\n\n const wrapped = (...args: TArgs): any => {\n // Resolve the tracer at CALL time, not definition time: a decorator applied\n // before init() would otherwise capture the no-op global provider forever\n // and emit zero spans once the private provider is configured.\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // End-user belongs to the trace root only. Capture root status BEFORE the\n // span is created (once it's active, it would be the \"current\" span).\n const isRoot = isRootSpan();\n // Parent from our private span store; a root decorated function anchors a\n // fresh trace under ROOT_CONTEXT.\n const parentContext = isRoot ? ROOT_CONTEXT : getNeatlogsParentContext();\n const autoRoot = maybeOpenAutoRoot(\n tracer,\n String(opts.kind ?? \"\"),\n parentContext,\n );\n if (autoRoot.root) {\n applySessionAttributes(autoRoot.root, opts.sessionId, true, {\n parentSessionId: opts.parentSessionId,\n sessionFeatureName: opts.sessionFeatureName,\n sessionEntryPoint: opts.sessionEntryPoint,\n });\n applyEndUserAttributes(\n autoRoot.root,\n opts.endUserId,\n opts.endUserMetadata,\n true,\n );\n }\n const span = bindSpanToAutoRoot(\n tracer.startSpan(spanName, {}, autoRoot.ctx),\n autoRoot.root,\n );\n return withNeatlogsSpan(span, () => {\n let finished = false;\n let streamInterrupted = false;\n const streamChunks: any[] = [];\n let totalStreamChunks = 0;\n let retainedStreamBytes = 0;\n let droppedStreamChunks = 0;\n let streamMediaCount = 0;\n\n const captureResult = (result: any): void => {\n if (captureOutput) {\n try {\n const serialized = serializeObj(result);\n const captured = captureMedia(\n span,\n mediaPrefix(\"output\"),\n serialized,\n \"output\",\n );\n span.setAttribute(\n \"output.value\",\n safeJsonDumps(captured),\n );\n } catch (err) {\n logger.debug(`Failed to capture output: ${err}`);\n }\n }\n\n if (opts.postprocessResult) {\n try {\n const boundInputs = _extractBoundInputs(fn, args);\n opts.postprocessResult(span, result, boundInputs);\n } catch (err) {\n logger.debug(`Postprocess failed: ${err}`);\n }\n }\n };\n\n const finishSuccess = (result: any): void => {\n if (finished) return;\n finished = true;\n captureResult(result);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n };\n\n const setStreamCounts = (): void => {\n span.setAttribute(\"neatlogs.stream.chunk_count\", totalStreamChunks);\n if (totalStreamChunks > DEFAULT_MAX_SEMANTIC_STREAM_EVENTS) {\n span.setAttribute(\n \"neatlogs.stream.events_dropped\",\n totalStreamChunks - DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n );\n }\n if (droppedStreamChunks > 0) {\n span.setAttribute(\"neatlogs.stream.output_truncated\", true);\n span.setAttribute(\n \"neatlogs.stream.chunks_not_captured\",\n droppedStreamChunks,\n );\n span.setAttribute(\n \"neatlogs.stream.retained_bytes\",\n retainedStreamBytes,\n );\n }\n };\n\n const streamCaptureValue = (): any[] =>\n droppedStreamChunks > 0\n ? [\n ...streamChunks,\n {\n neatlogs_stream_capture: STREAM_CAPTURE_TRUNCATED,\n chunks_not_captured: droppedStreamChunks,\n },\n ]\n : streamChunks;\n\n const finishError = (error: any): void => {\n if (finished) return;\n finished = true;\n if (error?.name === \"AbortError\") {\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n if (totalStreamChunks > 0) {\n setStreamCounts();\n captureResult(streamCaptureValue());\n }\n span.setStatus({ code: SpanStatusCode.UNSET });\n span.end();\n return;\n }\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error?.message ?? String(error),\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n span.end();\n };\n\n const recordChunk = (chunk: any): void => {\n const index = totalStreamChunks++;\n if (index >= DEFAULT_MAX_SEMANTIC_STREAM_EVENTS) {\n droppedStreamChunks += 1;\n return;\n }\n\n let serializedValue: unknown;\n let serialized: string;\n try {\n serializedValue = serializeObj(chunk);\n serialized = safeJsonDumps(\n sanitizeMediaPayload(serializedValue, \"output\"),\n );\n } catch {\n droppedStreamChunks += 1;\n return;\n }\n const encodedBytes = utf8ByteLength(\n serialized,\n DEFAULT_MAX_STREAM_CAPTURE_BYTES - retainedStreamBytes,\n );\n if (\n retainedStreamBytes + encodedBytes >\n DEFAULT_MAX_STREAM_CAPTURE_BYTES\n ) {\n droppedStreamChunks += 1;\n return;\n }\n\n const captured = captureMediaWithIndex(\n span,\n mediaPrefix(\"output\"),\n serializedValue,\n \"output\",\n streamMediaCount,\n );\n streamMediaCount += captured.count;\n streamChunks.push(captured.value);\n retainedStreamBytes += encodedBytes;\n const summary: Record<string, unknown> = {\n value_type: Array.isArray(chunk) ? \"array\" : typeof chunk,\n encoded_bytes: encodedBytes,\n };\n if (Array.isArray(chunk)) summary.items = chunk.length;\n else if (chunk && typeof chunk === \"object\")\n summary.keys = Object.keys(chunk).sort();\n span.addEvent(\"neatlogs.stream.chunk\", {\n \"neatlogs.stream.chunk.index\": index,\n \"neatlogs.stream.chunk.summary\": safeJsonDumps(summary),\n });\n };\n\n const finishStream = (cancelled = false): void => {\n setStreamCounts();\n if (!cancelled) {\n finishSuccess(streamCaptureValue());\n return;\n }\n if (finished) return;\n finished = true;\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n captureResult(streamCaptureValue());\n span.setStatus({ code: SpanStatusCode.UNSET });\n span.end();\n };\n\n const wrapAsyncIterator = (iterator: any): any => {\n let proxy: any;\n proxy = new Proxy(iterator, {\n get(target, property) {\n if (property === Symbol.asyncIterator) return () => proxy;\n if (property === \"next\") {\n return async (...nextArgs: any[]) => {\n try {\n const item = await withNeatlogsSpan(span, () =>\n target.next(...nextArgs),\n );\n if (item.done) finishStream(streamInterrupted);\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"return\") {\n return async (...returnArgs: any[]) => {\n try {\n streamInterrupted = true;\n const item = target.return\n ? await withNeatlogsSpan(span, () =>\n target.return(...returnArgs),\n )\n : { done: true, value: returnArgs[0] };\n if (item.done) finishStream(true);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"throw\") {\n return async (...throwArgs: any[]) => {\n try {\n const item = target.throw\n ? await withNeatlogsSpan(span, () =>\n target.throw(...throwArgs),\n )\n : Promise.reject(throwArgs[0]);\n if (item.done) finishStream();\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n return proxy;\n };\n\n const wrapAsyncIterable = (source: AsyncIterable<unknown>): any => {\n if (typeof (source as any).next === \"function\") {\n return wrapAsyncIterator(source);\n }\n return new Proxy(source as any, {\n get(target, property) {\n if (property === Symbol.asyncIterator) {\n return () => wrapAsyncIterator(target[Symbol.asyncIterator]());\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n };\n\n const wrapSyncIterator = (\n iterator: Iterator<unknown> & Iterable<unknown>,\n ): any => {\n let proxy: any;\n proxy = new Proxy(iterator, {\n get(target, property) {\n if (property === Symbol.iterator) return () => proxy;\n if (property === \"next\") {\n return (...nextArgs: any[]) => {\n try {\n const item = withNeatlogsSpan(span, () =>\n (\n target.next as (\n ...args: any[]\n ) => IteratorResult<unknown>\n )(...nextArgs),\n );\n if (item.done) finishStream(streamInterrupted);\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"return\") {\n return (...returnArgs: any[]) => {\n try {\n streamInterrupted = true;\n const item = target.return\n ? withNeatlogsSpan(span, () =>\n (\n target.return as (\n ...args: any[]\n ) => IteratorResult<unknown>\n )(...returnArgs),\n )\n : { done: true, value: returnArgs[0] };\n if (item.done) finishStream(true);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"throw\") {\n return (...throwArgs: any[]) => {\n try {\n if (!target.throw) throw throwArgs[0];\n const item = withNeatlogsSpan(span, () =>\n (\n target.throw as (\n ...args: any[]\n ) => IteratorResult<unknown>\n )(...throwArgs),\n );\n if (item.done) finishStream(streamInterrupted);\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n return proxy;\n };\n\n const wrapReader = (reader: any): any =>\n new Proxy(reader, {\n get(target, property) {\n if (property === \"read\") {\n return async (...readArgs: any[]) => {\n try {\n const item = await withNeatlogsSpan(span, () =>\n target.read(...readArgs),\n );\n if (item.done) finishStream();\n else recordChunk(item.value);\n return item;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"cancel\") {\n return async (...cancelArgs: any[]) => {\n try {\n const result = await target.cancel(...cancelArgs);\n finishStream(true);\n return result;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"releaseLock\") {\n return (...releaseArgs: any[]) => {\n try {\n return target.releaseLock(...releaseArgs);\n } finally {\n finishStream(true);\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n const wrapReadableStream = (stream: any): any =>\n new Proxy(stream, {\n get(target, property) {\n if (property === \"getReader\") {\n return (...readerArgs: any[]) =>\n wrapReader(target.getReader(...readerArgs));\n }\n if (property === Symbol.asyncIterator) {\n const iteratorFactory = target[Symbol.asyncIterator];\n if (typeof iteratorFactory === \"function\") {\n return () => wrapAsyncIterator(iteratorFactory.call(target));\n }\n }\n if (property === \"cancel\") {\n return async (...cancelArgs: any[]) => {\n try {\n const result = await target.cancel(...cancelArgs);\n finishStream(true);\n return result;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n if (property === \"pipeTo\") {\n return async (...pipeArgs: any[]) => {\n try {\n const result = await target.pipeTo(...pipeArgs);\n finishStream();\n return result;\n } catch (error) {\n finishError(error);\n throw error;\n }\n };\n }\n const value = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n try {\n // Set common attributes\n setCommonSpanAttrs(span, opts);\n\n // Session/end-user identity (root span only; skipped on a non-root child).\n const spanIsRoot = isRoot && !autoRoot.root;\n applySessionAttributes(span, opts.sessionId, spanIsRoot, {\n parentSessionId: opts.parentSessionId,\n sessionFeatureName: opts.sessionFeatureName,\n sessionEntryPoint: opts.sessionEntryPoint,\n });\n applyEndUserAttributes(\n span,\n opts.endUserId,\n opts.endUserMetadata,\n spanIsRoot,\n );\n\n // Capture input\n if (captureInput && args.length > 0) {\n try {\n const inputValue =\n args.length === 1\n ? serializeObj(args[0])\n : args.map(serializeObj);\n span.setAttribute(\n \"input.value\",\n safeJsonDumps(\n captureMedia(\n span,\n mediaPrefix(\"input\"),\n inputValue,\n \"input\",\n ),\n ),\n );\n } catch (err) {\n logger.debug(`Failed to capture input: ${err}`);\n }\n }\n\n // Execute the function\n const result = fn(...args);\n\n const handleResult = (resolved: any): any => {\n // A promise may resolve to a stream. Classify the resolved value before\n // ending the span so async factories remain open through consumption.\n if (isReadableStreamLike(resolved))\n return wrapReadableStream(resolved);\n if (isAsyncIterable(resolved)) return wrapAsyncIterable(resolved);\n if (isSyncIterator(resolved)) return wrapSyncIterator(resolved);\n finishSuccess(resolved);\n return resolved;\n };\n\n // Handle any Promise/A+ thenable, not only native Promise instances.\n if (isPromiseLike(result)) {\n return Promise.resolve(result)\n .then(handleResult)\n .catch((error: any) => {\n finishError(error);\n throw error;\n });\n }\n\n return handleResult(result);\n } catch (error: any) {\n finishError(error);\n throw error;\n }\n }, autoRoot.ctx, autoRoot.root);\n };\n\n // Preserve function name for debugging\n Object.defineProperty(wrapped, \"name\", {\n value: spanName,\n configurable: true,\n });\n\n return wrapped as any;\n}\n\n/** Extract named arguments from function call (best-effort). */\nfunction _extractBoundInputs(fn: Function, args: any[]): Record<string, any> {\n const result: Record<string, any> = {};\n // Parse function parameter names from toString()\n const fnStr = fn.toString();\n const match = fnStr.match(/\\(([^)]*)\\)/);\n if (match) {\n const paramNames = match[1]\n .split(\",\")\n .map((p) =>\n p\n .trim()\n .replace(/\\s*[:=].*$/, \"\")\n .replace(/^\\.\\.\\./, \"\"),\n )\n .filter(Boolean);\n for (let i = 0; i < Math.min(paramNames.length, args.length); i++) {\n result[paramNames[i]] = args[i];\n }\n }\n return result;\n}\n","/** Stable SDK-wide defaults shared by every NeatLogs entry point. */\nexport const DEFAULT_INGEST_ENDPOINT = 'https://ingest.neatlogs.com' as const;\nexport const DEFAULT_MAX_QUEUE_ITEMS = 2048;\nexport const DEFAULT_MAX_SEMANTIC_STREAM_EVENTS = 128;\nexport const DEFAULT_MAX_STREAM_CAPTURE_BYTES = 1024 * 1024;\nexport const DEFAULT_MAX_STREAM_CAPTURE_ITEMS = 1024;\n\n/** UTF-8 byte length without allocating a second buffer for hostile stream chunks. */\nexport function utf8ByteLength(\n value: string,\n limit = Number.MAX_SAFE_INTEGER,\n): number {\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code < 0x80) bytes += 1;\n else if (code < 0x800) bytes += 2;\n else if (code >= 0xd800 && code <= 0xdbff && i + 1 < value.length) {\n const next = value.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) {\n bytes += 4;\n i += 1;\n } else bytes += 3;\n } else bytes += 3;\n if (bytes > limit) return limit + 1;\n }\n return bytes;\n}\n\nexport function exportQueueCapacity(batchSize: number): number {\n return Math.max(DEFAULT_MAX_QUEUE_ITEMS, batchSize * 4);\n}\n","import { SpanStatusCode, type Span } from \"@opentelemetry/api\";\nimport { createHash } from \"node:crypto\";\nimport { captureMediaWithIndex, sanitizeMediaPayload } from \"./media.js\";\nimport {\n DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n DEFAULT_MAX_STREAM_CAPTURE_BYTES,\n DEFAULT_MAX_STREAM_CAPTURE_ITEMS,\n utf8ByteLength,\n} from \"../constants.js\";\n\ninterface ToolCallState {\n id: string;\n name: string;\n arguments: string;\n type: string;\n details: string;\n synthetic: boolean;\n incomplete: boolean;\n}\n\ninterface ChoiceState {\n role: string;\n content: string[];\n reasoning: string[];\n mediaCount: number;\n finishReason: string | null;\n toolCalls: Map<number, ToolCallState>;\n}\n\nfunction hash(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction stringify(value: unknown): string {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value ?? \"\");\n }\n}\n\n/** Incremental OpenAI-compatible choice/tool-fragment accumulator. */\nexport class ChoiceAccumulator {\n private readonly choices = new Map<number, ChoiceState>();\n private usage: any = null;\n private model = \"\";\n private responseId = \"\";\n private chunkCount = 0;\n private finalized = false;\n private capturedBytes = 0;\n private capturedItems = 0;\n private droppedBytes = 0;\n private droppedItems = 0;\n private toolCallCount = 0;\n private readonly incompleteReasons = new Set<string>();\n\n constructor(\n private readonly captureFidelity:\n | \"native\"\n | \"normalized\"\n | \"flattened\"\n | \"unknown\" = \"native\",\n ) {}\n\n addResponse(response: any, span?: Span): void {\n this.captureEnvelope(response);\n for (\n let position = 0;\n position < (response?.choices?.length ?? 0);\n position += 1\n ) {\n const choice = response.choices[position];\n const index = Number.isInteger(choice?.index) ? choice.index : position;\n if (!this.canRetainChoice(index)) continue;\n const message = choice?.message ?? {};\n const state = this.choice(index);\n if (message.role && String(message.role) !== state.role) {\n this.retainString(String(message.role), (value) => (state.role = value));\n }\n if (message.content != null) {\n const captured = span\n ? captureMediaWithIndex(\n span,\n `neatlogs.llm.output_messages.${index}`,\n message.content,\n \"output\",\n state.mediaCount,\n )\n : {\n value: sanitizeMediaPayload(message.content, \"output\"),\n count: 0,\n };\n state.mediaCount += captured.count;\n this.retainString(stringify(captured.value), (value) => state.content.push(value));\n }\n if (message.reasoning_content != null) {\n this.retainString(stringify(message.reasoning_content), (value) =>\n state.reasoning.push(value),\n );\n }\n this.addToolFragments(index, message.tool_calls);\n if (choice?.finish_reason != null) {\n this.retainString(String(choice.finish_reason), (value) => (state.finishReason = value));\n }\n }\n }\n\n addChunk(span: Span, chunk: any): void {\n const chunkIndex = this.chunkCount++;\n this.captureEnvelope(chunk);\n const summary: Array<Record<string, unknown>> = [];\n for (\n let position = 0;\n position < (chunk?.choices?.length ?? 0);\n position += 1\n ) {\n const choice = chunk.choices[position];\n const index = Number.isInteger(choice?.index) ? choice.index : position;\n if (!this.canRetainChoice(index)) continue;\n const delta = choice?.delta ?? {};\n const state = this.choice(index);\n if (delta.role && String(delta.role) !== state.role) {\n this.retainString(String(delta.role), (value) => (state.role = value));\n }\n const captured =\n delta.content == null\n ? { value: \"\", count: 0 }\n : captureMediaWithIndex(\n span,\n `neatlogs.llm.output_messages.${index}`,\n delta.content,\n \"output\",\n state.mediaCount,\n );\n state.mediaCount += captured.count;\n const content = delta.content == null ? \"\" : stringify(captured.value);\n const reasoning =\n delta.reasoning_content == null\n ? \"\"\n : stringify(delta.reasoning_content);\n const contentBytes = content\n ? this.retainString(content, (value) => state.content.push(value))\n : 0;\n const reasoningBytes = reasoning\n ? this.retainString(reasoning, (value) => state.reasoning.push(value))\n : 0;\n this.addToolFragments(index, delta.tool_calls);\n if (choice?.finish_reason != null) {\n this.retainString(String(choice.finish_reason), (value) => (state.finishReason = value));\n }\n summary.push({\n choice_index: index,\n content_bytes: contentBytes,\n reasoning_bytes: reasoningBytes,\n tool_fragments: Array.isArray(delta.tool_calls)\n ? delta.tool_calls.length\n : 0,\n finish_reason: choice?.finish_reason ?? null,\n });\n }\n if (\n chunkIndex < DEFAULT_MAX_SEMANTIC_STREAM_EVENTS &&\n (summary.length > 0 || chunk?.usage)\n ) {\n span.addEvent(\"neatlogs.stream.chunk\", {\n \"neatlogs.stream.chunk.index\": chunkIndex,\n \"neatlogs.stream.chunk.summary\": stringify({\n choices: summary,\n usage: !!chunk?.usage,\n }),\n });\n }\n }\n\n apply(span: Span): void {\n let flattenedToolIndex = 0;\n const indexes = [...this.choices.keys()].sort(\n (left, right) => left - right,\n );\n for (const choiceIndex of indexes) {\n const choice = this.choices.get(choiceIndex)!;\n const prefix = `neatlogs.llm.output_messages.${choiceIndex}`;\n span.setAttribute(`${prefix}.role`, choice.role || \"assistant\");\n const content = choice.content.join(\"\");\n if (content) span.setAttribute(`${prefix}.content`, content);\n const reasoning = choice.reasoning.join(\"\");\n if (reasoning) span.setAttribute(`${prefix}.thinking`, reasoning);\n if (choice.finishReason !== null) {\n span.setAttribute(\n `neatlogs.llm.choices.${choiceIndex}.finish_reason`,\n choice.finishReason,\n );\n if (choiceIndex === indexes[0]) {\n span.setAttribute(\"neatlogs.llm.finish_reason\", choice.finishReason);\n }\n }\n for (const toolIndex of [...choice.toolCalls.keys()].sort(\n (left, right) => left - right,\n )) {\n const tool = choice.toolCalls.get(toolIndex)!;\n if (!tool.id) {\n const context = span.spanContext();\n tool.id = `nl_${hash(\n `${context.traceId}:${context.spanId}:${choiceIndex}:${toolIndex}:${tool.name}:${hash(tool.arguments)}`,\n ).slice(0, 24)}`;\n tool.synthetic = true;\n }\n const toolPrefix = `neatlogs.llm.tool_calls.${flattenedToolIndex}`;\n span.setAttribute(`${toolPrefix}.id`, tool.id);\n if (tool.type) span.setAttribute(`${toolPrefix}.type`, tool.type);\n span.setAttribute(`${toolPrefix}.name`, tool.name);\n span.setAttribute(\n `${toolPrefix}.arguments`,\n tool.incomplete\n ? \"[incomplete: stream capture limit reached]\"\n : tool.arguments,\n );\n if (tool.details)\n span.setAttribute(`${toolPrefix}.details`, tool.details);\n span.setAttribute(`${toolPrefix}.choice_index`, choiceIndex);\n span.setAttribute(`${toolPrefix}.tool_call_index`, toolIndex);\n if (tool.synthetic)\n span.setAttribute(`${toolPrefix}.id_synthetic`, true);\n flattenedToolIndex += 1;\n }\n }\n if (this.model) span.setAttribute(\"neatlogs.llm.model_name\", this.model);\n if (this.responseId)\n span.setAttribute(\"neatlogs.llm.response_id\", this.responseId);\n this.applyUsage(span);\n span.setAttribute(\n \"neatlogs.capture_fidelity\",\n this.incompleteReasons.size > 0 ? \"truncated\" : this.captureFidelity,\n );\n if (this.chunkCount > 0 || this.incompleteReasons.size > 0) {\n span.setAttribute(\"neatlogs.stream.capture_bytes\", this.capturedBytes);\n span.setAttribute(\"neatlogs.stream.capture_items\", this.capturedItems);\n }\n if (this.incompleteReasons.size > 0) {\n span.setAttribute(\"neatlogs.stream.incomplete\", true);\n span.setAttribute(\n \"neatlogs.stream.incomplete_reason\",\n [...this.incompleteReasons].sort().join(\",\"),\n );\n span.setAttribute(\"neatlogs.stream.dropped_bytes\", this.droppedBytes);\n span.setAttribute(\"neatlogs.stream.dropped_bytes_is_lower_bound\", true);\n span.setAttribute(\"neatlogs.stream.dropped_items\", this.droppedItems);\n }\n if (this.chunkCount > 0) {\n span.setAttribute(\"neatlogs.stream.chunk_count\", this.chunkCount);\n if (this.chunkCount > DEFAULT_MAX_SEMANTIC_STREAM_EVENTS) {\n span.setAttribute(\n \"neatlogs.stream.events_dropped\",\n this.chunkCount - DEFAULT_MAX_SEMANTIC_STREAM_EVENTS,\n );\n }\n }\n }\n\n finish(span: Span, interrupted = false): void {\n if (this.finalized) return;\n this.finalized = true;\n this.apply(span);\n if (interrupted) {\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n }\n\n fail(span: Span, error: unknown): void {\n if (this.finalized) return;\n this.finalized = true;\n this.apply(span);\n if ((error as any)?.name === \"AbortError\") {\n span.setAttribute(\"neatlogs.stream.cancelled\", true);\n span.setStatus({ code: SpanStatusCode.UNSET });\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });\n if (error instanceof Error) span.recordException(error);\n }\n span.end();\n }\n\n private canRetainChoice(index: number): boolean {\n if (this.choices.has(index)) return true;\n if (this.choices.size < DEFAULT_MAX_STREAM_CAPTURE_ITEMS) return true;\n this.markDropped(\"item_limit_exceeded\", 0);\n return false;\n }\n\n private retainString(value: string, retain: (value: string) => void): number {\n const remaining = Math.max(0, DEFAULT_MAX_STREAM_CAPTURE_BYTES - this.capturedBytes);\n const bytes = utf8ByteLength(value, remaining);\n if (this.capturedItems >= DEFAULT_MAX_STREAM_CAPTURE_ITEMS) {\n this.markDropped(\"item_limit_exceeded\", bytes);\n return 0;\n }\n if (bytes > remaining) {\n this.markDropped(\"byte_limit_exceeded\", bytes);\n return 0;\n }\n retain(value);\n this.capturedItems += 1;\n this.capturedBytes += bytes;\n return bytes;\n }\n\n private markDropped(reason: string, bytes: number): void {\n this.droppedItems += 1;\n this.droppedBytes += bytes;\n this.incompleteReasons.add(reason);\n }\n\n private choice(index: number): ChoiceState {\n let choice = this.choices.get(index);\n if (!choice) {\n choice = {\n role: \"assistant\",\n content: [],\n reasoning: [],\n mediaCount: 0,\n finishReason: null,\n toolCalls: new Map(),\n };\n this.choices.set(index, choice);\n }\n return choice;\n }\n\n private addToolFragments(choiceIndex: number, fragments: any): void {\n if (!Array.isArray(fragments)) return;\n for (let position = 0; position < fragments.length; position += 1) {\n const fragment = fragments[position];\n const index = Number.isInteger(fragment?.index)\n ? fragment.index\n : position;\n const tools = this.choice(choiceIndex).toolCalls;\n let tool = tools.get(index);\n if (!tool) {\n if (this.toolCallCount >= DEFAULT_MAX_STREAM_CAPTURE_ITEMS) {\n this.markDropped(\"item_limit_exceeded\", 0);\n continue;\n }\n tool = {\n id: \"\",\n name: \"\",\n arguments: \"\",\n type: \"\",\n details: \"\",\n synthetic: false,\n incomplete: false,\n };\n tools.set(index, tool);\n this.toolCallCount += 1;\n }\n if (fragment?.id && String(fragment.id) !== tool.id) {\n this.retainString(String(fragment.id), (value) => (tool!.id = value));\n }\n if (fragment?.type && String(fragment.type) !== tool.type) {\n this.retainString(String(fragment.type), (value) => (tool!.type = value));\n }\n if (fragment?.function?.name && String(fragment.function.name) !== tool.name) {\n this.retainString(String(fragment.function.name), (value) => (tool!.name = value));\n }\n if (fragment?.function?.arguments != null) {\n const retained = this.retainString(\n stringify(fragment.function.arguments),\n (value) => (tool!.arguments += value),\n );\n if (retained === 0) tool.incomplete = true;\n }\n if (fragment && !fragment.function) {\n const retained = this.retainString(\n stringify(fragment),\n (value) => (tool!.details = value),\n );\n if (retained === 0) tool.incomplete = true;\n }\n }\n }\n\n private captureEnvelope(value: any): void {\n if (value?.usage) {\n const safeUsage = sanitizeMediaPayload(value.usage, \"output\");\n this.retainString(stringify(safeUsage), () => (this.usage = safeUsage));\n }\n if (value?.model && String(value.model) !== this.model) {\n this.retainString(String(value.model), (model) => (this.model = model));\n }\n if (value?.id && String(value.id) !== this.responseId) {\n this.retainString(String(value.id), (id) => (this.responseId = id));\n }\n }\n\n private applyUsage(span: Span): void {\n const usage = this.usage;\n if (!usage) return;\n span.setAttribute(\"neatlogs.llm.usage\", stringify(usage));\n if (usage.prompt_tokens != null)\n span.setAttribute(\"neatlogs.llm.token_count.prompt\", usage.prompt_tokens);\n if (usage.completion_tokens != null)\n span.setAttribute(\n \"neatlogs.llm.token_count.completion\",\n usage.completion_tokens,\n );\n if (usage.total_tokens != null)\n span.setAttribute(\"neatlogs.llm.token_count.total\", usage.total_tokens);\n if (usage.prompt_tokens_details?.cached_tokens != null) {\n span.setAttribute(\n \"neatlogs.llm.token_count.cache_read\",\n usage.prompt_tokens_details.cached_tokens,\n );\n }\n if (usage.completion_tokens_details?.reasoning_tokens != null) {\n span.setAttribute(\n \"neatlogs.llm.token_count.reasoning\",\n usage.completion_tokens_details.reasoning_tokens,\n );\n }\n }\n}\n"],"mappings":"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","names":["SpanStatusCode","otelTrace","otelTrace","createContextKey","ROOT_CONTEXT","SpanStatusCode","AsyncLocalStorage","AsyncLocalStorage","AsyncLocalStorage","neatlogsGlobal","AsyncLocalStorage","logger","logger","logger","createHash","createHash","result","ROOT_CONTEXT","logger","logger","createContextKey","otelTrace","SpanStatusCode","createHash","createHash","SpanStatusCode","SpanStatusCode","value"]}