neatlogs 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-sdk.cjs +5 -1
- package/dist/ai-sdk.cjs.map +1 -1
- package/dist/ai-sdk.mjs +6 -1
- package/dist/ai-sdk.mjs.map +1 -1
- package/dist/anthropic.cjs +5 -1
- package/dist/anthropic.cjs.map +1 -1
- package/dist/anthropic.mjs +12 -7
- package/dist/anthropic.mjs.map +1 -1
- package/dist/azure-openai.cjs +5 -1
- package/dist/azure-openai.cjs.map +1 -1
- package/dist/azure-openai.mjs +12 -7
- package/dist/azure-openai.mjs.map +1 -1
- package/dist/bedrock.cjs +5 -1
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.mjs +12 -7
- package/dist/bedrock.mjs.map +1 -1
- package/dist/claude-agent-sdk.cjs +5 -1
- package/dist/claude-agent-sdk.cjs.map +1 -1
- package/dist/claude-agent-sdk.mjs +6 -1
- package/dist/claude-agent-sdk.mjs.map +1 -1
- package/dist/google-genai.cjs +5 -1
- package/dist/google-genai.cjs.map +1 -1
- package/dist/google-genai.mjs +12 -7
- package/dist/google-genai.mjs.map +1 -1
- package/dist/index.cjs +48 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +25 -2
- package/dist/index.mjs +56 -17
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +1 -0
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.mjs +8 -6
- package/dist/langchain.mjs.map +1 -1
- package/dist/mastra-wrap.cjs +17 -1
- package/dist/mastra-wrap.cjs.map +1 -1
- package/dist/mastra-wrap.mjs +18 -1
- package/dist/mastra-wrap.mjs.map +1 -1
- package/dist/openai-agents.cjs +1 -0
- package/dist/openai-agents.cjs.map +1 -1
- package/dist/openai-agents.mjs +2 -0
- package/dist/openai-agents.mjs.map +1 -1
- package/dist/openai.cjs +5 -1
- package/dist/openai.cjs.map +1 -1
- package/dist/openai.mjs +12 -7
- package/dist/openai.mjs.map +1 -1
- package/dist/opencode-plugin.cjs +17 -8
- package/dist/opencode-plugin.cjs.map +1 -1
- package/dist/opencode-plugin.d.ts +10 -6
- package/dist/opencode-plugin.mjs +17 -8
- package/dist/opencode-plugin.mjs.map +1 -1
- package/dist/openrouter-agent.cjs +5 -1
- package/dist/openrouter-agent.cjs.map +1 -1
- package/dist/openrouter-agent.mjs +12 -7
- package/dist/openrouter-agent.mjs.map +1 -1
- package/dist/pi-agent.cjs +1 -0
- package/dist/pi-agent.cjs.map +1 -1
- package/dist/pi-agent.mjs +2 -0
- package/dist/pi-agent.mjs.map +1 -1
- package/dist/vertex-ai.cjs +5 -1
- package/dist/vertex-ai.cjs.map +1 -1
- package/dist/vertex-ai.mjs +12 -7
- package/dist/vertex-ai.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/claude-agent-sdk.ts","../src/core/provider.ts"],"sourcesContent":["/**\n * Neatlogs Claude Agent SDK integration.\n *\n * Wraps Anthropic's `@anthropic-ai/claude-agent-sdk` so every `query()` run is\n * traced. The SDK's `query()` returns an async-iterable of SDKMessages\n * (system init → assistant (text + tool_use) → user (tool_result) → … →\n * result). Each message carries `parent_tool_use_id`: null for the main\n * (orchestrator) agent, or the id of the spawning Task tool call for a subagent.\n * We translate that into a neatlogs span tree:\n *\n * AGENT claude_agent.query (orchestrator = trace root)\n * ↳ LLM orchestrator turn (one per model turn; text + tool_calls)\n * ↳ TOOL Read / Edit / Bash …\n * ↳ TOOL Task (spawns a subagent)\n * ↳ AGENT claude_agent.subagent.<type> (each subagent, nested)\n * ↳ LLM subagent turn\n * ↳ TOOL subagent tool call\n *\n * The orchestrator is the single root AGENT span — there is NO redundant WORKFLOW\n * wrapper. Subagents (Task-tool invocations, e.g. the wizard's parallel per-file\n * edits) get their own AGENT spans nested under the Task TOOL span that spawned\n * them, so a multi-agent run is represented faithfully and each agent's I/O is\n * distinct.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { wrapClaudeAgentSDK } from 'neatlogs/claude-agent-sdk';\n * import * as claudeAgentSDK from '@anthropic-ai/claude-agent-sdk';\n *\n * await init({ apiKey, workflowName });\n * const { query } = wrapClaudeAgentSDK(claudeAgentSDK);\n * for await (const msg of query({ prompt: 'Hello', options: {...} })) { ... }\n *\n * Conversation tracking: Claude's `session_id` is captured on the root AGENT span\n * as `neatlogs.conversation.id`. Tool calls are traced only through the wrapped\n * `query` — calling the unwrapped SDK directly produces no tracing.\n */\n\nimport { trace, SpanStatusCode, type Span, type Context } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsBaseContext, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.claude_agent_sdk';\nconst ROOT_SCOPE = '__root__';\n\nexport interface WrapClaudeAgentSDKOptions {\n /** Logical grouping for traces (also settable globally via init({ workflowName })). */\n workflowName?: string;\n}\n\n/**\n * Wrap the Claude Agent SDK module. Returns a shallow copy of the module with an\n * instrumented `query`; all other exports (createSdkMcpServer, tool, the built-in\n * Tool helpers, etc.) are passed through unchanged.\n */\nexport function wrapClaudeAgentSDK<T extends Record<string, any>>(\n sdk: T,\n options: WrapClaudeAgentSDKOptions = {},\n): T {\n if (!sdk || typeof sdk.query !== 'function') return sdk;\n if ((sdk as any)._neatlogsWrapped) return sdk;\n\n const wrapped: Record<string, any> = { ...sdk };\n wrapped.query = wrapQuery(sdk.query.bind(sdk), options);\n\n try {\n Object.defineProperty(wrapped, '_neatlogsWrapped', {\n value: true,\n enumerable: false,\n configurable: true,\n });\n } catch {\n wrapped._neatlogsWrapped = true;\n }\n\n return wrapped as T;\n}\n\n// ---------------------------------------------------------------------------\n// query() wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapQuery(original: (...args: any[]) => any, options: WrapClaudeAgentSDKOptions) {\n return function (params: any, ...rest: any[]): any {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const workflowName = options.workflowName ?? 'claude_agent.query';\n\n // Shared ref the input-tap fills with the first user-prompt text. In\n // streaming-input mode the prompt is an async generator fed into the SDK\n // subprocess and is NOT echoed back as a `user` output message before the\n // first assistant turn — so without tapping it, the first LLM span has no\n // input. The tap reads prompt text as the SDK pulls it.\n const promptRef: { text: string } = { text: '' };\n\n // The orchestrator AGENT is the trace ROOT (no WORKFLOW wrapper — a single\n // query() is one agent run; subagents nest below as their own AGENT spans).\n const agentSpan = tracer.startSpan(\n 'claude_agent.query',\n { attributes: { 'neatlogs.span.kind': 'AGENT', 'neatlogs.workflow.name': workflowName } },\n getNeatlogsBaseContext(),\n );\n\n // Input: a string prompt is captured directly. A streaming-input prompt (an\n // async iterable) is tapped — promptRef.text fills as the SDK pulls the\n // first user message — so the agent input and the first LLM span's input are\n // populated even before any `user` message echoes back through the output.\n const promptText = extractPromptText(params?.prompt);\n if (promptText) {\n promptRef.text = promptText;\n agentSpan.setAttribute('input.value', promptText);\n } else if (params && isAsyncIterable(params.prompt)) {\n params = { ...params, prompt: tapPromptStream(params.prompt, promptRef) };\n }\n\n const agentCtx = trace.setSpan(getNeatlogsBaseContext(), agentSpan);\n\n // Call the original query with the AGENT span active only under our private\n // context so a foreign provider's spans neither parent nor nest under ours.\n const queryObj = withNeatlogsSpan(agentSpan, () => original(params, ...rest));\n\n return instrumentQueryIterable(queryObj, agentSpan, agentCtx, tracer, promptRef);\n };\n}\n\n/** True for an async iterable (streaming-input prompt). */\nfunction isAsyncIterable(v: any): boolean {\n return Boolean(v) && typeof v[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Pass-through wrapper over a streaming-input prompt that records the first\n * user message's text into `ref` as the SDK consumes it. Never alters what the\n * SDK receives.\n */\nasync function* tapPromptStream(prompt: any, ref: { text: string }): AsyncGenerator<any> {\n // userMessageText is fully defensive (returns '' for any non-text shape), so\n // no try/catch is needed — the message is always yielded through untouched.\n for await (const message of prompt) {\n if (!ref.text) {\n const text = userMessageText(message);\n if (text) ref.text = text;\n }\n yield message;\n }\n}\n\ninterface ToolCallAccum {\n id: string;\n name: string;\n input: unknown;\n}\n\ninterface AssistantTurnBuffer {\n textParts: string[];\n thinkingParts: string[];\n toolCalls: ToolCallAccum[];\n usage: any;\n model?: string;\n stopReason?: string;\n}\n\n/**\n * One agent's tracing scope. There is always a root scope (the orchestrator,\n * keyed ROOT_SCOPE). Each subagent — identified by the `parent_tool_use_id` of\n * the Task tool call that spawned it — gets its own scope created lazily, with\n * its AGENT span nested under that Task TOOL span. Per-scope state keeps each\n * agent's conversation/turn buffer independent.\n */\ninterface AgentScope {\n /** The AGENT span for this scope (root = orchestrator; others = subagents). */\n span: Span;\n /** OTel context whose active span is this scope's AGENT span (children nest here). */\n ctx: Context;\n /** Running conversation (user/tool/assistant turns) — each LLM span's input. */\n inputMessages: Array<{ role: string; content: string }>;\n /** In-progress model turn, coalesced from multiple `assistant` messages. */\n assistantBuffer: AssistantTurnBuffer | null;\n /** Last assistant text seen (subagent output = its final text). */\n finalText: string;\n /** Whether input.value has been set on this scope's AGENT span. */\n inputCaptured: boolean;\n}\n\ninterface QueryState {\n /** TOOL spans keyed by tool_use_id, closed by the matching tool_result. */\n toolSpans: Map<string, Span>;\n /** Agent scopes keyed by parent_tool_use_id (ROOT_SCOPE for the orchestrator). */\n scopes: Map<string, AgentScope>;\n sessionId?: string;\n model?: string;\n finished: boolean;\n /** Lazily-filled prompt text from a streaming-input tap. */\n promptRef: { text: string };\n}\n\n/**\n * Wrap the Query object so iteration is instrumented while preserving its own\n * methods (interrupt, setPermissionMode, …). The SDK returns an async-iterable\n * object, not a bare generator.\n */\nfunction instrumentQueryIterable(\n queryObj: any,\n agentSpan: Span,\n agentCtx: Context,\n tracer: ReturnType<typeof trace.getTracer>,\n promptRef: { text: string },\n): any {\n const originalAsyncIterator = queryObj?.[Symbol.asyncIterator]?.bind(queryObj);\n if (!originalAsyncIterator) {\n // Not iterable — nothing to trace; close the root span immediately.\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n return queryObj;\n }\n\n // The root scope is the orchestrator agent. Seed its conversation with the\n // prompt (known up front for string prompts; filled by the tap otherwise).\n const rootScope: AgentScope = {\n span: agentSpan,\n ctx: agentCtx,\n inputMessages: promptRef.text ? [{ role: 'user', content: promptRef.text }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(promptRef.text),\n };\n const state: QueryState = {\n toolSpans: new Map(),\n scopes: new Map([[ROOT_SCOPE, rootScope]]),\n finished: false,\n promptRef,\n };\n\n const finalizeAgent = (status: 'ok' | 'error', err?: unknown) => {\n if (state.finished) return;\n state.finished = true;\n // Flush every scope's in-progress turn, then close subagent AGENT spans\n // (deepest-first) and finally the root.\n for (const scope of state.scopes.values()) flushAssistantTurn(tracer, scope, state);\n // Close any tool spans that never got a matching result.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n // Close subagent scopes first (any still open), then the root.\n for (const [key, scope] of state.scopes) {\n if (key === ROOT_SCOPE) continue;\n closeScope(scope, 'ok');\n }\n if (rootScope.finalText) agentSpan.setAttribute('output.value', rootScope.finalText);\n if (status === 'error') {\n recordError(agentSpan, err);\n } else {\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n }\n };\n\n const wrapped = Object.create(Object.getPrototypeOf(queryObj));\n Object.assign(wrapped, queryObj);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await withNeatlogsSpan(agentSpan, () => iterator.next());\n if (result.done) {\n finalizeAgent('ok');\n return result;\n }\n try {\n handleMessage(tracer, state, result.value, finalizeAgent);\n } catch {\n /* never let tracing break the run */\n }\n return result;\n } catch (err) {\n finalizeAgent('error', err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeAgent('ok');\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n finalizeAgent('error', err);\n if (iterator.throw) return iterator.throw(err);\n throw err;\n },\n };\n };\n\n return wrapped;\n}\n\n/** End a subagent scope's AGENT span, setting its output to the subagent's final text. */\nfunction closeScope(scope: AgentScope, status: 'ok' | 'error'): void {\n try {\n if (scope.finalText) scope.span.setAttribute('output.value', scope.finalText);\n scope.span.setStatus({ code: status === 'ok' ? SpanStatusCode.OK : SpanStatusCode.ERROR });\n scope.span.end();\n } catch {\n /* ignore */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message handling\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the agent scope for a message. `parent_tool_use_id` is null for the\n * orchestrator (root scope) and the spawning Task tool_use_id for a subagent.\n * Subagent scopes are created lazily, with their AGENT span nested under the\n * Task TOOL span (looked up by that id) so the hierarchy is\n * orchestrator → Task TOOL → subagent AGENT.\n */\nfunction getScope(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n): AgentScope {\n const parentId = msg?.parent_tool_use_id ?? null;\n if (!parentId) return state.scopes.get(ROOT_SCOPE)!;\n\n const existing = state.scopes.get(parentId);\n if (existing) return existing;\n\n // New subagent. Its spawning Task tool_use may still be buffered in the root\n // turn (the SDK emits subagent messages before the Task's tool_result closes\n // the parent turn). Flush the root turn first so the Task TOOL span exists and\n // this subagent AGENT can nest under it.\n const root = state.scopes.get(ROOT_SCOPE)!;\n if (!state.toolSpans.has(parentId) && root.assistantBuffer) {\n flushAssistantTurn(tracer, root, state);\n }\n\n // Nest the subagent AGENT span under the spawning Task TOOL span if we have it;\n // otherwise under the root agent.\n const parentToolSpan = state.toolSpans.get(parentId);\n const parentCtx = parentToolSpan\n ? trace.setSpan(getNeatlogsBaseContext(), parentToolSpan)\n : state.scopes.get(ROOT_SCOPE)!.ctx;\n\n const subType = msg?.subagent_type ? String(msg.subagent_type) : 'subagent';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'AGENT',\n 'neatlogs.agent.name': subType,\n };\n if (msg?.task_description) attrs['input.value'] = String(msg.task_description);\n const span = tracer.startSpan(`claude_agent.subagent.${subType}`, { attributes: attrs }, parentCtx);\n\n const scope: AgentScope = {\n span,\n ctx: trace.setSpan(getNeatlogsBaseContext(), span),\n inputMessages: msg?.task_description ? [{ role: 'user', content: String(msg.task_description) }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(msg?.task_description),\n };\n state.scopes.set(parentId, scope);\n return scope;\n}\n\nfunction handleMessage(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n finalizeAgent: (status: 'ok' | 'error', err?: unknown) => void,\n): void {\n if (!msg || typeof msg !== 'object') return;\n\n const rootScope = state.scopes.get(ROOT_SCOPE)!;\n\n // Backfill root input from the tapped streaming-input prompt as soon as it's\n // available (it fills before the first assistant turn).\n if (!rootScope.inputCaptured && state.promptRef.text) {\n rootScope.inputCaptured = true;\n rootScope.span.setAttribute('input.value', state.promptRef.text);\n }\n\n switch (msg.type) {\n case 'system': {\n // init message — carries session_id, model, available tools.\n if (msg.session_id) {\n state.sessionId = msg.session_id;\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n if (msg.model) {\n state.model = msg.model;\n rootScope.span.setAttribute('neatlogs.agent.model', String(msg.model));\n }\n break;\n }\n\n case 'user': {\n // A `user` message (prompt, or tool_result turns) is a turn boundary for\n // its scope: flush the buffered turn as ONE LLM span first.\n const scope = getScope(tracer, state, msg);\n flushAssistantTurn(tracer, scope, state);\n\n const userText = userMessageText(msg);\n if (userText) {\n if (!scope.inputCaptured) {\n scope.inputCaptured = true;\n scope.span.setAttribute('input.value', userText);\n }\n scope.inputMessages.push({ role: 'user', content: userText });\n }\n closeToolSpansFromUser(state, scope, msg);\n break;\n }\n\n case 'assistant': {\n // Don't emit yet — the SDK delivers one model turn as multiple `assistant`\n // messages (text block, then tool_use block, …). Buffer them per scope;\n // the next user/result boundary flushes the turn as a single LLM span.\n const scope = getScope(tracer, state, msg);\n bufferAssistantMessage(scope, msg);\n break;\n }\n\n case 'result': {\n // The run is complete. Flush the root turn (a final text answer may have no\n // trailing user message), then finalize.\n flushAssistantTurn(tracer, rootScope, state);\n\n const text = typeof msg.result === 'string' ? msg.result : '';\n if (text) rootScope.finalText = text;\n if (msg.session_id && !state.sessionId) {\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n const usage = msg.usage;\n if (usage) setUsage(rootScope.span, usage);\n if (msg.total_cost_usd != null) rootScope.span.setAttribute('neatlogs.agent.cost_usd', msg.total_cost_usd);\n if (msg.num_turns != null) rootScope.span.setAttribute('neatlogs.agent.num_turns', msg.num_turns);\n if (msg.is_error) rootScope.span.setAttribute('neatlogs.agent.is_error', true);\n\n finalizeAgent(msg.is_error ? 'error' : 'ok', msg.is_error ? new Error(String(text || 'agent run failed')) : undefined);\n break;\n }\n\n default:\n break;\n }\n}\n\n/**\n * Append one `assistant` SDK message to the in-progress turn buffer. The SDK\n * splits a single model turn into multiple assistant messages (a text block,\n * then tool_use blocks); they share token usage. We merge their text, thinking,\n * and tool_use blocks so the turn becomes ONE LLM span on flush.\n */\nfunction bufferAssistantMessage(scope: AgentScope, msg: any): void {\n const message = msg.message ?? msg;\n const content = message?.content ?? [];\n\n if (!scope.assistantBuffer) {\n scope.assistantBuffer = { textParts: [], thinkingParts: [], toolCalls: [], usage: undefined };\n }\n const buf = scope.assistantBuffer;\n if (message?.model) buf.model = message.model;\n if (message?.stop_reason) buf.stopReason = message.stop_reason;\n // Usage is reported per assistant message but is the SAME turn total — keep\n // the largest/last non-empty one rather than summing (summing double-counts).\n if (message?.usage) buf.usage = message.usage;\n\n for (const block of Array.isArray(content) ? content : []) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') buf.textParts.push(block.text);\n else if (block.type === 'thinking' && typeof block.thinking === 'string') buf.thinkingParts.push(block.thinking);\n else if (block.type === 'tool_use') {\n buf.toolCalls.push({ id: block.id ?? '', name: block.name ?? '', input: block.input });\n }\n }\n}\n\n/**\n * Emit the buffered model turn as a SINGLE LLM span, then open TOOL spans for\n * its tool calls. No-op if no turn is buffered. This is what makes one LLM span\n * per real model turn (not per SDK assistant message).\n */\nfunction flushAssistantTurn(\n tracer: ReturnType<typeof trace.getTracer>,\n scope: AgentScope,\n state: QueryState,\n): void {\n const buf = scope.assistantBuffer;\n if (!buf) return;\n scope.assistantBuffer = null;\n\n const model = buf.model ?? state.model ?? '';\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n };\n if (model) attrs['neatlogs.llm.model_name'] = String(model);\n\n // If this is the root scope's first turn and no user message was recorded yet\n // (streaming-input mode), seed input from the tapped prompt text.\n if (scope.inputMessages.length === 0 && state.promptRef.text) {\n scope.inputMessages.push({ role: 'user', content: state.promptRef.text });\n }\n\n // Input = the exact accumulated conversation up to this turn. Emitted BOTH as\n // structured indexed input_messages.* AND as the flat `input.value` blob —\n // per neatlogs/config/attribute-mapping.json, the main UI panel renders\n // `neatlogs.{span_kind}.input` (mapped from `input.value`); the indexed\n // messages alone do NOT populate it. Without input.value the LLM Input is blank.\n scope.inputMessages.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;\n });\n if (scope.inputMessages.length) {\n attrs['input.value'] = safeStringify({ messages: scope.inputMessages });\n }\n\n // Output = the turn's actual assistant content. Prefer the model's text; for a\n // tool-only turn (no text) the output IS the tool call(s), so render them as\n // the exact `name(arguments)` the model emitted (not a vague summary). The\n // structured tool_calls.* below still carry the same data for programmatic use.\n // `output.value` is the flat blob the UI maps to `neatlogs.{span_kind}.output`.\n const outText = buf.textParts.join('');\n const outValue =\n outText ||\n buf.toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.input ?? {})})`).join('\\n');\n if (outValue) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = outValue;\n attrs['output.value'] = outValue;\n }\n if (buf.thinkingParts.length) {\n attrs['neatlogs.llm.output_messages.0.thinking'] = buf.thinkingParts.join('');\n }\n buf.toolCalls.forEach((tc, j) => {\n attrs[`neatlogs.llm.tool_calls.${j}.id`] = tc.id;\n attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.input ?? {});\n });\n if (buf.stopReason) attrs['neatlogs.llm.finish_reason'] = String(buf.stopReason);\n\n // The LLM span nests under THIS scope's AGENT span.\n const span = tracer.startSpan(`claude_agent.llm.${model || 'model'}`, { attributes: attrs }, scope.ctx);\n if (buf.usage) setUsage(span, buf.usage);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n\n // Track this scope's latest text as its output (the subagent/orchestrator\n // final answer is the last assistant text).\n if (outText) scope.finalText = outText;\n\n // Record this assistant turn so the NEXT turn's LLM span sees it as context.\n const turnParts: string[] = [];\n if (outText) turnParts.push(outText);\n for (const tc of buf.toolCalls) turnParts.push(`[tool_call ${tc.name} ${safeStringify(tc.input ?? {})}]`);\n if (turnParts.length) scope.inputMessages.push({ role: 'assistant', content: turnParts.join('\\n') });\n\n // Open TOOL spans for this turn's tool calls, nested under THIS scope's AGENT\n // span (closed by their tool_result). A Task tool's id becomes the key a\n // subagent's messages resolve to (see getScope).\n for (const tc of buf.toolCalls) {\n const toolSpan = tracer.startSpan(\n `claude_agent.tool.${tc.name || 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': String(tc.name ?? ''),\n ...(tc.id ? { 'neatlogs.tool_call.id': String(tc.id) } : {}),\n 'input.value': safeStringify(tc.input ?? {}),\n },\n },\n scope.ctx,\n );\n if (tc.id) state.toolSpans.set(tc.id, toolSpan);\n }\n}\n\nfunction closeToolSpansFromUser(state: QueryState, scope: AgentScope, msg: any): void {\n const content = (msg.message ?? msg)?.content ?? [];\n for (const block of Array.isArray(content) ? content : []) {\n if (block?.type !== 'tool_result') continue;\n const id = block.tool_use_id ?? '';\n const out = block.content;\n const outText = (typeof out === 'string' ? out : safeStringify(out));\n // Feed the tool result into THIS scope's conversation so the next LLM span's\n // input reflects what the model actually saw.\n if (outText) scope.inputMessages.push({ role: 'tool', content: outText });\n const span = state.toolSpans.get(id);\n if (!span) continue;\n span.setAttribute('output.value', outText);\n if (block.is_error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n state.toolSpans.delete(id);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the human/text content of a `user` SDK message — the prompt, not\n * tool_result blocks (those are handled separately as tool outputs).\n */\nfunction userMessageText(msg: any): string {\n const content = (msg.message ?? msg)?.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string') {\n parts.push(block.text);\n }\n }\n return parts.join('\\n');\n}\n\nfunction setUsage(span: Span, usage: any): void {\n if (!usage) return;\n if (usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.input_tokens);\n if (usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.output_tokens);\n if (usage.input_tokens != null && usage.output_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.total', usage.input_tokens + usage.output_tokens);\n }\n if (usage.cache_read_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.cache_read_input_tokens);\n }\n if (usage.cache_creation_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_write', usage.cache_creation_input_tokens);\n }\n}\n\nfunction extractPromptText(prompt: any): string {\n if (typeof prompt === 'string') return prompt;\n // Streaming-input mode passes an async iterable of messages; we can't read it\n // synchronously without consuming it, so leave input.value unset in that case.\n return '';\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return 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 * 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 trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\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 return providerState.provider?.getTracer(name) ?? preInitTracer;\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 return providerState.provider;\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 * 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 const ctx = otelTrace.setSpan(base, span);\n return privateContextStorage.run(ctx, fn);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAsCA,IAAAA,cAA+D;;;AC7B/D,8BAAkC;AAClC,iBASO;AASP,IAAM,2BAA2B,uBAAO;AAAA,EACtC;AACF;AACA,IAAM,6BAA6B,uBAAO;AAAA,EACxC;AACF;AAQA,IAAM,iBAAiB;AAKvB,IAAM,wBACJ,eAAe,wBAAwB,MACtC,eAAe,wBAAwB,IAAI,IAAI,0CAA2B;AAC7E,IAAM,gBACJ,eAAe,0BAA0B,MACxC,eAAe,0BAA0B,IAAI;AAAA,EAC5C,UAAU;AACZ;AAIF,IAAM,gBAAwB;AAAA,EAC5B,YAAkB;AAChB,WAAO,WAAAC,MAAU,gBAAgB,+BAAoB;AAAA,EACvD;AAAA,EACA,gBACE,OACA,MACA,MACA,MACe;AACf,UAAM,KACJ,OAAO,SAAS,aACZ,OACA,OAAO,SAAS,aACd,OACA;AACR,WAAO,GAAI,WAAAA,MAAU,gBAAgB,+BAAoB,CAAC;AAAA,EAC5D;AACF;AAQO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,cAAc,UAAU,UAAU,IAAI,KAAK;AACpD;AA+DO,SAAS,2BAAoC;AAClD,SAAO,sBAAsB,SAAS,KAAK;AAC7C;AA+BO,SAAS,uBAAuB,aAAgC;AACrE,SAAO,eAAe,yBAAyB;AACjD;AAqBO,SAAS,iBACd,MACA,IACA,aACG;AACH,QAAM,OAAO,eAAe,yBAAyB;AACrD,QAAM,MAAM,WAAAC,MAAU,QAAQ,MAAM,IAAI;AACxC,SAAO,sBAAsB,IAAI,KAAK,EAAE;AAC1C;;;AD3KA,IAAM,cAAc;AACpB,IAAM,aAAa;AAYZ,SAAS,mBACd,KACA,UAAqC,CAAC,GACnC;AACH,MAAI,CAAC,OAAO,OAAO,IAAI,UAAU,WAAY,QAAO;AACpD,MAAK,IAAY,iBAAkB,QAAO;AAE1C,QAAM,UAA+B,EAAE,GAAG,IAAI;AAC9C,UAAQ,QAAQ,UAAU,IAAI,MAAM,KAAK,GAAG,GAAG,OAAO;AAEtD,MAAI;AACF,WAAO,eAAe,SAAS,oBAAoB;AAAA,MACjD,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB,CAAC;AAAA,EACH,QAAQ;AACN,YAAQ,mBAAmB;AAAA,EAC7B;AAEA,SAAO;AACT;AAMA,SAAS,UAAU,UAAmC,SAAoC;AACxF,SAAO,SAAU,WAAgB,MAAkB;AACjD,UAAM,SAAS,kBAAkB,WAAW;AAC5C,UAAM,eAAe,QAAQ,gBAAgB;AAO7C,UAAM,YAA8B,EAAE,MAAM,GAAG;AAI/C,UAAM,YAAY,OAAO;AAAA,MACvB;AAAA,MACA,EAAE,YAAY,EAAE,sBAAsB,SAAS,0BAA0B,aAAa,EAAE;AAAA,MACxF,uBAAuB;AAAA,IACzB;AAMA,UAAM,aAAa,kBAAkB,QAAQ,MAAM;AACnD,QAAI,YAAY;AACd,gBAAU,OAAO;AACjB,gBAAU,aAAa,eAAe,UAAU;AAAA,IAClD,WAAW,UAAU,gBAAgB,OAAO,MAAM,GAAG;AACnD,eAAS,EAAE,GAAG,QAAQ,QAAQ,gBAAgB,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC1E;AAEA,UAAM,WAAW,kBAAM,QAAQ,uBAAuB,GAAG,SAAS;AAIlE,UAAM,WAAW,iBAAiB,WAAW,MAAM,SAAS,QAAQ,GAAG,IAAI,CAAC;AAE5E,WAAO,wBAAwB,UAAU,WAAW,UAAU,QAAQ,SAAS;AAAA,EACjF;AACF;AAGA,SAAS,gBAAgB,GAAiB;AACxC,SAAO,QAAQ,CAAC,KAAK,OAAO,EAAE,OAAO,aAAa,MAAM;AAC1D;AAOA,gBAAgB,gBAAgB,QAAa,KAA4C;AAGvF,mBAAiB,WAAW,QAAQ;AAClC,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,OAAO,gBAAgB,OAAO;AACpC,UAAI,KAAM,KAAI,OAAO;AAAA,IACvB;AACA,UAAM;AAAA,EACR;AACF;AAwDA,SAAS,wBACP,UACA,WACA,UACA,QACA,WACK;AACL,QAAM,wBAAwB,WAAW,OAAO,aAAa,GAAG,KAAK,QAAQ;AAC7E,MAAI,CAAC,uBAAuB;AAE1B,cAAU,UAAU,EAAE,MAAM,2BAAe,GAAG,CAAC;AAC/C,cAAU,IAAI;AACd,WAAO;AAAA,EACT;AAIA,QAAM,YAAwB;AAAA,IAC5B,MAAM;AAAA,IACN,KAAK;AAAA,IACL,eAAe,UAAU,OAAO,CAAC,EAAE,MAAM,QAAQ,SAAS,UAAU,KAAK,CAAC,IAAI,CAAC;AAAA,IAC/E,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,eAAe,QAAQ,UAAU,IAAI;AAAA,EACvC;AACA,QAAM,QAAoB;AAAA,IACxB,WAAW,oBAAI,IAAI;AAAA,IACnB,QAAQ,oBAAI,IAAI,CAAC,CAAC,YAAY,SAAS,CAAC,CAAC;AAAA,IACzC,UAAU;AAAA,IACV;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,QAAwB,QAAkB;AAC/D,QAAI,MAAM,SAAU;AACpB,UAAM,WAAW;AAGjB,eAAW,SAAS,MAAM,OAAO,OAAO,EAAG,oBAAmB,QAAQ,OAAO,KAAK;AAElF,eAAW,MAAM,MAAM,UAAU,OAAO,GAAG;AACzC,UAAI;AACF,WAAG,IAAI;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,UAAU,MAAM;AAEtB,eAAW,CAAC,KAAK,KAAK,KAAK,MAAM,QAAQ;AACvC,UAAI,QAAQ,WAAY;AACxB,iBAAW,OAAO,IAAI;AAAA,IACxB;AACA,QAAI,UAAU,UAAW,WAAU,aAAa,gBAAgB,UAAU,SAAS;AACnF,QAAI,WAAW,SAAS;AACtB,kBAAY,WAAW,GAAG;AAAA,IAC5B,OAAO;AACL,gBAAU,UAAU,EAAE,MAAM,2BAAe,GAAG,CAAC;AAC/C,gBAAU,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,OAAO,OAAO,eAAe,QAAQ,CAAC;AAC7D,SAAO,OAAO,SAAS,QAAQ;AAE/B,UAAQ,OAAO,aAAa,IAAI,WAAY;AAC1C,UAAM,WAAW,sBAAsB;AACvC,WAAO;AAAA,MACL,MAAM,OAAqC;AACzC,YAAI;AACF,gBAAM,SAAS,MAAM,iBAAiB,WAAW,MAAM,SAAS,KAAK,CAAC;AACtE,cAAI,OAAO,MAAM;AACf,0BAAc,IAAI;AAClB,mBAAO;AAAA,UACT;AACA,cAAI;AACF,0BAAc,QAAQ,OAAO,OAAO,OAAO,aAAa;AAAA,UAC1D,QAAQ;AAAA,UAER;AACA,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,wBAAc,SAAS,GAAG;AAC1B,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,MAAM,OAAO,OAA2C;AACtD,sBAAc,IAAI;AAClB,eAAO,SAAS,SAAS,KAAK,KAAK,EAAE,MAAM,MAAM,OAAO,OAAU;AAAA,MACpE;AAAA,MACA,MAAM,MAAM,KAAyC;AACnD,sBAAc,SAAS,GAAG;AAC1B,YAAI,SAAS,MAAO,QAAO,SAAS,MAAM,GAAG;AAC7C,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,WAAW,OAAmB,QAA8B;AACnE,MAAI;AACF,QAAI,MAAM,UAAW,OAAM,KAAK,aAAa,gBAAgB,MAAM,SAAS;AAC5E,UAAM,KAAK,UAAU,EAAE,MAAM,WAAW,OAAO,2BAAe,KAAK,2BAAe,MAAM,CAAC;AACzF,UAAM,KAAK,IAAI;AAAA,EACjB,QAAQ;AAAA,EAER;AACF;AAaA,SAAS,SACP,QACA,OACA,KACY;AACZ,QAAM,WAAW,KAAK,sBAAsB;AAC5C,MAAI,CAAC,SAAU,QAAO,MAAM,OAAO,IAAI,UAAU;AAEjD,QAAM,WAAW,MAAM,OAAO,IAAI,QAAQ;AAC1C,MAAI,SAAU,QAAO;AAMrB,QAAM,OAAO,MAAM,OAAO,IAAI,UAAU;AACxC,MAAI,CAAC,MAAM,UAAU,IAAI,QAAQ,KAAK,KAAK,iBAAiB;AAC1D,uBAAmB,QAAQ,MAAM,KAAK;AAAA,EACxC;AAIA,QAAM,iBAAiB,MAAM,UAAU,IAAI,QAAQ;AACnD,QAAM,YAAY,iBACd,kBAAM,QAAQ,uBAAuB,GAAG,cAAc,IACtD,MAAM,OAAO,IAAI,UAAU,EAAG;AAElC,QAAM,UAAU,KAAK,gBAAgB,OAAO,IAAI,aAAa,IAAI;AACjE,QAAM,QAA6B;AAAA,IACjC,sBAAsB;AAAA,IACtB,uBAAuB;AAAA,EACzB;AACA,MAAI,KAAK,iBAAkB,OAAM,aAAa,IAAI,OAAO,IAAI,gBAAgB;AAC7E,QAAM,OAAO,OAAO,UAAU,yBAAyB,OAAO,IAAI,EAAE,YAAY,MAAM,GAAG,SAAS;AAElG,QAAM,QAAoB;AAAA,IACxB;AAAA,IACA,KAAK,kBAAM,QAAQ,uBAAuB,GAAG,IAAI;AAAA,IACjD,eAAe,KAAK,mBAAmB,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,IAAI,gBAAgB,EAAE,CAAC,IAAI,CAAC;AAAA,IACpG,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,eAAe,QAAQ,KAAK,gBAAgB;AAAA,EAC9C;AACA,QAAM,OAAO,IAAI,UAAU,KAAK;AAChC,SAAO;AACT;AAEA,SAAS,cACP,QACA,OACA,KACA,eACM;AACN,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AAErC,QAAM,YAAY,MAAM,OAAO,IAAI,UAAU;AAI7C,MAAI,CAAC,UAAU,iBAAiB,MAAM,UAAU,MAAM;AACpD,cAAU,gBAAgB;AAC1B,cAAU,KAAK,aAAa,eAAe,MAAM,UAAU,IAAI;AAAA,EACjE;AAEA,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK,UAAU;AAEb,UAAI,IAAI,YAAY;AAClB,cAAM,YAAY,IAAI;AACtB,kBAAU,KAAK,aAAa,4BAA4B,OAAO,IAAI,UAAU,CAAC;AAAA,MAChF;AACA,UAAI,IAAI,OAAO;AACb,cAAM,QAAQ,IAAI;AAClB,kBAAU,KAAK,aAAa,wBAAwB,OAAO,IAAI,KAAK,CAAC;AAAA,MACvE;AACA;AAAA,IACF;AAAA,IAEA,KAAK,QAAQ;AAGX,YAAM,QAAQ,SAAS,QAAQ,OAAO,GAAG;AACzC,yBAAmB,QAAQ,OAAO,KAAK;AAEvC,YAAM,WAAW,gBAAgB,GAAG;AACpC,UAAI,UAAU;AACZ,YAAI,CAAC,MAAM,eAAe;AACxB,gBAAM,gBAAgB;AACtB,gBAAM,KAAK,aAAa,eAAe,QAAQ;AAAA,QACjD;AACA,cAAM,cAAc,KAAK,EAAE,MAAM,QAAQ,SAAS,SAAS,CAAC;AAAA,MAC9D;AACA,6BAAuB,OAAO,OAAO,GAAG;AACxC;AAAA,IACF;AAAA,IAEA,KAAK,aAAa;AAIhB,YAAM,QAAQ,SAAS,QAAQ,OAAO,GAAG;AACzC,6BAAuB,OAAO,GAAG;AACjC;AAAA,IACF;AAAA,IAEA,KAAK,UAAU;AAGb,yBAAmB,QAAQ,WAAW,KAAK;AAE3C,YAAM,OAAO,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAC3D,UAAI,KAAM,WAAU,YAAY;AAChC,UAAI,IAAI,cAAc,CAAC,MAAM,WAAW;AACtC,kBAAU,KAAK,aAAa,4BAA4B,OAAO,IAAI,UAAU,CAAC;AAAA,MAChF;AACA,YAAM,QAAQ,IAAI;AAClB,UAAI,MAAO,UAAS,UAAU,MAAM,KAAK;AACzC,UAAI,IAAI,kBAAkB,KAAM,WAAU,KAAK,aAAa,2BAA2B,IAAI,cAAc;AACzG,UAAI,IAAI,aAAa,KAAM,WAAU,KAAK,aAAa,4BAA4B,IAAI,SAAS;AAChG,UAAI,IAAI,SAAU,WAAU,KAAK,aAAa,2BAA2B,IAAI;AAE7E,oBAAc,IAAI,WAAW,UAAU,MAAM,IAAI,WAAW,IAAI,MAAM,OAAO,QAAQ,kBAAkB,CAAC,IAAI,MAAS;AACrH;AAAA,IACF;AAAA,IAEA;AACE;AAAA,EACJ;AACF;AAQA,SAAS,uBAAuB,OAAmB,KAAgB;AACjE,QAAM,UAAU,IAAI,WAAW;AAC/B,QAAM,UAAU,SAAS,WAAW,CAAC;AAErC,MAAI,CAAC,MAAM,iBAAiB;AAC1B,UAAM,kBAAkB,EAAE,WAAW,CAAC,GAAG,eAAe,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,OAAU;AAAA,EAC9F;AACA,QAAM,MAAM,MAAM;AAClB,MAAI,SAAS,MAAO,KAAI,QAAQ,QAAQ;AACxC,MAAI,SAAS,YAAa,KAAI,aAAa,QAAQ;AAGnD,MAAI,SAAS,MAAO,KAAI,QAAQ,QAAQ;AAExC,aAAW,SAAS,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,GAAG;AACzD,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,QAAI,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,SAAU,KAAI,UAAU,KAAK,MAAM,IAAI;AAAA,aACjF,MAAM,SAAS,cAAc,OAAO,MAAM,aAAa,SAAU,KAAI,cAAc,KAAK,MAAM,QAAQ;AAAA,aACtG,MAAM,SAAS,YAAY;AAClC,UAAI,UAAU,KAAK,EAAE,IAAI,MAAM,MAAM,IAAI,MAAM,MAAM,QAAQ,IAAI,OAAO,MAAM,MAAM,CAAC;AAAA,IACvF;AAAA,EACF;AACF;AAOA,SAAS,mBACP,QACA,OACA,OACM;AACN,QAAM,MAAM,MAAM;AAClB,MAAI,CAAC,IAAK;AACV,QAAM,kBAAkB;AAExB,QAAM,QAAQ,IAAI,SAAS,MAAM,SAAS;AAE1C,QAAM,QAA6B;AAAA,IACjC,sBAAsB;AAAA,IACtB,yBAAyB;AAAA,IACzB,uBAAuB;AAAA,EACzB;AACA,MAAI,MAAO,OAAM,yBAAyB,IAAI,OAAO,KAAK;AAI1D,MAAI,MAAM,cAAc,WAAW,KAAK,MAAM,UAAU,MAAM;AAC5D,UAAM,cAAc,KAAK,EAAE,MAAM,QAAQ,SAAS,MAAM,UAAU,KAAK,CAAC;AAAA,EAC1E;AAOA,QAAM,cAAc,QAAQ,CAAC,GAAG,MAAM;AACpC,UAAM,+BAA+B,CAAC,OAAO,IAAI,EAAE;AACnD,UAAM,+BAA+B,CAAC,UAAU,IAAI,EAAE;AAAA,EACxD,CAAC;AACD,MAAI,MAAM,cAAc,QAAQ;AAC9B,UAAM,aAAa,IAAI,cAAc,EAAE,UAAU,MAAM,cAAc,CAAC;AAAA,EACxE;AAOA,QAAM,UAAU,IAAI,UAAU,KAAK,EAAE;AACrC,QAAM,WACJ,WACA,IAAI,UAAU,IAAI,CAAC,OAAO,GAAG,GAAG,IAAI,IAAI,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,IAAI;AACrF,MAAI,UAAU;AACZ,UAAM,qCAAqC,IAAI;AAC/C,UAAM,wCAAwC,IAAI;AAClD,UAAM,cAAc,IAAI;AAAA,EAC1B;AACA,MAAI,IAAI,cAAc,QAAQ;AAC5B,UAAM,yCAAyC,IAAI,IAAI,cAAc,KAAK,EAAE;AAAA,EAC9E;AACA,MAAI,UAAU,QAAQ,CAAC,IAAI,MAAM;AAC/B,UAAM,2BAA2B,CAAC,KAAK,IAAI,GAAG;AAC9C,UAAM,2BAA2B,CAAC,OAAO,IAAI,GAAG;AAChD,UAAM,2BAA2B,CAAC,YAAY,IAAI,cAAc,GAAG,SAAS,CAAC,CAAC;AAAA,EAChF,CAAC;AACD,MAAI,IAAI,WAAY,OAAM,4BAA4B,IAAI,OAAO,IAAI,UAAU;AAG/E,QAAM,OAAO,OAAO,UAAU,oBAAoB,SAAS,OAAO,IAAI,EAAE,YAAY,MAAM,GAAG,MAAM,GAAG;AACtG,MAAI,IAAI,MAAO,UAAS,MAAM,IAAI,KAAK;AACvC,OAAK,UAAU,EAAE,MAAM,2BAAe,GAAG,CAAC;AAC1C,OAAK,IAAI;AAIT,MAAI,QAAS,OAAM,YAAY;AAG/B,QAAM,YAAsB,CAAC;AAC7B,MAAI,QAAS,WAAU,KAAK,OAAO;AACnC,aAAW,MAAM,IAAI,UAAW,WAAU,KAAK,cAAc,GAAG,IAAI,IAAI,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG;AACxG,MAAI,UAAU,OAAQ,OAAM,cAAc,KAAK,EAAE,MAAM,aAAa,SAAS,UAAU,KAAK,IAAI,EAAE,CAAC;AAKnG,aAAW,MAAM,IAAI,WAAW;AAC9B,UAAM,WAAW,OAAO;AAAA,MACtB,qBAAqB,GAAG,QAAQ,MAAM;AAAA,MACtC;AAAA,QACE,YAAY;AAAA,UACV,sBAAsB;AAAA,UACtB,sBAAsB,OAAO,GAAG,QAAQ,EAAE;AAAA,UAC1C,GAAI,GAAG,KAAK,EAAE,yBAAyB,OAAO,GAAG,EAAE,EAAE,IAAI,CAAC;AAAA,UAC1D,eAAe,cAAc,GAAG,SAAS,CAAC,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR;AACA,QAAI,GAAG,GAAI,OAAM,UAAU,IAAI,GAAG,IAAI,QAAQ;AAAA,EAChD;AACF;AAEA,SAAS,uBAAuB,OAAmB,OAAmB,KAAgB;AACpF,QAAM,WAAW,IAAI,WAAW,MAAM,WAAW,CAAC;AAClD,aAAW,SAAS,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,GAAG;AACzD,QAAI,OAAO,SAAS,cAAe;AACnC,UAAM,KAAK,MAAM,eAAe;AAChC,UAAM,MAAM,MAAM;AAClB,UAAM,UAAW,OAAO,QAAQ,WAAW,MAAM,cAAc,GAAG;AAGlE,QAAI,QAAS,OAAM,cAAc,KAAK,EAAE,MAAM,QAAQ,SAAS,QAAQ,CAAC;AACxE,UAAM,OAAO,MAAM,UAAU,IAAI,EAAE;AACnC,QAAI,CAAC,KAAM;AACX,SAAK,aAAa,gBAAgB,OAAO;AACzC,QAAI,MAAM,UAAU;AAClB,WAAK,UAAU,EAAE,MAAM,2BAAe,MAAM,CAAC;AAC7C,WAAK,aAAa,0BAA0B,IAAI;AAAA,IAClD,OAAO;AACL,WAAK,UAAU,EAAE,MAAM,2BAAe,GAAG,CAAC;AAAA,IAC5C;AACA,SAAK,IAAI;AACT,UAAM,UAAU,OAAO,EAAE;AAAA,EAC3B;AACF;AAUA,SAAS,gBAAgB,KAAkB;AACzC,QAAM,WAAW,IAAI,WAAW,MAAM;AACtC,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,UAAU,SAAU,OAAM,KAAK,KAAK;AAAA,aACtC,SAAS,OAAO,UAAU,YAAY,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,UAAU;AACtG,YAAM,KAAK,MAAM,IAAI;AAAA,IACvB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,SAAS,MAAY,OAAkB;AAC9C,MAAI,CAAC,MAAO;AACZ,MAAI,MAAM,gBAAgB,KAAM,MAAK,aAAa,mCAAmC,MAAM,YAAY;AACvG,MAAI,MAAM,iBAAiB,KAAM,MAAK,aAAa,uCAAuC,MAAM,aAAa;AAC7G,MAAI,MAAM,gBAAgB,QAAQ,MAAM,iBAAiB,MAAM;AAC7D,SAAK,aAAa,kCAAkC,MAAM,eAAe,MAAM,aAAa;AAAA,EAC9F;AACA,MAAI,MAAM,2BAA2B,MAAM;AACzC,SAAK,aAAa,uCAAuC,MAAM,uBAAuB;AAAA,EACxF;AACA,MAAI,MAAM,+BAA+B,MAAM;AAC7C,SAAK,aAAa,wCAAwC,MAAM,2BAA2B;AAAA,EAC7F;AACF;AAEA,SAAS,kBAAkB,QAAqB;AAC9C,MAAI,OAAO,WAAW,SAAU,QAAO;AAGvC,SAAO;AACT;AAEA,SAAS,cAAc,OAAwB;AAC7C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK,KAAK;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,MAAY,KAAoB;AACnD,MAAI,eAAe,OAAO;AACxB,SAAK,UAAU,EAAE,MAAM,2BAAe,OAAO,SAAS,IAAI,QAAQ,CAAC;AACnE,SAAK,gBAAgB,GAAG;AAAA,EAC1B,OAAO;AACL,SAAK,UAAU,EAAE,MAAM,2BAAe,OAAO,SAAS,OAAO,GAAG,EAAE,CAAC;AAAA,EACrE;AACA,OAAK,IAAI;AACX;","names":["import_api","otelTrace","otelTrace"]}
|
|
1
|
+
{"version":3,"sources":["../src/claude-agent-sdk.ts","../src/core/provider.ts"],"sourcesContent":["/**\n * Neatlogs Claude Agent SDK integration.\n *\n * Wraps Anthropic's `@anthropic-ai/claude-agent-sdk` so every `query()` run is\n * traced. The SDK's `query()` returns an async-iterable of SDKMessages\n * (system init → assistant (text + tool_use) → user (tool_result) → … →\n * result). Each message carries `parent_tool_use_id`: null for the main\n * (orchestrator) agent, or the id of the spawning Task tool call for a subagent.\n * We translate that into a neatlogs span tree:\n *\n * AGENT claude_agent.query (orchestrator = trace root)\n * ↳ LLM orchestrator turn (one per model turn; text + tool_calls)\n * ↳ TOOL Read / Edit / Bash …\n * ↳ TOOL Task (spawns a subagent)\n * ↳ AGENT claude_agent.subagent.<type> (each subagent, nested)\n * ↳ LLM subagent turn\n * ↳ TOOL subagent tool call\n *\n * The orchestrator is the single root AGENT span — there is NO redundant WORKFLOW\n * wrapper. Subagents (Task-tool invocations, e.g. the wizard's parallel per-file\n * edits) get their own AGENT spans nested under the Task TOOL span that spawned\n * them, so a multi-agent run is represented faithfully and each agent's I/O is\n * distinct.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { wrapClaudeAgentSDK } from 'neatlogs/claude-agent-sdk';\n * import * as claudeAgentSDK from '@anthropic-ai/claude-agent-sdk';\n *\n * await init({ apiKey, workflowName });\n * const { query } = wrapClaudeAgentSDK(claudeAgentSDK);\n * for await (const msg of query({ prompt: 'Hello', options: {...} })) { ... }\n *\n * Conversation tracking: Claude's `session_id` is captured on the root AGENT span\n * as `neatlogs.conversation.id`. Tool calls are traced only through the wrapped\n * `query` — calling the unwrapped SDK directly produces no tracing.\n */\n\nimport { trace, SpanStatusCode, type Span, type Context } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsBaseContext, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.claude_agent_sdk';\nconst ROOT_SCOPE = '__root__';\n\nexport interface WrapClaudeAgentSDKOptions {\n /** Logical grouping for traces (also settable globally via init({ workflowName })). */\n workflowName?: string;\n}\n\n/**\n * Wrap the Claude Agent SDK module. Returns a shallow copy of the module with an\n * instrumented `query`; all other exports (createSdkMcpServer, tool, the built-in\n * Tool helpers, etc.) are passed through unchanged.\n */\nexport function wrapClaudeAgentSDK<T extends Record<string, any>>(\n sdk: T,\n options: WrapClaudeAgentSDKOptions = {},\n): T {\n if (!sdk || typeof sdk.query !== 'function') return sdk;\n if ((sdk as any)._neatlogsWrapped) return sdk;\n\n const wrapped: Record<string, any> = { ...sdk };\n wrapped.query = wrapQuery(sdk.query.bind(sdk), options);\n\n try {\n Object.defineProperty(wrapped, '_neatlogsWrapped', {\n value: true,\n enumerable: false,\n configurable: true,\n });\n } catch {\n wrapped._neatlogsWrapped = true;\n }\n\n return wrapped as T;\n}\n\n// ---------------------------------------------------------------------------\n// query() wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapQuery(original: (...args: any[]) => any, options: WrapClaudeAgentSDKOptions) {\n return function (params: any, ...rest: any[]): any {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const workflowName = options.workflowName ?? 'claude_agent.query';\n\n // Shared ref the input-tap fills with the first user-prompt text. In\n // streaming-input mode the prompt is an async generator fed into the SDK\n // subprocess and is NOT echoed back as a `user` output message before the\n // first assistant turn — so without tapping it, the first LLM span has no\n // input. The tap reads prompt text as the SDK pulls it.\n const promptRef: { text: string } = { text: '' };\n\n // The orchestrator AGENT is the trace ROOT (no WORKFLOW wrapper — a single\n // query() is one agent run; subagents nest below as their own AGENT spans).\n const agentSpan = tracer.startSpan(\n 'claude_agent.query',\n { attributes: { 'neatlogs.span.kind': 'AGENT', 'neatlogs.workflow.name': workflowName } },\n getNeatlogsBaseContext(),\n );\n\n // Input: a string prompt is captured directly. A streaming-input prompt (an\n // async iterable) is tapped — promptRef.text fills as the SDK pulls the\n // first user message — so the agent input and the first LLM span's input are\n // populated even before any `user` message echoes back through the output.\n const promptText = extractPromptText(params?.prompt);\n if (promptText) {\n promptRef.text = promptText;\n agentSpan.setAttribute('input.value', promptText);\n } else if (params && isAsyncIterable(params.prompt)) {\n params = { ...params, prompt: tapPromptStream(params.prompt, promptRef) };\n }\n\n const agentCtx = trace.setSpan(getNeatlogsBaseContext(), agentSpan);\n\n // Call the original query with the AGENT span active only under our private\n // context so a foreign provider's spans neither parent nor nest under ours.\n const queryObj = withNeatlogsSpan(agentSpan, () => original(params, ...rest));\n\n return instrumentQueryIterable(queryObj, agentSpan, agentCtx, tracer, promptRef);\n };\n}\n\n/** True for an async iterable (streaming-input prompt). */\nfunction isAsyncIterable(v: any): boolean {\n return Boolean(v) && typeof v[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Pass-through wrapper over a streaming-input prompt that records the first\n * user message's text into `ref` as the SDK consumes it. Never alters what the\n * SDK receives.\n */\nasync function* tapPromptStream(prompt: any, ref: { text: string }): AsyncGenerator<any> {\n // userMessageText is fully defensive (returns '' for any non-text shape), so\n // no try/catch is needed — the message is always yielded through untouched.\n for await (const message of prompt) {\n if (!ref.text) {\n const text = userMessageText(message);\n if (text) ref.text = text;\n }\n yield message;\n }\n}\n\ninterface ToolCallAccum {\n id: string;\n name: string;\n input: unknown;\n}\n\ninterface AssistantTurnBuffer {\n textParts: string[];\n thinkingParts: string[];\n toolCalls: ToolCallAccum[];\n usage: any;\n model?: string;\n stopReason?: string;\n}\n\n/**\n * One agent's tracing scope. There is always a root scope (the orchestrator,\n * keyed ROOT_SCOPE). Each subagent — identified by the `parent_tool_use_id` of\n * the Task tool call that spawned it — gets its own scope created lazily, with\n * its AGENT span nested under that Task TOOL span. Per-scope state keeps each\n * agent's conversation/turn buffer independent.\n */\ninterface AgentScope {\n /** The AGENT span for this scope (root = orchestrator; others = subagents). */\n span: Span;\n /** OTel context whose active span is this scope's AGENT span (children nest here). */\n ctx: Context;\n /** Running conversation (user/tool/assistant turns) — each LLM span's input. */\n inputMessages: Array<{ role: string; content: string }>;\n /** In-progress model turn, coalesced from multiple `assistant` messages. */\n assistantBuffer: AssistantTurnBuffer | null;\n /** Last assistant text seen (subagent output = its final text). */\n finalText: string;\n /** Whether input.value has been set on this scope's AGENT span. */\n inputCaptured: boolean;\n}\n\ninterface QueryState {\n /** TOOL spans keyed by tool_use_id, closed by the matching tool_result. */\n toolSpans: Map<string, Span>;\n /** Agent scopes keyed by parent_tool_use_id (ROOT_SCOPE for the orchestrator). */\n scopes: Map<string, AgentScope>;\n sessionId?: string;\n model?: string;\n finished: boolean;\n /** Lazily-filled prompt text from a streaming-input tap. */\n promptRef: { text: string };\n}\n\n/**\n * Wrap the Query object so iteration is instrumented while preserving its own\n * methods (interrupt, setPermissionMode, …). The SDK returns an async-iterable\n * object, not a bare generator.\n */\nfunction instrumentQueryIterable(\n queryObj: any,\n agentSpan: Span,\n agentCtx: Context,\n tracer: ReturnType<typeof trace.getTracer>,\n promptRef: { text: string },\n): any {\n const originalAsyncIterator = queryObj?.[Symbol.asyncIterator]?.bind(queryObj);\n if (!originalAsyncIterator) {\n // Not iterable — nothing to trace; close the root span immediately.\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n return queryObj;\n }\n\n // The root scope is the orchestrator agent. Seed its conversation with the\n // prompt (known up front for string prompts; filled by the tap otherwise).\n const rootScope: AgentScope = {\n span: agentSpan,\n ctx: agentCtx,\n inputMessages: promptRef.text ? [{ role: 'user', content: promptRef.text }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(promptRef.text),\n };\n const state: QueryState = {\n toolSpans: new Map(),\n scopes: new Map([[ROOT_SCOPE, rootScope]]),\n finished: false,\n promptRef,\n };\n\n const finalizeAgent = (status: 'ok' | 'error', err?: unknown) => {\n if (state.finished) return;\n state.finished = true;\n // Flush every scope's in-progress turn, then close subagent AGENT spans\n // (deepest-first) and finally the root.\n for (const scope of state.scopes.values()) flushAssistantTurn(tracer, scope, state);\n // Close any tool spans that never got a matching result.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n // Close subagent scopes first (any still open), then the root.\n for (const [key, scope] of state.scopes) {\n if (key === ROOT_SCOPE) continue;\n closeScope(scope, 'ok');\n }\n if (rootScope.finalText) agentSpan.setAttribute('output.value', rootScope.finalText);\n if (status === 'error') {\n recordError(agentSpan, err);\n } else {\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n }\n };\n\n const wrapped = Object.create(Object.getPrototypeOf(queryObj));\n Object.assign(wrapped, queryObj);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await withNeatlogsSpan(agentSpan, () => iterator.next());\n if (result.done) {\n finalizeAgent('ok');\n return result;\n }\n try {\n handleMessage(tracer, state, result.value, finalizeAgent);\n } catch {\n /* never let tracing break the run */\n }\n return result;\n } catch (err) {\n finalizeAgent('error', err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeAgent('ok');\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n finalizeAgent('error', err);\n if (iterator.throw) return iterator.throw(err);\n throw err;\n },\n };\n };\n\n return wrapped;\n}\n\n/** End a subagent scope's AGENT span, setting its output to the subagent's final text. */\nfunction closeScope(scope: AgentScope, status: 'ok' | 'error'): void {\n try {\n if (scope.finalText) scope.span.setAttribute('output.value', scope.finalText);\n scope.span.setStatus({ code: status === 'ok' ? SpanStatusCode.OK : SpanStatusCode.ERROR });\n scope.span.end();\n } catch {\n /* ignore */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message handling\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the agent scope for a message. `parent_tool_use_id` is null for the\n * orchestrator (root scope) and the spawning Task tool_use_id for a subagent.\n * Subagent scopes are created lazily, with their AGENT span nested under the\n * Task TOOL span (looked up by that id) so the hierarchy is\n * orchestrator → Task TOOL → subagent AGENT.\n */\nfunction getScope(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n): AgentScope {\n const parentId = msg?.parent_tool_use_id ?? null;\n if (!parentId) return state.scopes.get(ROOT_SCOPE)!;\n\n const existing = state.scopes.get(parentId);\n if (existing) return existing;\n\n // New subagent. Its spawning Task tool_use may still be buffered in the root\n // turn (the SDK emits subagent messages before the Task's tool_result closes\n // the parent turn). Flush the root turn first so the Task TOOL span exists and\n // this subagent AGENT can nest under it.\n const root = state.scopes.get(ROOT_SCOPE)!;\n if (!state.toolSpans.has(parentId) && root.assistantBuffer) {\n flushAssistantTurn(tracer, root, state);\n }\n\n // Nest the subagent AGENT span under the spawning Task TOOL span if we have it;\n // otherwise under the root agent.\n const parentToolSpan = state.toolSpans.get(parentId);\n const parentCtx = parentToolSpan\n ? trace.setSpan(getNeatlogsBaseContext(), parentToolSpan)\n : state.scopes.get(ROOT_SCOPE)!.ctx;\n\n const subType = msg?.subagent_type ? String(msg.subagent_type) : 'subagent';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'AGENT',\n 'neatlogs.agent.name': subType,\n };\n if (msg?.task_description) attrs['input.value'] = String(msg.task_description);\n const span = tracer.startSpan(`claude_agent.subagent.${subType}`, { attributes: attrs }, parentCtx);\n\n const scope: AgentScope = {\n span,\n ctx: trace.setSpan(getNeatlogsBaseContext(), span),\n inputMessages: msg?.task_description ? [{ role: 'user', content: String(msg.task_description) }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(msg?.task_description),\n };\n state.scopes.set(parentId, scope);\n return scope;\n}\n\nfunction handleMessage(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n finalizeAgent: (status: 'ok' | 'error', err?: unknown) => void,\n): void {\n if (!msg || typeof msg !== 'object') return;\n\n const rootScope = state.scopes.get(ROOT_SCOPE)!;\n\n // Backfill root input from the tapped streaming-input prompt as soon as it's\n // available (it fills before the first assistant turn).\n if (!rootScope.inputCaptured && state.promptRef.text) {\n rootScope.inputCaptured = true;\n rootScope.span.setAttribute('input.value', state.promptRef.text);\n }\n\n switch (msg.type) {\n case 'system': {\n // init message — carries session_id, model, available tools.\n if (msg.session_id) {\n state.sessionId = msg.session_id;\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n if (msg.model) {\n state.model = msg.model;\n rootScope.span.setAttribute('neatlogs.agent.model', String(msg.model));\n }\n break;\n }\n\n case 'user': {\n // A `user` message (prompt, or tool_result turns) is a turn boundary for\n // its scope: flush the buffered turn as ONE LLM span first.\n const scope = getScope(tracer, state, msg);\n flushAssistantTurn(tracer, scope, state);\n\n const userText = userMessageText(msg);\n if (userText) {\n if (!scope.inputCaptured) {\n scope.inputCaptured = true;\n scope.span.setAttribute('input.value', userText);\n }\n scope.inputMessages.push({ role: 'user', content: userText });\n }\n closeToolSpansFromUser(state, scope, msg);\n break;\n }\n\n case 'assistant': {\n // Don't emit yet — the SDK delivers one model turn as multiple `assistant`\n // messages (text block, then tool_use block, …). Buffer them per scope;\n // the next user/result boundary flushes the turn as a single LLM span.\n const scope = getScope(tracer, state, msg);\n bufferAssistantMessage(scope, msg);\n break;\n }\n\n case 'result': {\n // The run is complete. Flush the root turn (a final text answer may have no\n // trailing user message), then finalize.\n flushAssistantTurn(tracer, rootScope, state);\n\n const text = typeof msg.result === 'string' ? msg.result : '';\n if (text) rootScope.finalText = text;\n if (msg.session_id && !state.sessionId) {\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n const usage = msg.usage;\n if (usage) setUsage(rootScope.span, usage);\n if (msg.total_cost_usd != null) rootScope.span.setAttribute('neatlogs.agent.cost_usd', msg.total_cost_usd);\n if (msg.num_turns != null) rootScope.span.setAttribute('neatlogs.agent.num_turns', msg.num_turns);\n if (msg.is_error) rootScope.span.setAttribute('neatlogs.agent.is_error', true);\n\n finalizeAgent(msg.is_error ? 'error' : 'ok', msg.is_error ? new Error(String(text || 'agent run failed')) : undefined);\n break;\n }\n\n default:\n break;\n }\n}\n\n/**\n * Append one `assistant` SDK message to the in-progress turn buffer. The SDK\n * splits a single model turn into multiple assistant messages (a text block,\n * then tool_use blocks); they share token usage. We merge their text, thinking,\n * and tool_use blocks so the turn becomes ONE LLM span on flush.\n */\nfunction bufferAssistantMessage(scope: AgentScope, msg: any): void {\n const message = msg.message ?? msg;\n const content = message?.content ?? [];\n\n if (!scope.assistantBuffer) {\n scope.assistantBuffer = { textParts: [], thinkingParts: [], toolCalls: [], usage: undefined };\n }\n const buf = scope.assistantBuffer;\n if (message?.model) buf.model = message.model;\n if (message?.stop_reason) buf.stopReason = message.stop_reason;\n // Usage is reported per assistant message but is the SAME turn total — keep\n // the largest/last non-empty one rather than summing (summing double-counts).\n if (message?.usage) buf.usage = message.usage;\n\n for (const block of Array.isArray(content) ? content : []) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') buf.textParts.push(block.text);\n else if (block.type === 'thinking' && typeof block.thinking === 'string') buf.thinkingParts.push(block.thinking);\n else if (block.type === 'tool_use') {\n buf.toolCalls.push({ id: block.id ?? '', name: block.name ?? '', input: block.input });\n }\n }\n}\n\n/**\n * Emit the buffered model turn as a SINGLE LLM span, then open TOOL spans for\n * its tool calls. No-op if no turn is buffered. This is what makes one LLM span\n * per real model turn (not per SDK assistant message).\n */\nfunction flushAssistantTurn(\n tracer: ReturnType<typeof trace.getTracer>,\n scope: AgentScope,\n state: QueryState,\n): void {\n const buf = scope.assistantBuffer;\n if (!buf) return;\n scope.assistantBuffer = null;\n\n const model = buf.model ?? state.model ?? '';\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n };\n if (model) attrs['neatlogs.llm.model_name'] = String(model);\n\n // If this is the root scope's first turn and no user message was recorded yet\n // (streaming-input mode), seed input from the tapped prompt text.\n if (scope.inputMessages.length === 0 && state.promptRef.text) {\n scope.inputMessages.push({ role: 'user', content: state.promptRef.text });\n }\n\n // Input = the exact accumulated conversation up to this turn. Emitted BOTH as\n // structured indexed input_messages.* AND as the flat `input.value` blob —\n // per neatlogs/config/attribute-mapping.json, the main UI panel renders\n // `neatlogs.{span_kind}.input` (mapped from `input.value`); the indexed\n // messages alone do NOT populate it. Without input.value the LLM Input is blank.\n scope.inputMessages.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;\n });\n if (scope.inputMessages.length) {\n attrs['input.value'] = safeStringify({ messages: scope.inputMessages });\n }\n\n // Output = the turn's actual assistant content. Prefer the model's text; for a\n // tool-only turn (no text) the output IS the tool call(s), so render them as\n // the exact `name(arguments)` the model emitted (not a vague summary). The\n // structured tool_calls.* below still carry the same data for programmatic use.\n // `output.value` is the flat blob the UI maps to `neatlogs.{span_kind}.output`.\n const outText = buf.textParts.join('');\n const outValue =\n outText ||\n buf.toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.input ?? {})})`).join('\\n');\n if (outValue) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = outValue;\n attrs['output.value'] = outValue;\n }\n if (buf.thinkingParts.length) {\n attrs['neatlogs.llm.output_messages.0.thinking'] = buf.thinkingParts.join('');\n }\n buf.toolCalls.forEach((tc, j) => {\n attrs[`neatlogs.llm.tool_calls.${j}.id`] = tc.id;\n attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.input ?? {});\n });\n if (buf.stopReason) attrs['neatlogs.llm.finish_reason'] = String(buf.stopReason);\n\n // The LLM span nests under THIS scope's AGENT span.\n const span = tracer.startSpan(`claude_agent.llm.${model || 'model'}`, { attributes: attrs }, scope.ctx);\n if (buf.usage) setUsage(span, buf.usage);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n\n // Track this scope's latest text as its output (the subagent/orchestrator\n // final answer is the last assistant text).\n if (outText) scope.finalText = outText;\n\n // Record this assistant turn so the NEXT turn's LLM span sees it as context.\n const turnParts: string[] = [];\n if (outText) turnParts.push(outText);\n for (const tc of buf.toolCalls) turnParts.push(`[tool_call ${tc.name} ${safeStringify(tc.input ?? {})}]`);\n if (turnParts.length) scope.inputMessages.push({ role: 'assistant', content: turnParts.join('\\n') });\n\n // Open TOOL spans for this turn's tool calls, nested under THIS scope's AGENT\n // span (closed by their tool_result). A Task tool's id becomes the key a\n // subagent's messages resolve to (see getScope).\n for (const tc of buf.toolCalls) {\n const toolSpan = tracer.startSpan(\n `claude_agent.tool.${tc.name || 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': String(tc.name ?? ''),\n ...(tc.id ? { 'neatlogs.tool_call.id': String(tc.id) } : {}),\n 'input.value': safeStringify(tc.input ?? {}),\n },\n },\n scope.ctx,\n );\n if (tc.id) state.toolSpans.set(tc.id, toolSpan);\n }\n}\n\nfunction closeToolSpansFromUser(state: QueryState, scope: AgentScope, msg: any): void {\n const content = (msg.message ?? msg)?.content ?? [];\n for (const block of Array.isArray(content) ? content : []) {\n if (block?.type !== 'tool_result') continue;\n const id = block.tool_use_id ?? '';\n const out = block.content;\n const outText = (typeof out === 'string' ? out : safeStringify(out));\n // Feed the tool result into THIS scope's conversation so the next LLM span's\n // input reflects what the model actually saw.\n if (outText) scope.inputMessages.push({ role: 'tool', content: outText });\n const span = state.toolSpans.get(id);\n if (!span) continue;\n span.setAttribute('output.value', outText);\n if (block.is_error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n state.toolSpans.delete(id);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the human/text content of a `user` SDK message — the prompt, not\n * tool_result blocks (those are handled separately as tool outputs).\n */\nfunction userMessageText(msg: any): string {\n const content = (msg.message ?? msg)?.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string') {\n parts.push(block.text);\n }\n }\n return parts.join('\\n');\n}\n\nfunction setUsage(span: Span, usage: any): void {\n if (!usage) return;\n if (usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.input_tokens);\n if (usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.output_tokens);\n if (usage.input_tokens != null && usage.output_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.total', usage.input_tokens + usage.output_tokens);\n }\n if (usage.cache_read_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.cache_read_input_tokens);\n }\n if (usage.cache_creation_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_write', usage.cache_creation_input_tokens);\n }\n}\n\nfunction extractPromptText(prompt: any): string {\n if (typeof prompt === 'string') return prompt;\n // Streaming-input mode passes an async iterable of messages; we can't read it\n // synchronously without consuming it, so leave input.value unset in that case.\n return '';\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return 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 * 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';\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 return providerState.provider?.getTracer(name) ?? preInitTracer;\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 return providerState.provider;\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"],"mappings":"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","names":["import_api","otelTrace","otelTrace"]}
|
|
@@ -6,8 +6,10 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
6
6
|
import {
|
|
7
7
|
ROOT_CONTEXT,
|
|
8
8
|
INVALID_SPAN_CONTEXT,
|
|
9
|
+
createContextKey,
|
|
9
10
|
trace as otelTrace
|
|
10
11
|
} from "@opentelemetry/api";
|
|
12
|
+
var NEATLOGS_ROOT_SPAN_KEY = createContextKey("neatlogs.root_span");
|
|
11
13
|
var PRIVATE_SPAN_STORAGE_KEY = /* @__PURE__ */ Symbol.for(
|
|
12
14
|
"neatlogs.private_span_async_local_storage"
|
|
13
15
|
);
|
|
@@ -39,7 +41,10 @@ function getNeatlogsBaseContext(baseContext) {
|
|
|
39
41
|
}
|
|
40
42
|
function withNeatlogsSpan(span, fn, baseContext) {
|
|
41
43
|
const base = baseContext ?? getNeatlogsActiveContext();
|
|
42
|
-
|
|
44
|
+
let ctx = otelTrace.setSpan(base, span);
|
|
45
|
+
if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === void 0) {
|
|
46
|
+
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);
|
|
47
|
+
}
|
|
43
48
|
return privateContextStorage.run(ctx, fn);
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/claude-agent-sdk.ts","../src/core/provider.ts"],"sourcesContent":["/**\n * Neatlogs Claude Agent SDK integration.\n *\n * Wraps Anthropic's `@anthropic-ai/claude-agent-sdk` so every `query()` run is\n * traced. The SDK's `query()` returns an async-iterable of SDKMessages\n * (system init → assistant (text + tool_use) → user (tool_result) → … →\n * result). Each message carries `parent_tool_use_id`: null for the main\n * (orchestrator) agent, or the id of the spawning Task tool call for a subagent.\n * We translate that into a neatlogs span tree:\n *\n * AGENT claude_agent.query (orchestrator = trace root)\n * ↳ LLM orchestrator turn (one per model turn; text + tool_calls)\n * ↳ TOOL Read / Edit / Bash …\n * ↳ TOOL Task (spawns a subagent)\n * ↳ AGENT claude_agent.subagent.<type> (each subagent, nested)\n * ↳ LLM subagent turn\n * ↳ TOOL subagent tool call\n *\n * The orchestrator is the single root AGENT span — there is NO redundant WORKFLOW\n * wrapper. Subagents (Task-tool invocations, e.g. the wizard's parallel per-file\n * edits) get their own AGENT spans nested under the Task TOOL span that spawned\n * them, so a multi-agent run is represented faithfully and each agent's I/O is\n * distinct.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { wrapClaudeAgentSDK } from 'neatlogs/claude-agent-sdk';\n * import * as claudeAgentSDK from '@anthropic-ai/claude-agent-sdk';\n *\n * await init({ apiKey, workflowName });\n * const { query } = wrapClaudeAgentSDK(claudeAgentSDK);\n * for await (const msg of query({ prompt: 'Hello', options: {...} })) { ... }\n *\n * Conversation tracking: Claude's `session_id` is captured on the root AGENT span\n * as `neatlogs.conversation.id`. Tool calls are traced only through the wrapped\n * `query` — calling the unwrapped SDK directly produces no tracing.\n */\n\nimport { trace, SpanStatusCode, type Span, type Context } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsBaseContext, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.claude_agent_sdk';\nconst ROOT_SCOPE = '__root__';\n\nexport interface WrapClaudeAgentSDKOptions {\n /** Logical grouping for traces (also settable globally via init({ workflowName })). */\n workflowName?: string;\n}\n\n/**\n * Wrap the Claude Agent SDK module. Returns a shallow copy of the module with an\n * instrumented `query`; all other exports (createSdkMcpServer, tool, the built-in\n * Tool helpers, etc.) are passed through unchanged.\n */\nexport function wrapClaudeAgentSDK<T extends Record<string, any>>(\n sdk: T,\n options: WrapClaudeAgentSDKOptions = {},\n): T {\n if (!sdk || typeof sdk.query !== 'function') return sdk;\n if ((sdk as any)._neatlogsWrapped) return sdk;\n\n const wrapped: Record<string, any> = { ...sdk };\n wrapped.query = wrapQuery(sdk.query.bind(sdk), options);\n\n try {\n Object.defineProperty(wrapped, '_neatlogsWrapped', {\n value: true,\n enumerable: false,\n configurable: true,\n });\n } catch {\n wrapped._neatlogsWrapped = true;\n }\n\n return wrapped as T;\n}\n\n// ---------------------------------------------------------------------------\n// query() wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapQuery(original: (...args: any[]) => any, options: WrapClaudeAgentSDKOptions) {\n return function (params: any, ...rest: any[]): any {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const workflowName = options.workflowName ?? 'claude_agent.query';\n\n // Shared ref the input-tap fills with the first user-prompt text. In\n // streaming-input mode the prompt is an async generator fed into the SDK\n // subprocess and is NOT echoed back as a `user` output message before the\n // first assistant turn — so without tapping it, the first LLM span has no\n // input. The tap reads prompt text as the SDK pulls it.\n const promptRef: { text: string } = { text: '' };\n\n // The orchestrator AGENT is the trace ROOT (no WORKFLOW wrapper — a single\n // query() is one agent run; subagents nest below as their own AGENT spans).\n const agentSpan = tracer.startSpan(\n 'claude_agent.query',\n { attributes: { 'neatlogs.span.kind': 'AGENT', 'neatlogs.workflow.name': workflowName } },\n getNeatlogsBaseContext(),\n );\n\n // Input: a string prompt is captured directly. A streaming-input prompt (an\n // async iterable) is tapped — promptRef.text fills as the SDK pulls the\n // first user message — so the agent input and the first LLM span's input are\n // populated even before any `user` message echoes back through the output.\n const promptText = extractPromptText(params?.prompt);\n if (promptText) {\n promptRef.text = promptText;\n agentSpan.setAttribute('input.value', promptText);\n } else if (params && isAsyncIterable(params.prompt)) {\n params = { ...params, prompt: tapPromptStream(params.prompt, promptRef) };\n }\n\n const agentCtx = trace.setSpan(getNeatlogsBaseContext(), agentSpan);\n\n // Call the original query with the AGENT span active only under our private\n // context so a foreign provider's spans neither parent nor nest under ours.\n const queryObj = withNeatlogsSpan(agentSpan, () => original(params, ...rest));\n\n return instrumentQueryIterable(queryObj, agentSpan, agentCtx, tracer, promptRef);\n };\n}\n\n/** True for an async iterable (streaming-input prompt). */\nfunction isAsyncIterable(v: any): boolean {\n return Boolean(v) && typeof v[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Pass-through wrapper over a streaming-input prompt that records the first\n * user message's text into `ref` as the SDK consumes it. Never alters what the\n * SDK receives.\n */\nasync function* tapPromptStream(prompt: any, ref: { text: string }): AsyncGenerator<any> {\n // userMessageText is fully defensive (returns '' for any non-text shape), so\n // no try/catch is needed — the message is always yielded through untouched.\n for await (const message of prompt) {\n if (!ref.text) {\n const text = userMessageText(message);\n if (text) ref.text = text;\n }\n yield message;\n }\n}\n\ninterface ToolCallAccum {\n id: string;\n name: string;\n input: unknown;\n}\n\ninterface AssistantTurnBuffer {\n textParts: string[];\n thinkingParts: string[];\n toolCalls: ToolCallAccum[];\n usage: any;\n model?: string;\n stopReason?: string;\n}\n\n/**\n * One agent's tracing scope. There is always a root scope (the orchestrator,\n * keyed ROOT_SCOPE). Each subagent — identified by the `parent_tool_use_id` of\n * the Task tool call that spawned it — gets its own scope created lazily, with\n * its AGENT span nested under that Task TOOL span. Per-scope state keeps each\n * agent's conversation/turn buffer independent.\n */\ninterface AgentScope {\n /** The AGENT span for this scope (root = orchestrator; others = subagents). */\n span: Span;\n /** OTel context whose active span is this scope's AGENT span (children nest here). */\n ctx: Context;\n /** Running conversation (user/tool/assistant turns) — each LLM span's input. */\n inputMessages: Array<{ role: string; content: string }>;\n /** In-progress model turn, coalesced from multiple `assistant` messages. */\n assistantBuffer: AssistantTurnBuffer | null;\n /** Last assistant text seen (subagent output = its final text). */\n finalText: string;\n /** Whether input.value has been set on this scope's AGENT span. */\n inputCaptured: boolean;\n}\n\ninterface QueryState {\n /** TOOL spans keyed by tool_use_id, closed by the matching tool_result. */\n toolSpans: Map<string, Span>;\n /** Agent scopes keyed by parent_tool_use_id (ROOT_SCOPE for the orchestrator). */\n scopes: Map<string, AgentScope>;\n sessionId?: string;\n model?: string;\n finished: boolean;\n /** Lazily-filled prompt text from a streaming-input tap. */\n promptRef: { text: string };\n}\n\n/**\n * Wrap the Query object so iteration is instrumented while preserving its own\n * methods (interrupt, setPermissionMode, …). The SDK returns an async-iterable\n * object, not a bare generator.\n */\nfunction instrumentQueryIterable(\n queryObj: any,\n agentSpan: Span,\n agentCtx: Context,\n tracer: ReturnType<typeof trace.getTracer>,\n promptRef: { text: string },\n): any {\n const originalAsyncIterator = queryObj?.[Symbol.asyncIterator]?.bind(queryObj);\n if (!originalAsyncIterator) {\n // Not iterable — nothing to trace; close the root span immediately.\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n return queryObj;\n }\n\n // The root scope is the orchestrator agent. Seed its conversation with the\n // prompt (known up front for string prompts; filled by the tap otherwise).\n const rootScope: AgentScope = {\n span: agentSpan,\n ctx: agentCtx,\n inputMessages: promptRef.text ? [{ role: 'user', content: promptRef.text }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(promptRef.text),\n };\n const state: QueryState = {\n toolSpans: new Map(),\n scopes: new Map([[ROOT_SCOPE, rootScope]]),\n finished: false,\n promptRef,\n };\n\n const finalizeAgent = (status: 'ok' | 'error', err?: unknown) => {\n if (state.finished) return;\n state.finished = true;\n // Flush every scope's in-progress turn, then close subagent AGENT spans\n // (deepest-first) and finally the root.\n for (const scope of state.scopes.values()) flushAssistantTurn(tracer, scope, state);\n // Close any tool spans that never got a matching result.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n // Close subagent scopes first (any still open), then the root.\n for (const [key, scope] of state.scopes) {\n if (key === ROOT_SCOPE) continue;\n closeScope(scope, 'ok');\n }\n if (rootScope.finalText) agentSpan.setAttribute('output.value', rootScope.finalText);\n if (status === 'error') {\n recordError(agentSpan, err);\n } else {\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n }\n };\n\n const wrapped = Object.create(Object.getPrototypeOf(queryObj));\n Object.assign(wrapped, queryObj);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await withNeatlogsSpan(agentSpan, () => iterator.next());\n if (result.done) {\n finalizeAgent('ok');\n return result;\n }\n try {\n handleMessage(tracer, state, result.value, finalizeAgent);\n } catch {\n /* never let tracing break the run */\n }\n return result;\n } catch (err) {\n finalizeAgent('error', err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeAgent('ok');\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n finalizeAgent('error', err);\n if (iterator.throw) return iterator.throw(err);\n throw err;\n },\n };\n };\n\n return wrapped;\n}\n\n/** End a subagent scope's AGENT span, setting its output to the subagent's final text. */\nfunction closeScope(scope: AgentScope, status: 'ok' | 'error'): void {\n try {\n if (scope.finalText) scope.span.setAttribute('output.value', scope.finalText);\n scope.span.setStatus({ code: status === 'ok' ? SpanStatusCode.OK : SpanStatusCode.ERROR });\n scope.span.end();\n } catch {\n /* ignore */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message handling\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the agent scope for a message. `parent_tool_use_id` is null for the\n * orchestrator (root scope) and the spawning Task tool_use_id for a subagent.\n * Subagent scopes are created lazily, with their AGENT span nested under the\n * Task TOOL span (looked up by that id) so the hierarchy is\n * orchestrator → Task TOOL → subagent AGENT.\n */\nfunction getScope(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n): AgentScope {\n const parentId = msg?.parent_tool_use_id ?? null;\n if (!parentId) return state.scopes.get(ROOT_SCOPE)!;\n\n const existing = state.scopes.get(parentId);\n if (existing) return existing;\n\n // New subagent. Its spawning Task tool_use may still be buffered in the root\n // turn (the SDK emits subagent messages before the Task's tool_result closes\n // the parent turn). Flush the root turn first so the Task TOOL span exists and\n // this subagent AGENT can nest under it.\n const root = state.scopes.get(ROOT_SCOPE)!;\n if (!state.toolSpans.has(parentId) && root.assistantBuffer) {\n flushAssistantTurn(tracer, root, state);\n }\n\n // Nest the subagent AGENT span under the spawning Task TOOL span if we have it;\n // otherwise under the root agent.\n const parentToolSpan = state.toolSpans.get(parentId);\n const parentCtx = parentToolSpan\n ? trace.setSpan(getNeatlogsBaseContext(), parentToolSpan)\n : state.scopes.get(ROOT_SCOPE)!.ctx;\n\n const subType = msg?.subagent_type ? String(msg.subagent_type) : 'subagent';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'AGENT',\n 'neatlogs.agent.name': subType,\n };\n if (msg?.task_description) attrs['input.value'] = String(msg.task_description);\n const span = tracer.startSpan(`claude_agent.subagent.${subType}`, { attributes: attrs }, parentCtx);\n\n const scope: AgentScope = {\n span,\n ctx: trace.setSpan(getNeatlogsBaseContext(), span),\n inputMessages: msg?.task_description ? [{ role: 'user', content: String(msg.task_description) }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(msg?.task_description),\n };\n state.scopes.set(parentId, scope);\n return scope;\n}\n\nfunction handleMessage(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n finalizeAgent: (status: 'ok' | 'error', err?: unknown) => void,\n): void {\n if (!msg || typeof msg !== 'object') return;\n\n const rootScope = state.scopes.get(ROOT_SCOPE)!;\n\n // Backfill root input from the tapped streaming-input prompt as soon as it's\n // available (it fills before the first assistant turn).\n if (!rootScope.inputCaptured && state.promptRef.text) {\n rootScope.inputCaptured = true;\n rootScope.span.setAttribute('input.value', state.promptRef.text);\n }\n\n switch (msg.type) {\n case 'system': {\n // init message — carries session_id, model, available tools.\n if (msg.session_id) {\n state.sessionId = msg.session_id;\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n if (msg.model) {\n state.model = msg.model;\n rootScope.span.setAttribute('neatlogs.agent.model', String(msg.model));\n }\n break;\n }\n\n case 'user': {\n // A `user` message (prompt, or tool_result turns) is a turn boundary for\n // its scope: flush the buffered turn as ONE LLM span first.\n const scope = getScope(tracer, state, msg);\n flushAssistantTurn(tracer, scope, state);\n\n const userText = userMessageText(msg);\n if (userText) {\n if (!scope.inputCaptured) {\n scope.inputCaptured = true;\n scope.span.setAttribute('input.value', userText);\n }\n scope.inputMessages.push({ role: 'user', content: userText });\n }\n closeToolSpansFromUser(state, scope, msg);\n break;\n }\n\n case 'assistant': {\n // Don't emit yet — the SDK delivers one model turn as multiple `assistant`\n // messages (text block, then tool_use block, …). Buffer them per scope;\n // the next user/result boundary flushes the turn as a single LLM span.\n const scope = getScope(tracer, state, msg);\n bufferAssistantMessage(scope, msg);\n break;\n }\n\n case 'result': {\n // The run is complete. Flush the root turn (a final text answer may have no\n // trailing user message), then finalize.\n flushAssistantTurn(tracer, rootScope, state);\n\n const text = typeof msg.result === 'string' ? msg.result : '';\n if (text) rootScope.finalText = text;\n if (msg.session_id && !state.sessionId) {\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n const usage = msg.usage;\n if (usage) setUsage(rootScope.span, usage);\n if (msg.total_cost_usd != null) rootScope.span.setAttribute('neatlogs.agent.cost_usd', msg.total_cost_usd);\n if (msg.num_turns != null) rootScope.span.setAttribute('neatlogs.agent.num_turns', msg.num_turns);\n if (msg.is_error) rootScope.span.setAttribute('neatlogs.agent.is_error', true);\n\n finalizeAgent(msg.is_error ? 'error' : 'ok', msg.is_error ? new Error(String(text || 'agent run failed')) : undefined);\n break;\n }\n\n default:\n break;\n }\n}\n\n/**\n * Append one `assistant` SDK message to the in-progress turn buffer. The SDK\n * splits a single model turn into multiple assistant messages (a text block,\n * then tool_use blocks); they share token usage. We merge their text, thinking,\n * and tool_use blocks so the turn becomes ONE LLM span on flush.\n */\nfunction bufferAssistantMessage(scope: AgentScope, msg: any): void {\n const message = msg.message ?? msg;\n const content = message?.content ?? [];\n\n if (!scope.assistantBuffer) {\n scope.assistantBuffer = { textParts: [], thinkingParts: [], toolCalls: [], usage: undefined };\n }\n const buf = scope.assistantBuffer;\n if (message?.model) buf.model = message.model;\n if (message?.stop_reason) buf.stopReason = message.stop_reason;\n // Usage is reported per assistant message but is the SAME turn total — keep\n // the largest/last non-empty one rather than summing (summing double-counts).\n if (message?.usage) buf.usage = message.usage;\n\n for (const block of Array.isArray(content) ? content : []) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') buf.textParts.push(block.text);\n else if (block.type === 'thinking' && typeof block.thinking === 'string') buf.thinkingParts.push(block.thinking);\n else if (block.type === 'tool_use') {\n buf.toolCalls.push({ id: block.id ?? '', name: block.name ?? '', input: block.input });\n }\n }\n}\n\n/**\n * Emit the buffered model turn as a SINGLE LLM span, then open TOOL spans for\n * its tool calls. No-op if no turn is buffered. This is what makes one LLM span\n * per real model turn (not per SDK assistant message).\n */\nfunction flushAssistantTurn(\n tracer: ReturnType<typeof trace.getTracer>,\n scope: AgentScope,\n state: QueryState,\n): void {\n const buf = scope.assistantBuffer;\n if (!buf) return;\n scope.assistantBuffer = null;\n\n const model = buf.model ?? state.model ?? '';\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n };\n if (model) attrs['neatlogs.llm.model_name'] = String(model);\n\n // If this is the root scope's first turn and no user message was recorded yet\n // (streaming-input mode), seed input from the tapped prompt text.\n if (scope.inputMessages.length === 0 && state.promptRef.text) {\n scope.inputMessages.push({ role: 'user', content: state.promptRef.text });\n }\n\n // Input = the exact accumulated conversation up to this turn. Emitted BOTH as\n // structured indexed input_messages.* AND as the flat `input.value` blob —\n // per neatlogs/config/attribute-mapping.json, the main UI panel renders\n // `neatlogs.{span_kind}.input` (mapped from `input.value`); the indexed\n // messages alone do NOT populate it. Without input.value the LLM Input is blank.\n scope.inputMessages.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;\n });\n if (scope.inputMessages.length) {\n attrs['input.value'] = safeStringify({ messages: scope.inputMessages });\n }\n\n // Output = the turn's actual assistant content. Prefer the model's text; for a\n // tool-only turn (no text) the output IS the tool call(s), so render them as\n // the exact `name(arguments)` the model emitted (not a vague summary). The\n // structured tool_calls.* below still carry the same data for programmatic use.\n // `output.value` is the flat blob the UI maps to `neatlogs.{span_kind}.output`.\n const outText = buf.textParts.join('');\n const outValue =\n outText ||\n buf.toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.input ?? {})})`).join('\\n');\n if (outValue) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = outValue;\n attrs['output.value'] = outValue;\n }\n if (buf.thinkingParts.length) {\n attrs['neatlogs.llm.output_messages.0.thinking'] = buf.thinkingParts.join('');\n }\n buf.toolCalls.forEach((tc, j) => {\n attrs[`neatlogs.llm.tool_calls.${j}.id`] = tc.id;\n attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.input ?? {});\n });\n if (buf.stopReason) attrs['neatlogs.llm.finish_reason'] = String(buf.stopReason);\n\n // The LLM span nests under THIS scope's AGENT span.\n const span = tracer.startSpan(`claude_agent.llm.${model || 'model'}`, { attributes: attrs }, scope.ctx);\n if (buf.usage) setUsage(span, buf.usage);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n\n // Track this scope's latest text as its output (the subagent/orchestrator\n // final answer is the last assistant text).\n if (outText) scope.finalText = outText;\n\n // Record this assistant turn so the NEXT turn's LLM span sees it as context.\n const turnParts: string[] = [];\n if (outText) turnParts.push(outText);\n for (const tc of buf.toolCalls) turnParts.push(`[tool_call ${tc.name} ${safeStringify(tc.input ?? {})}]`);\n if (turnParts.length) scope.inputMessages.push({ role: 'assistant', content: turnParts.join('\\n') });\n\n // Open TOOL spans for this turn's tool calls, nested under THIS scope's AGENT\n // span (closed by their tool_result). A Task tool's id becomes the key a\n // subagent's messages resolve to (see getScope).\n for (const tc of buf.toolCalls) {\n const toolSpan = tracer.startSpan(\n `claude_agent.tool.${tc.name || 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': String(tc.name ?? ''),\n ...(tc.id ? { 'neatlogs.tool_call.id': String(tc.id) } : {}),\n 'input.value': safeStringify(tc.input ?? {}),\n },\n },\n scope.ctx,\n );\n if (tc.id) state.toolSpans.set(tc.id, toolSpan);\n }\n}\n\nfunction closeToolSpansFromUser(state: QueryState, scope: AgentScope, msg: any): void {\n const content = (msg.message ?? msg)?.content ?? [];\n for (const block of Array.isArray(content) ? content : []) {\n if (block?.type !== 'tool_result') continue;\n const id = block.tool_use_id ?? '';\n const out = block.content;\n const outText = (typeof out === 'string' ? out : safeStringify(out));\n // Feed the tool result into THIS scope's conversation so the next LLM span's\n // input reflects what the model actually saw.\n if (outText) scope.inputMessages.push({ role: 'tool', content: outText });\n const span = state.toolSpans.get(id);\n if (!span) continue;\n span.setAttribute('output.value', outText);\n if (block.is_error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n state.toolSpans.delete(id);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the human/text content of a `user` SDK message — the prompt, not\n * tool_result blocks (those are handled separately as tool outputs).\n */\nfunction userMessageText(msg: any): string {\n const content = (msg.message ?? msg)?.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string') {\n parts.push(block.text);\n }\n }\n return parts.join('\\n');\n}\n\nfunction setUsage(span: Span, usage: any): void {\n if (!usage) return;\n if (usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.input_tokens);\n if (usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.output_tokens);\n if (usage.input_tokens != null && usage.output_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.total', usage.input_tokens + usage.output_tokens);\n }\n if (usage.cache_read_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.cache_read_input_tokens);\n }\n if (usage.cache_creation_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_write', usage.cache_creation_input_tokens);\n }\n}\n\nfunction extractPromptText(prompt: any): string {\n if (typeof prompt === 'string') return prompt;\n // Streaming-input mode passes an async iterable of messages; we can't read it\n // synchronously without consuming it, so leave input.value unset in that case.\n return '';\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return 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 * 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 trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\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 return providerState.provider?.getTracer(name) ?? preInitTracer;\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 return providerState.provider;\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 * 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 const ctx = otelTrace.setSpan(base, span);\n return privateContextStorage.run(ctx, fn);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/claude-agent-sdk.ts","../src/core/provider.ts"],"sourcesContent":["/**\n * Neatlogs Claude Agent SDK integration.\n *\n * Wraps Anthropic's `@anthropic-ai/claude-agent-sdk` so every `query()` run is\n * traced. The SDK's `query()` returns an async-iterable of SDKMessages\n * (system init → assistant (text + tool_use) → user (tool_result) → … →\n * result). Each message carries `parent_tool_use_id`: null for the main\n * (orchestrator) agent, or the id of the spawning Task tool call for a subagent.\n * We translate that into a neatlogs span tree:\n *\n * AGENT claude_agent.query (orchestrator = trace root)\n * ↳ LLM orchestrator turn (one per model turn; text + tool_calls)\n * ↳ TOOL Read / Edit / Bash …\n * ↳ TOOL Task (spawns a subagent)\n * ↳ AGENT claude_agent.subagent.<type> (each subagent, nested)\n * ↳ LLM subagent turn\n * ↳ TOOL subagent tool call\n *\n * The orchestrator is the single root AGENT span — there is NO redundant WORKFLOW\n * wrapper. Subagents (Task-tool invocations, e.g. the wizard's parallel per-file\n * edits) get their own AGENT spans nested under the Task TOOL span that spawned\n * them, so a multi-agent run is represented faithfully and each agent's I/O is\n * distinct.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { wrapClaudeAgentSDK } from 'neatlogs/claude-agent-sdk';\n * import * as claudeAgentSDK from '@anthropic-ai/claude-agent-sdk';\n *\n * await init({ apiKey, workflowName });\n * const { query } = wrapClaudeAgentSDK(claudeAgentSDK);\n * for await (const msg of query({ prompt: 'Hello', options: {...} })) { ... }\n *\n * Conversation tracking: Claude's `session_id` is captured on the root AGENT span\n * as `neatlogs.conversation.id`. Tool calls are traced only through the wrapped\n * `query` — calling the unwrapped SDK directly produces no tracing.\n */\n\nimport { trace, SpanStatusCode, type Span, type Context } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsBaseContext, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.claude_agent_sdk';\nconst ROOT_SCOPE = '__root__';\n\nexport interface WrapClaudeAgentSDKOptions {\n /** Logical grouping for traces (also settable globally via init({ workflowName })). */\n workflowName?: string;\n}\n\n/**\n * Wrap the Claude Agent SDK module. Returns a shallow copy of the module with an\n * instrumented `query`; all other exports (createSdkMcpServer, tool, the built-in\n * Tool helpers, etc.) are passed through unchanged.\n */\nexport function wrapClaudeAgentSDK<T extends Record<string, any>>(\n sdk: T,\n options: WrapClaudeAgentSDKOptions = {},\n): T {\n if (!sdk || typeof sdk.query !== 'function') return sdk;\n if ((sdk as any)._neatlogsWrapped) return sdk;\n\n const wrapped: Record<string, any> = { ...sdk };\n wrapped.query = wrapQuery(sdk.query.bind(sdk), options);\n\n try {\n Object.defineProperty(wrapped, '_neatlogsWrapped', {\n value: true,\n enumerable: false,\n configurable: true,\n });\n } catch {\n wrapped._neatlogsWrapped = true;\n }\n\n return wrapped as T;\n}\n\n// ---------------------------------------------------------------------------\n// query() wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapQuery(original: (...args: any[]) => any, options: WrapClaudeAgentSDKOptions) {\n return function (params: any, ...rest: any[]): any {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n const workflowName = options.workflowName ?? 'claude_agent.query';\n\n // Shared ref the input-tap fills with the first user-prompt text. In\n // streaming-input mode the prompt is an async generator fed into the SDK\n // subprocess and is NOT echoed back as a `user` output message before the\n // first assistant turn — so without tapping it, the first LLM span has no\n // input. The tap reads prompt text as the SDK pulls it.\n const promptRef: { text: string } = { text: '' };\n\n // The orchestrator AGENT is the trace ROOT (no WORKFLOW wrapper — a single\n // query() is one agent run; subagents nest below as their own AGENT spans).\n const agentSpan = tracer.startSpan(\n 'claude_agent.query',\n { attributes: { 'neatlogs.span.kind': 'AGENT', 'neatlogs.workflow.name': workflowName } },\n getNeatlogsBaseContext(),\n );\n\n // Input: a string prompt is captured directly. A streaming-input prompt (an\n // async iterable) is tapped — promptRef.text fills as the SDK pulls the\n // first user message — so the agent input and the first LLM span's input are\n // populated even before any `user` message echoes back through the output.\n const promptText = extractPromptText(params?.prompt);\n if (promptText) {\n promptRef.text = promptText;\n agentSpan.setAttribute('input.value', promptText);\n } else if (params && isAsyncIterable(params.prompt)) {\n params = { ...params, prompt: tapPromptStream(params.prompt, promptRef) };\n }\n\n const agentCtx = trace.setSpan(getNeatlogsBaseContext(), agentSpan);\n\n // Call the original query with the AGENT span active only under our private\n // context so a foreign provider's spans neither parent nor nest under ours.\n const queryObj = withNeatlogsSpan(agentSpan, () => original(params, ...rest));\n\n return instrumentQueryIterable(queryObj, agentSpan, agentCtx, tracer, promptRef);\n };\n}\n\n/** True for an async iterable (streaming-input prompt). */\nfunction isAsyncIterable(v: any): boolean {\n return Boolean(v) && typeof v[Symbol.asyncIterator] === 'function';\n}\n\n/**\n * Pass-through wrapper over a streaming-input prompt that records the first\n * user message's text into `ref` as the SDK consumes it. Never alters what the\n * SDK receives.\n */\nasync function* tapPromptStream(prompt: any, ref: { text: string }): AsyncGenerator<any> {\n // userMessageText is fully defensive (returns '' for any non-text shape), so\n // no try/catch is needed — the message is always yielded through untouched.\n for await (const message of prompt) {\n if (!ref.text) {\n const text = userMessageText(message);\n if (text) ref.text = text;\n }\n yield message;\n }\n}\n\ninterface ToolCallAccum {\n id: string;\n name: string;\n input: unknown;\n}\n\ninterface AssistantTurnBuffer {\n textParts: string[];\n thinkingParts: string[];\n toolCalls: ToolCallAccum[];\n usage: any;\n model?: string;\n stopReason?: string;\n}\n\n/**\n * One agent's tracing scope. There is always a root scope (the orchestrator,\n * keyed ROOT_SCOPE). Each subagent — identified by the `parent_tool_use_id` of\n * the Task tool call that spawned it — gets its own scope created lazily, with\n * its AGENT span nested under that Task TOOL span. Per-scope state keeps each\n * agent's conversation/turn buffer independent.\n */\ninterface AgentScope {\n /** The AGENT span for this scope (root = orchestrator; others = subagents). */\n span: Span;\n /** OTel context whose active span is this scope's AGENT span (children nest here). */\n ctx: Context;\n /** Running conversation (user/tool/assistant turns) — each LLM span's input. */\n inputMessages: Array<{ role: string; content: string }>;\n /** In-progress model turn, coalesced from multiple `assistant` messages. */\n assistantBuffer: AssistantTurnBuffer | null;\n /** Last assistant text seen (subagent output = its final text). */\n finalText: string;\n /** Whether input.value has been set on this scope's AGENT span. */\n inputCaptured: boolean;\n}\n\ninterface QueryState {\n /** TOOL spans keyed by tool_use_id, closed by the matching tool_result. */\n toolSpans: Map<string, Span>;\n /** Agent scopes keyed by parent_tool_use_id (ROOT_SCOPE for the orchestrator). */\n scopes: Map<string, AgentScope>;\n sessionId?: string;\n model?: string;\n finished: boolean;\n /** Lazily-filled prompt text from a streaming-input tap. */\n promptRef: { text: string };\n}\n\n/**\n * Wrap the Query object so iteration is instrumented while preserving its own\n * methods (interrupt, setPermissionMode, …). The SDK returns an async-iterable\n * object, not a bare generator.\n */\nfunction instrumentQueryIterable(\n queryObj: any,\n agentSpan: Span,\n agentCtx: Context,\n tracer: ReturnType<typeof trace.getTracer>,\n promptRef: { text: string },\n): any {\n const originalAsyncIterator = queryObj?.[Symbol.asyncIterator]?.bind(queryObj);\n if (!originalAsyncIterator) {\n // Not iterable — nothing to trace; close the root span immediately.\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n return queryObj;\n }\n\n // The root scope is the orchestrator agent. Seed its conversation with the\n // prompt (known up front for string prompts; filled by the tap otherwise).\n const rootScope: AgentScope = {\n span: agentSpan,\n ctx: agentCtx,\n inputMessages: promptRef.text ? [{ role: 'user', content: promptRef.text }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(promptRef.text),\n };\n const state: QueryState = {\n toolSpans: new Map(),\n scopes: new Map([[ROOT_SCOPE, rootScope]]),\n finished: false,\n promptRef,\n };\n\n const finalizeAgent = (status: 'ok' | 'error', err?: unknown) => {\n if (state.finished) return;\n state.finished = true;\n // Flush every scope's in-progress turn, then close subagent AGENT spans\n // (deepest-first) and finally the root.\n for (const scope of state.scopes.values()) flushAssistantTurn(tracer, scope, state);\n // Close any tool spans that never got a matching result.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n // Close subagent scopes first (any still open), then the root.\n for (const [key, scope] of state.scopes) {\n if (key === ROOT_SCOPE) continue;\n closeScope(scope, 'ok');\n }\n if (rootScope.finalText) agentSpan.setAttribute('output.value', rootScope.finalText);\n if (status === 'error') {\n recordError(agentSpan, err);\n } else {\n agentSpan.setStatus({ code: SpanStatusCode.OK });\n agentSpan.end();\n }\n };\n\n const wrapped = Object.create(Object.getPrototypeOf(queryObj));\n Object.assign(wrapped, queryObj);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await withNeatlogsSpan(agentSpan, () => iterator.next());\n if (result.done) {\n finalizeAgent('ok');\n return result;\n }\n try {\n handleMessage(tracer, state, result.value, finalizeAgent);\n } catch {\n /* never let tracing break the run */\n }\n return result;\n } catch (err) {\n finalizeAgent('error', err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeAgent('ok');\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n finalizeAgent('error', err);\n if (iterator.throw) return iterator.throw(err);\n throw err;\n },\n };\n };\n\n return wrapped;\n}\n\n/** End a subagent scope's AGENT span, setting its output to the subagent's final text. */\nfunction closeScope(scope: AgentScope, status: 'ok' | 'error'): void {\n try {\n if (scope.finalText) scope.span.setAttribute('output.value', scope.finalText);\n scope.span.setStatus({ code: status === 'ok' ? SpanStatusCode.OK : SpanStatusCode.ERROR });\n scope.span.end();\n } catch {\n /* ignore */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message handling\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the agent scope for a message. `parent_tool_use_id` is null for the\n * orchestrator (root scope) and the spawning Task tool_use_id for a subagent.\n * Subagent scopes are created lazily, with their AGENT span nested under the\n * Task TOOL span (looked up by that id) so the hierarchy is\n * orchestrator → Task TOOL → subagent AGENT.\n */\nfunction getScope(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n): AgentScope {\n const parentId = msg?.parent_tool_use_id ?? null;\n if (!parentId) return state.scopes.get(ROOT_SCOPE)!;\n\n const existing = state.scopes.get(parentId);\n if (existing) return existing;\n\n // New subagent. Its spawning Task tool_use may still be buffered in the root\n // turn (the SDK emits subagent messages before the Task's tool_result closes\n // the parent turn). Flush the root turn first so the Task TOOL span exists and\n // this subagent AGENT can nest under it.\n const root = state.scopes.get(ROOT_SCOPE)!;\n if (!state.toolSpans.has(parentId) && root.assistantBuffer) {\n flushAssistantTurn(tracer, root, state);\n }\n\n // Nest the subagent AGENT span under the spawning Task TOOL span if we have it;\n // otherwise under the root agent.\n const parentToolSpan = state.toolSpans.get(parentId);\n const parentCtx = parentToolSpan\n ? trace.setSpan(getNeatlogsBaseContext(), parentToolSpan)\n : state.scopes.get(ROOT_SCOPE)!.ctx;\n\n const subType = msg?.subagent_type ? String(msg.subagent_type) : 'subagent';\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'AGENT',\n 'neatlogs.agent.name': subType,\n };\n if (msg?.task_description) attrs['input.value'] = String(msg.task_description);\n const span = tracer.startSpan(`claude_agent.subagent.${subType}`, { attributes: attrs }, parentCtx);\n\n const scope: AgentScope = {\n span,\n ctx: trace.setSpan(getNeatlogsBaseContext(), span),\n inputMessages: msg?.task_description ? [{ role: 'user', content: String(msg.task_description) }] : [],\n assistantBuffer: null,\n finalText: '',\n inputCaptured: Boolean(msg?.task_description),\n };\n state.scopes.set(parentId, scope);\n return scope;\n}\n\nfunction handleMessage(\n tracer: ReturnType<typeof trace.getTracer>,\n state: QueryState,\n msg: any,\n finalizeAgent: (status: 'ok' | 'error', err?: unknown) => void,\n): void {\n if (!msg || typeof msg !== 'object') return;\n\n const rootScope = state.scopes.get(ROOT_SCOPE)!;\n\n // Backfill root input from the tapped streaming-input prompt as soon as it's\n // available (it fills before the first assistant turn).\n if (!rootScope.inputCaptured && state.promptRef.text) {\n rootScope.inputCaptured = true;\n rootScope.span.setAttribute('input.value', state.promptRef.text);\n }\n\n switch (msg.type) {\n case 'system': {\n // init message — carries session_id, model, available tools.\n if (msg.session_id) {\n state.sessionId = msg.session_id;\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n if (msg.model) {\n state.model = msg.model;\n rootScope.span.setAttribute('neatlogs.agent.model', String(msg.model));\n }\n break;\n }\n\n case 'user': {\n // A `user` message (prompt, or tool_result turns) is a turn boundary for\n // its scope: flush the buffered turn as ONE LLM span first.\n const scope = getScope(tracer, state, msg);\n flushAssistantTurn(tracer, scope, state);\n\n const userText = userMessageText(msg);\n if (userText) {\n if (!scope.inputCaptured) {\n scope.inputCaptured = true;\n scope.span.setAttribute('input.value', userText);\n }\n scope.inputMessages.push({ role: 'user', content: userText });\n }\n closeToolSpansFromUser(state, scope, msg);\n break;\n }\n\n case 'assistant': {\n // Don't emit yet — the SDK delivers one model turn as multiple `assistant`\n // messages (text block, then tool_use block, …). Buffer them per scope;\n // the next user/result boundary flushes the turn as a single LLM span.\n const scope = getScope(tracer, state, msg);\n bufferAssistantMessage(scope, msg);\n break;\n }\n\n case 'result': {\n // The run is complete. Flush the root turn (a final text answer may have no\n // trailing user message), then finalize.\n flushAssistantTurn(tracer, rootScope, state);\n\n const text = typeof msg.result === 'string' ? msg.result : '';\n if (text) rootScope.finalText = text;\n if (msg.session_id && !state.sessionId) {\n rootScope.span.setAttribute('neatlogs.conversation.id', String(msg.session_id));\n }\n const usage = msg.usage;\n if (usage) setUsage(rootScope.span, usage);\n if (msg.total_cost_usd != null) rootScope.span.setAttribute('neatlogs.agent.cost_usd', msg.total_cost_usd);\n if (msg.num_turns != null) rootScope.span.setAttribute('neatlogs.agent.num_turns', msg.num_turns);\n if (msg.is_error) rootScope.span.setAttribute('neatlogs.agent.is_error', true);\n\n finalizeAgent(msg.is_error ? 'error' : 'ok', msg.is_error ? new Error(String(text || 'agent run failed')) : undefined);\n break;\n }\n\n default:\n break;\n }\n}\n\n/**\n * Append one `assistant` SDK message to the in-progress turn buffer. The SDK\n * splits a single model turn into multiple assistant messages (a text block,\n * then tool_use blocks); they share token usage. We merge their text, thinking,\n * and tool_use blocks so the turn becomes ONE LLM span on flush.\n */\nfunction bufferAssistantMessage(scope: AgentScope, msg: any): void {\n const message = msg.message ?? msg;\n const content = message?.content ?? [];\n\n if (!scope.assistantBuffer) {\n scope.assistantBuffer = { textParts: [], thinkingParts: [], toolCalls: [], usage: undefined };\n }\n const buf = scope.assistantBuffer;\n if (message?.model) buf.model = message.model;\n if (message?.stop_reason) buf.stopReason = message.stop_reason;\n // Usage is reported per assistant message but is the SAME turn total — keep\n // the largest/last non-empty one rather than summing (summing double-counts).\n if (message?.usage) buf.usage = message.usage;\n\n for (const block of Array.isArray(content) ? content : []) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') buf.textParts.push(block.text);\n else if (block.type === 'thinking' && typeof block.thinking === 'string') buf.thinkingParts.push(block.thinking);\n else if (block.type === 'tool_use') {\n buf.toolCalls.push({ id: block.id ?? '', name: block.name ?? '', input: block.input });\n }\n }\n}\n\n/**\n * Emit the buffered model turn as a SINGLE LLM span, then open TOOL spans for\n * its tool calls. No-op if no turn is buffered. This is what makes one LLM span\n * per real model turn (not per SDK assistant message).\n */\nfunction flushAssistantTurn(\n tracer: ReturnType<typeof trace.getTracer>,\n scope: AgentScope,\n state: QueryState,\n): void {\n const buf = scope.assistantBuffer;\n if (!buf) return;\n scope.assistantBuffer = null;\n\n const model = buf.model ?? state.model ?? '';\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n };\n if (model) attrs['neatlogs.llm.model_name'] = String(model);\n\n // If this is the root scope's first turn and no user message was recorded yet\n // (streaming-input mode), seed input from the tapped prompt text.\n if (scope.inputMessages.length === 0 && state.promptRef.text) {\n scope.inputMessages.push({ role: 'user', content: state.promptRef.text });\n }\n\n // Input = the exact accumulated conversation up to this turn. Emitted BOTH as\n // structured indexed input_messages.* AND as the flat `input.value` blob —\n // per neatlogs/config/attribute-mapping.json, the main UI panel renders\n // `neatlogs.{span_kind}.input` (mapped from `input.value`); the indexed\n // messages alone do NOT populate it. Without input.value the LLM Input is blank.\n scope.inputMessages.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;\n });\n if (scope.inputMessages.length) {\n attrs['input.value'] = safeStringify({ messages: scope.inputMessages });\n }\n\n // Output = the turn's actual assistant content. Prefer the model's text; for a\n // tool-only turn (no text) the output IS the tool call(s), so render them as\n // the exact `name(arguments)` the model emitted (not a vague summary). The\n // structured tool_calls.* below still carry the same data for programmatic use.\n // `output.value` is the flat blob the UI maps to `neatlogs.{span_kind}.output`.\n const outText = buf.textParts.join('');\n const outValue =\n outText ||\n buf.toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.input ?? {})})`).join('\\n');\n if (outValue) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = outValue;\n attrs['output.value'] = outValue;\n }\n if (buf.thinkingParts.length) {\n attrs['neatlogs.llm.output_messages.0.thinking'] = buf.thinkingParts.join('');\n }\n buf.toolCalls.forEach((tc, j) => {\n attrs[`neatlogs.llm.tool_calls.${j}.id`] = tc.id;\n attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.input ?? {});\n });\n if (buf.stopReason) attrs['neatlogs.llm.finish_reason'] = String(buf.stopReason);\n\n // The LLM span nests under THIS scope's AGENT span.\n const span = tracer.startSpan(`claude_agent.llm.${model || 'model'}`, { attributes: attrs }, scope.ctx);\n if (buf.usage) setUsage(span, buf.usage);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n\n // Track this scope's latest text as its output (the subagent/orchestrator\n // final answer is the last assistant text).\n if (outText) scope.finalText = outText;\n\n // Record this assistant turn so the NEXT turn's LLM span sees it as context.\n const turnParts: string[] = [];\n if (outText) turnParts.push(outText);\n for (const tc of buf.toolCalls) turnParts.push(`[tool_call ${tc.name} ${safeStringify(tc.input ?? {})}]`);\n if (turnParts.length) scope.inputMessages.push({ role: 'assistant', content: turnParts.join('\\n') });\n\n // Open TOOL spans for this turn's tool calls, nested under THIS scope's AGENT\n // span (closed by their tool_result). A Task tool's id becomes the key a\n // subagent's messages resolve to (see getScope).\n for (const tc of buf.toolCalls) {\n const toolSpan = tracer.startSpan(\n `claude_agent.tool.${tc.name || 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': String(tc.name ?? ''),\n ...(tc.id ? { 'neatlogs.tool_call.id': String(tc.id) } : {}),\n 'input.value': safeStringify(tc.input ?? {}),\n },\n },\n scope.ctx,\n );\n if (tc.id) state.toolSpans.set(tc.id, toolSpan);\n }\n}\n\nfunction closeToolSpansFromUser(state: QueryState, scope: AgentScope, msg: any): void {\n const content = (msg.message ?? msg)?.content ?? [];\n for (const block of Array.isArray(content) ? content : []) {\n if (block?.type !== 'tool_result') continue;\n const id = block.tool_use_id ?? '';\n const out = block.content;\n const outText = (typeof out === 'string' ? out : safeStringify(out));\n // Feed the tool result into THIS scope's conversation so the next LLM span's\n // input reflects what the model actually saw.\n if (outText) scope.inputMessages.push({ role: 'tool', content: outText });\n const span = state.toolSpans.get(id);\n if (!span) continue;\n span.setAttribute('output.value', outText);\n if (block.is_error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n state.toolSpans.delete(id);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the human/text content of a `user` SDK message — the prompt, not\n * tool_result blocks (those are handled separately as tool outputs).\n */\nfunction userMessageText(msg: any): string {\n const content = (msg.message ?? msg)?.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string') {\n parts.push(block.text);\n }\n }\n return parts.join('\\n');\n}\n\nfunction setUsage(span: Span, usage: any): void {\n if (!usage) return;\n if (usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.input_tokens);\n if (usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.output_tokens);\n if (usage.input_tokens != null && usage.output_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.total', usage.input_tokens + usage.output_tokens);\n }\n if (usage.cache_read_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.cache_read_input_tokens);\n }\n if (usage.cache_creation_input_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_write', usage.cache_creation_input_tokens);\n }\n}\n\nfunction extractPromptText(prompt: any): string {\n if (typeof prompt === 'string') return prompt;\n // Streaming-input mode passes an async iterable of messages; we can't read it\n // synchronously without consuming it, so leave input.value unset in that case.\n return '';\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return 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 * 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';\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 return providerState.provider?.getTracer(name) ?? preInitTracer;\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 return providerState.provider;\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"],"mappings":"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","names":[]}
|
package/dist/google-genai.cjs
CHANGED
|
@@ -102,6 +102,7 @@ function currentEndUserMetadata() {
|
|
|
102
102
|
// src/core/provider.ts
|
|
103
103
|
var import_node_async_hooks3 = require("async_hooks");
|
|
104
104
|
var import_api = require("@opentelemetry/api");
|
|
105
|
+
var NEATLOGS_ROOT_SPAN_KEY = (0, import_api.createContextKey)("neatlogs.root_span");
|
|
105
106
|
var PRIVATE_SPAN_STORAGE_KEY = /* @__PURE__ */ Symbol.for(
|
|
106
107
|
"neatlogs.private_span_async_local_storage"
|
|
107
108
|
);
|
|
@@ -136,7 +137,10 @@ function getNeatlogsParentContext(baseContext) {
|
|
|
136
137
|
}
|
|
137
138
|
function withNeatlogsSpan(span, fn, baseContext) {
|
|
138
139
|
const base = baseContext ?? getNeatlogsActiveContext();
|
|
139
|
-
|
|
140
|
+
let ctx = import_api.trace.setSpan(base, span);
|
|
141
|
+
if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === void 0) {
|
|
142
|
+
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);
|
|
143
|
+
}
|
|
140
144
|
return privateContextStorage.run(ctx, fn);
|
|
141
145
|
}
|
|
142
146
|
|