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/openai-agents.ts","../src/core/provider.ts","../src/core/active-client.ts"],"sourcesContent":["/**\n * Neatlogs OpenAI Agents SDK trace processor.\n *\n * Usage:\n * import { openaiAgentsProcessor } from 'neatlogs/openai-agents';\n * import { addTraceProcessor } from '@openai/agents';\n * addTraceProcessor(openaiAgentsProcessor());\n *\n * Creates spans: WORKFLOW (traces), AGENT (agent runs), LLM (generations), TOOL (function calls).\n */\n\nimport { trace, SpanStatusCode, type Span } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsBaseContext } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.openai_agents';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function openaiAgentsProcessor(): any {\n return new NeatlogsTraceProcessor();\n}\n\n// ---------------------------------------------------------------------------\n// Trace Processor (implements OpenAI Agents SDK TracingProcessor protocol)\n// ---------------------------------------------------------------------------\n\nclass NeatlogsTraceProcessor {\n private _spans: Map<string, Span> = new Map();\n private _startTimes: Map<string, number> = new Map();\n\n // The @openai/agents SDK passes a Trace object: { traceId, name, groupId, metadata }.\n onTraceStart(traceData: any): void {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'WORKFLOW' };\n\n const workflowName = traceData?.name ?? traceData?.workflow_name;\n if (workflowName) attrs['neatlogs.workflow.name'] = workflowName;\n\n const traceId = traceData?.traceId ?? traceData?.trace_id;\n if (traceId) attrs['neatlogs.agent.trace_id'] = String(traceId);\n\n const span = tracer.startSpan('openai_agents.trace', { attributes: attrs }, getNeatlogsBaseContext());\n const key = String(traceId ?? `trace_${Date.now()}`);\n this._spans.set(key, span);\n this._startTimes.set(key, Date.now());\n }\n\n onTraceEnd(traceData: any): void {\n const key = String(traceData?.traceId ?? traceData?.trace_id ?? '');\n const span = this._spans.get(key);\n if (!span) return;\n\n const startTime = this._startTimes.get(key);\n if (startTime) {\n span.setAttribute('neatlogs.metrics.duration_ms', Date.now() - startTime);\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(key);\n this._startTimes.delete(key);\n }\n\n // The SDK passes a Span object: { type, spanId, traceId, parentId?, spanData: {...} }.\n // The meaningful payload (type, name, input, output, usage, ...) lives in `spanData`.\n onSpanStart(span: any): void {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const data = span?.spanData ?? span ?? {};\n const spanType = data?.type ?? data?.span_type ?? span?.type ?? '';\n const spanId = String(span?.spanId ?? span?.span_id ?? data?.span_id ?? `span_${Date.now()}`);\n\n // Parent context: nest under the trace span (or a parent span) so the tree\n // forms. Base is ROOT_CONTEXT in isolated mode so a foreign provider's\n // active span can never leak in as our ancestor.\n const base = getNeatlogsBaseContext();\n const parentKey = String(span?.parentId ?? span?.traceId ?? span?.trace_id ?? '');\n const parentSpan = this._spans.get(parentKey);\n const parentCtx = parentSpan ? trace.setSpan(base, parentSpan) : base;\n\n let otelSpan: Span;\n\n if (spanType === 'agent' || spanType === 'agent_run') {\n const agentName = data?.name ?? data?.agent_name ?? 'agent';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'AGENT',\n 'neatlogs.agent.name': agentName,\n };\n if (Array.isArray(data?.tools) && data.tools.length) {\n attrs['neatlogs.agent.available_tools'] = data.tools.join(',');\n }\n otelSpan = tracer.startSpan(`openai_agents.agent.${agentName}`, { attributes: attrs }, parentCtx);\n\n } else if (spanType === 'response' || spanType === 'generation' || spanType === 'llm') {\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'openai',\n };\n\n const model = data?.model;\n if (model) attrs['neatlogs.llm.model_name'] = model;\n\n const inputMsgs = data?.input ?? data?.messages;\n if (inputMsgs && Array.isArray(inputMsgs)) {\n for (let i = 0; i < inputMsgs.length; i++) {\n const msg = inputMsgs[i];\n const role = typeof msg === 'object' ? (msg.role ?? '') : '';\n const content = typeof msg === 'object' ? (msg.content ?? '') : String(msg);\n if (role) attrs[`neatlogs.llm.input_messages.${i}.role`] = role;\n if (content) attrs[`neatlogs.llm.input_messages.${i}.content`] = (typeof content === 'string' ? content : safeStringify(content));\n }\n }\n\n otelSpan = tracer.startSpan('openai_agents.generation', { attributes: attrs }, parentCtx);\n\n } else if (spanType === 'function' || spanType === 'tool' || spanType === 'tool_call') {\n const toolName = data?.name ?? data?.function_name ?? 'tool';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': toolName,\n };\n\n const toolInput = data?.input ?? data?.arguments;\n if (toolInput !== undefined) {\n attrs['input.value'] = (typeof toolInput === 'string' ? toolInput : safeStringify(toolInput));\n }\n\n otelSpan = tracer.startSpan(`openai_agents.tool.${toolName}`, { attributes: attrs }, parentCtx);\n\n } else if (spanType === 'handoff') {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'AGENT' };\n if (data?.from_agent) attrs['neatlogs.agent.handoff_from'] = String(data.from_agent);\n if (data?.to_agent) attrs['neatlogs.agent.name'] = String(data.to_agent);\n\n otelSpan = tracer.startSpan('openai_agents.handoff', { attributes: attrs }, parentCtx);\n\n } else {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'CHAIN' };\n otelSpan = tracer.startSpan(`openai_agents.${spanType || 'span'}`, { attributes: attrs }, parentCtx);\n }\n\n this._spans.set(spanId, otelSpan);\n this._startTimes.set(spanId, Date.now());\n }\n\n onSpanEnd(span: any): void {\n const data = span?.spanData ?? span ?? {};\n const spanId = String(span?.spanId ?? span?.span_id ?? data?.span_id ?? '');\n const otelSpan = this._spans.get(spanId);\n if (!otelSpan) return;\n\n const spanType = data?.type ?? data?.span_type ?? span?.type ?? '';\n const startTime = this._startTimes.get(spanId);\n\n if (spanType === 'response' || spanType === 'generation' || spanType === 'llm') {\n // @openai/agents 'response' span nests the full Responses API object under\n // `_response` and the request under `_input`. Older shapes use output/usage/model directly.\n const resp = data?._response ?? data?.response ?? {};\n const model = data?.model ?? resp?.model;\n if (model) otelSpan.setAttribute('neatlogs.llm.model_name', model);\n\n // Output text: Responses API output[] has message items with content[].text\n const outputItems = data?.output ?? resp?.output;\n if (Array.isArray(outputItems)) {\n const text = outputItems\n .filter((o: any) => o?.type === 'message' || o?.role === 'assistant')\n .flatMap((o: any) => (Array.isArray(o.content) ? o.content : [o.content]))\n .map((c: any) => (typeof c === 'string' ? c : c?.text ?? ''))\n .join('');\n if (text) {\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.content', text);\n }\n // Tool-call items advertised in the response output\n const toolCalls = outputItems.filter((o: any) => o?.type === 'function_call');\n for (let i = 0; i < toolCalls.length; i++) {\n const tc = toolCalls[i];\n otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.name`, tc.name ?? '');\n otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.arguments`, typeof tc.arguments === 'string' ? tc.arguments : safeStringify(tc.arguments ?? {}));\n if (tc.callId ?? tc.id) otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.id`, tc.callId ?? tc.id);\n }\n } else if (outputItems?.content) {\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n const c = outputItems.content;\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.content', (typeof c === 'string' ? c : safeStringify(c)));\n }\n\n const usage = data?.usage ?? resp?.usage;\n if (usage) {\n const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens;\n const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens;\n const totalTokens = usage.total_tokens ?? usage.totalTokens;\n if (inputTokens != null) otelSpan.setAttribute('neatlogs.llm.token_count.prompt', inputTokens);\n if (outputTokens != null) otelSpan.setAttribute('neatlogs.llm.token_count.completion', outputTokens);\n if (totalTokens != null) otelSpan.setAttribute('neatlogs.llm.token_count.total', totalTokens);\n else if (inputTokens != null && outputTokens != null) {\n otelSpan.setAttribute('neatlogs.llm.token_count.total', inputTokens + outputTokens);\n }\n }\n\n } else if (spanType === 'function' || spanType === 'tool' || spanType === 'tool_call') {\n const output = data?.output ?? data?.result;\n if (output != null) {\n otelSpan.setAttribute('output.value', (typeof output === 'string' ? output : safeStringify(output)));\n }\n\n } else if (spanType === 'agent' || spanType === 'agent_run') {\n const output = data?.output;\n if (output != null) {\n otelSpan.setAttribute('output.value', (typeof output === 'string' ? output : safeStringify(output)));\n }\n }\n\n const error = data?.error ?? span?.error;\n if (error) {\n if (error instanceof Error) {\n otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n otelSpan.recordException(error);\n } else {\n otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });\n }\n } else {\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n }\n\n if (startTime) {\n otelSpan.setAttribute('neatlogs.llm.metrics.duration_ms', Date.now() - startTime);\n }\n\n otelSpan.end();\n this._spans.delete(spanId);\n this._startTimes.delete(spanId);\n }\n\n shutdown(): void {\n for (const [, span] of this._spans) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: 'Processor shutdown before span completed' });\n span.end();\n }\n this._spans.clear();\n this._startTimes.clear();\n }\n\n forceFlush(): void {}\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\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","/**\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"],"mappings":"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","names":["AsyncLocalStorage","neatlogsGlobal","AsyncLocalStorage"]}
1
+ {"version":3,"sources":["../src/openai-agents.ts","../src/core/provider.ts","../src/core/active-client.ts"],"sourcesContent":["/**\n * Neatlogs OpenAI Agents SDK trace processor.\n *\n * Usage:\n * import { openaiAgentsProcessor } from 'neatlogs/openai-agents';\n * import { addTraceProcessor } from '@openai/agents';\n * addTraceProcessor(openaiAgentsProcessor());\n *\n * Creates spans: WORKFLOW (traces), AGENT (agent runs), LLM (generations), TOOL (function calls).\n */\n\nimport { trace, SpanStatusCode, type Span } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsBaseContext } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.openai_agents';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function openaiAgentsProcessor(): any {\n return new NeatlogsTraceProcessor();\n}\n\n// ---------------------------------------------------------------------------\n// Trace Processor (implements OpenAI Agents SDK TracingProcessor protocol)\n// ---------------------------------------------------------------------------\n\nclass NeatlogsTraceProcessor {\n private _spans: Map<string, Span> = new Map();\n private _startTimes: Map<string, number> = new Map();\n\n // The @openai/agents SDK passes a Trace object: { traceId, name, groupId, metadata }.\n onTraceStart(traceData: any): void {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'WORKFLOW' };\n\n const workflowName = traceData?.name ?? traceData?.workflow_name;\n if (workflowName) attrs['neatlogs.workflow.name'] = workflowName;\n\n const traceId = traceData?.traceId ?? traceData?.trace_id;\n if (traceId) attrs['neatlogs.agent.trace_id'] = String(traceId);\n\n const span = tracer.startSpan('openai_agents.trace', { attributes: attrs }, getNeatlogsBaseContext());\n const key = String(traceId ?? `trace_${Date.now()}`);\n this._spans.set(key, span);\n this._startTimes.set(key, Date.now());\n }\n\n onTraceEnd(traceData: any): void {\n const key = String(traceData?.traceId ?? traceData?.trace_id ?? '');\n const span = this._spans.get(key);\n if (!span) return;\n\n const startTime = this._startTimes.get(key);\n if (startTime) {\n span.setAttribute('neatlogs.metrics.duration_ms', Date.now() - startTime);\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(key);\n this._startTimes.delete(key);\n }\n\n // The SDK passes a Span object: { type, spanId, traceId, parentId?, spanData: {...} }.\n // The meaningful payload (type, name, input, output, usage, ...) lives in `spanData`.\n onSpanStart(span: any): void {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const data = span?.spanData ?? span ?? {};\n const spanType = data?.type ?? data?.span_type ?? span?.type ?? '';\n const spanId = String(span?.spanId ?? span?.span_id ?? data?.span_id ?? `span_${Date.now()}`);\n\n // Parent context: nest under the trace span (or a parent span) so the tree\n // forms. Base is ROOT_CONTEXT in isolated mode so a foreign provider's\n // active span can never leak in as our ancestor.\n const base = getNeatlogsBaseContext();\n const parentKey = String(span?.parentId ?? span?.traceId ?? span?.trace_id ?? '');\n const parentSpan = this._spans.get(parentKey);\n const parentCtx = parentSpan ? trace.setSpan(base, parentSpan) : base;\n\n let otelSpan: Span;\n\n if (spanType === 'agent' || spanType === 'agent_run') {\n const agentName = data?.name ?? data?.agent_name ?? 'agent';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'AGENT',\n 'neatlogs.agent.name': agentName,\n };\n if (Array.isArray(data?.tools) && data.tools.length) {\n attrs['neatlogs.agent.available_tools'] = data.tools.join(',');\n }\n otelSpan = tracer.startSpan(`openai_agents.agent.${agentName}`, { attributes: attrs }, parentCtx);\n\n } else if (spanType === 'response' || spanType === 'generation' || spanType === 'llm') {\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'openai',\n };\n\n const model = data?.model;\n if (model) attrs['neatlogs.llm.model_name'] = model;\n\n const inputMsgs = data?.input ?? data?.messages;\n if (inputMsgs && Array.isArray(inputMsgs)) {\n for (let i = 0; i < inputMsgs.length; i++) {\n const msg = inputMsgs[i];\n const role = typeof msg === 'object' ? (msg.role ?? '') : '';\n const content = typeof msg === 'object' ? (msg.content ?? '') : String(msg);\n if (role) attrs[`neatlogs.llm.input_messages.${i}.role`] = role;\n if (content) attrs[`neatlogs.llm.input_messages.${i}.content`] = (typeof content === 'string' ? content : safeStringify(content));\n }\n }\n\n otelSpan = tracer.startSpan('openai_agents.generation', { attributes: attrs }, parentCtx);\n\n } else if (spanType === 'function' || spanType === 'tool' || spanType === 'tool_call') {\n const toolName = data?.name ?? data?.function_name ?? 'tool';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': toolName,\n };\n\n const toolInput = data?.input ?? data?.arguments;\n if (toolInput !== undefined) {\n attrs['input.value'] = (typeof toolInput === 'string' ? toolInput : safeStringify(toolInput));\n }\n\n otelSpan = tracer.startSpan(`openai_agents.tool.${toolName}`, { attributes: attrs }, parentCtx);\n\n } else if (spanType === 'handoff') {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'AGENT' };\n if (data?.from_agent) attrs['neatlogs.agent.handoff_from'] = String(data.from_agent);\n if (data?.to_agent) attrs['neatlogs.agent.name'] = String(data.to_agent);\n\n otelSpan = tracer.startSpan('openai_agents.handoff', { attributes: attrs }, parentCtx);\n\n } else {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'CHAIN' };\n otelSpan = tracer.startSpan(`openai_agents.${spanType || 'span'}`, { attributes: attrs }, parentCtx);\n }\n\n this._spans.set(spanId, otelSpan);\n this._startTimes.set(spanId, Date.now());\n }\n\n onSpanEnd(span: any): void {\n const data = span?.spanData ?? span ?? {};\n const spanId = String(span?.spanId ?? span?.span_id ?? data?.span_id ?? '');\n const otelSpan = this._spans.get(spanId);\n if (!otelSpan) return;\n\n const spanType = data?.type ?? data?.span_type ?? span?.type ?? '';\n const startTime = this._startTimes.get(spanId);\n\n if (spanType === 'response' || spanType === 'generation' || spanType === 'llm') {\n // @openai/agents 'response' span nests the full Responses API object under\n // `_response` and the request under `_input`. Older shapes use output/usage/model directly.\n const resp = data?._response ?? data?.response ?? {};\n const model = data?.model ?? resp?.model;\n if (model) otelSpan.setAttribute('neatlogs.llm.model_name', model);\n\n // Output text: Responses API output[] has message items with content[].text\n const outputItems = data?.output ?? resp?.output;\n if (Array.isArray(outputItems)) {\n const text = outputItems\n .filter((o: any) => o?.type === 'message' || o?.role === 'assistant')\n .flatMap((o: any) => (Array.isArray(o.content) ? o.content : [o.content]))\n .map((c: any) => (typeof c === 'string' ? c : c?.text ?? ''))\n .join('');\n if (text) {\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.content', text);\n }\n // Tool-call items advertised in the response output\n const toolCalls = outputItems.filter((o: any) => o?.type === 'function_call');\n for (let i = 0; i < toolCalls.length; i++) {\n const tc = toolCalls[i];\n otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.name`, tc.name ?? '');\n otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.arguments`, typeof tc.arguments === 'string' ? tc.arguments : safeStringify(tc.arguments ?? {}));\n if (tc.callId ?? tc.id) otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.id`, tc.callId ?? tc.id);\n }\n } else if (outputItems?.content) {\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n const c = outputItems.content;\n otelSpan.setAttribute('neatlogs.llm.output_messages.0.content', (typeof c === 'string' ? c : safeStringify(c)));\n }\n\n const usage = data?.usage ?? resp?.usage;\n if (usage) {\n const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens;\n const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens;\n const totalTokens = usage.total_tokens ?? usage.totalTokens;\n if (inputTokens != null) otelSpan.setAttribute('neatlogs.llm.token_count.prompt', inputTokens);\n if (outputTokens != null) otelSpan.setAttribute('neatlogs.llm.token_count.completion', outputTokens);\n if (totalTokens != null) otelSpan.setAttribute('neatlogs.llm.token_count.total', totalTokens);\n else if (inputTokens != null && outputTokens != null) {\n otelSpan.setAttribute('neatlogs.llm.token_count.total', inputTokens + outputTokens);\n }\n }\n\n } else if (spanType === 'function' || spanType === 'tool' || spanType === 'tool_call') {\n const output = data?.output ?? data?.result;\n if (output != null) {\n otelSpan.setAttribute('output.value', (typeof output === 'string' ? output : safeStringify(output)));\n }\n\n } else if (spanType === 'agent' || spanType === 'agent_run') {\n const output = data?.output;\n if (output != null) {\n otelSpan.setAttribute('output.value', (typeof output === 'string' ? output : safeStringify(output)));\n }\n }\n\n const error = data?.error ?? span?.error;\n if (error) {\n if (error instanceof Error) {\n otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n otelSpan.recordException(error);\n } else {\n otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });\n }\n } else {\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n }\n\n if (startTime) {\n otelSpan.setAttribute('neatlogs.llm.metrics.duration_ms', Date.now() - startTime);\n }\n\n otelSpan.end();\n this._spans.delete(spanId);\n this._startTimes.delete(spanId);\n }\n\n shutdown(): void {\n for (const [, span] of this._spans) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: 'Processor shutdown before span completed' });\n span.end();\n }\n this._spans.clear();\n this._startTimes.clear();\n }\n\n forceFlush(): void {}\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\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","/**\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"],"mappings":"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","names":["AsyncLocalStorage","neatlogsGlobal","AsyncLocalStorage"]}
package/dist/openai.cjs CHANGED
@@ -180,11 +180,11 @@ function getActiveNeatlogsSpan() {
180
180
  function getNeatlogsParentContext(baseContext) {
181
181
  return baseContext ?? getNeatlogsActiveContext();
182
182
  }
183
- function withNeatlogsSpan(span, fn, baseContext) {
183
+ function withNeatlogsSpan(span, fn, baseContext, rootSpan) {
184
184
  const base = baseContext ?? getNeatlogsActiveContext();
185
185
  let ctx = import_api.trace.setSpan(base, span);
186
186
  if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === void 0) {
187
- ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);
187
+ ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, rootSpan ?? span);
188
188
  }
189
189
  return privateContextStorage.run(ctx, fn);
190
190
  }
@@ -926,7 +926,8 @@ var USER_PROMPT_TEMPLATE_KEY = (0, import_api3.createContextKey)("neatlogs.user_
926
926
  var USER_PROMPT_VARIABLES_KEY = (0, import_api3.createContextKey)("neatlogs.user_prompt_variables");
927
927
 
928
928
  // src/core/auto-root.ts
929
- var ROOT_KINDS = /* @__PURE__ */ new Set(["workflow", "chain", "agent", "mcp_tool"]);
929
+ var boundAutoRoots = /* @__PURE__ */ new WeakMap();
930
+ var ROOT_KINDS = /* @__PURE__ */ new Set(["workflow", "chain", "agent", "mcp_tool", "llm"]);
930
931
  function autoRootEnabled() {
931
932
  const val = (process.env.NEATLOGS_AUTO_ROOT ?? "").trim().toLowerCase();
932
933
  return !["false", "0", "no", "off"].includes(val);
@@ -941,11 +942,12 @@ function resolveRootWorkflowName() {
941
942
  }
942
943
  return "workflow";
943
944
  }
944
- function stampAutoRootIdentity(root) {
945
+ function stampRootIdentity(root) {
945
946
  applySessionAttributes(root, void 0, true);
946
947
  applyEndUserAttributes(root, void 0, void 0, true);
947
948
  }
948
- function wrapSpanWithRoot(child, root) {
949
+ function bindSpanToAutoRoot(child, root) {
950
+ if (!root) return child;
949
951
  let ended = false;
950
952
  const proxy = new Proxy(child, {
951
953
  get(target, prop, _receiver) {
@@ -967,6 +969,7 @@ function wrapSpanWithRoot(child, root) {
967
969
  return typeof value === "function" ? value.bind(target) : value;
968
970
  }
969
971
  });
972
+ boundAutoRoots.set(proxy, root);
970
973
  aliasMediaCaptureSpan(proxy, child);
971
974
  return proxy;
972
975
  }
@@ -979,27 +982,41 @@ var AutoRootTracer = class {
979
982
  const kind = String(
980
983
  options?.attributes?.["neatlogs.span.kind"] ?? ""
981
984
  ).toLowerCase();
985
+ const hasExplicitContext = context !== void 0;
982
986
  const ctx = context ?? getNeatlogsParentContext();
983
987
  const parent = getActiveNeatlogsSpan();
984
- const needsRoot = autoRootEnabled() && !ROOT_KINDS.has(kind) && !(parent !== void 0 && parent.isRecording());
988
+ const isParentless = !hasExplicitContext && !(parent !== void 0 && parent.isRecording());
989
+ const needsRoot = autoRootEnabled() && !ROOT_KINDS.has(kind) && isParentless;
985
990
  if (!needsRoot) {
986
- return this._tracer.startSpan(name, options, ctx);
991
+ const span = this._tracer.startSpan(name, options, ctx);
992
+ if (isParentless && ROOT_KINDS.has(kind)) stampRootIdentity(span);
993
+ return span;
987
994
  }
988
995
  const root = this._tracer.startSpan(
989
996
  resolveRootWorkflowName(),
990
997
  { attributes: { "neatlogs.span.kind": "workflow", "neatlogs.auto_root": true } },
991
998
  ctx
992
999
  );
993
- stampAutoRootIdentity(root);
1000
+ stampRootIdentity(root);
994
1001
  const childCtx = import_api4.trace.setSpan(ctx, root);
995
1002
  const child = this._tracer.startSpan(name, options, childCtx);
996
- return wrapSpanWithRoot(child, root);
1003
+ return bindSpanToAutoRoot(child, root);
997
1004
  }
998
- // startActiveSpan is used by traceTool (callback-scoped, always under an
999
- // active span in practice) — pass straight through, no auto-root.
1000
- startActiveSpan = ((...args) => this._tracer.startActiveSpan(
1001
- ...args
1002
- ));
1005
+ startActiveSpan = ((name, ...args) => {
1006
+ const callback = args.at(-1);
1007
+ if (typeof callback !== "function") {
1008
+ throw new TypeError("startActiveSpan requires a callback");
1009
+ }
1010
+ const options = args.length >= 2 ? args[0] : void 0;
1011
+ const parentContext = args.length >= 3 ? args[1] : getNeatlogsParentContext();
1012
+ const span = args.length >= 3 ? this.startSpan(name, options, parentContext) : this.startSpan(name, options);
1013
+ return withNeatlogsSpan(
1014
+ span,
1015
+ () => callback(span),
1016
+ parentContext,
1017
+ boundAutoRoots.get(span)
1018
+ );
1019
+ });
1003
1020
  };
1004
1021
  function getProviderTracer(name) {
1005
1022
  return new AutoRootTracer(getNeatlogsTracer(name));