neatlogs 1.1.18 → 1.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +172 -82
  2. package/dist/ai-sdk.cjs +34 -5
  3. package/dist/ai-sdk.cjs.map +1 -1
  4. package/dist/ai-sdk.mjs +34 -5
  5. package/dist/ai-sdk.mjs.map +1 -1
  6. package/dist/anthropic.cjs +886 -66
  7. package/dist/anthropic.cjs.map +1 -1
  8. package/dist/anthropic.mjs +890 -67
  9. package/dist/anthropic.mjs.map +1 -1
  10. package/dist/azure-openai.cjs +1135 -82
  11. package/dist/azure-openai.cjs.map +1 -1
  12. package/dist/azure-openai.mjs +1139 -83
  13. package/dist/azure-openai.mjs.map +1 -1
  14. package/dist/bedrock.cjs +897 -53
  15. package/dist/bedrock.cjs.map +1 -1
  16. package/dist/bedrock.mjs +901 -54
  17. package/dist/bedrock.mjs.map +1 -1
  18. package/dist/browser.cjs +8 -3
  19. package/dist/browser.cjs.map +1 -1
  20. package/dist/browser.mjs +6 -3
  21. package/dist/browser.mjs.map +1 -1
  22. package/dist/claude-agent-sdk.cjs +25 -5
  23. package/dist/claude-agent-sdk.cjs.map +1 -1
  24. package/dist/claude-agent-sdk.mjs +25 -5
  25. package/dist/claude-agent-sdk.mjs.map +1 -1
  26. package/dist/cli.cjs +12827 -0
  27. package/dist/cli.cjs.map +1 -0
  28. package/dist/cli.d.ts +1 -0
  29. package/dist/cli.mjs +12824 -0
  30. package/dist/cli.mjs.map +1 -0
  31. package/dist/google-genai.cjs +836 -32
  32. package/dist/google-genai.cjs.map +1 -1
  33. package/dist/google-genai.d.ts +2 -2
  34. package/dist/google-genai.mjs +840 -33
  35. package/dist/google-genai.mjs.map +1 -1
  36. package/dist/index.cjs +11064 -3844
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.ts +1656 -66
  39. package/dist/index.mjs +11168 -3960
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/langchain.cjs +66 -4
  42. package/dist/langchain.cjs.map +1 -1
  43. package/dist/langchain.mjs +70 -5
  44. package/dist/langchain.mjs.map +1 -1
  45. package/dist/mastra-wrap.cjs +19 -4
  46. package/dist/mastra-wrap.cjs.map +1 -1
  47. package/dist/mastra-wrap.mjs +19 -4
  48. package/dist/mastra-wrap.mjs.map +1 -1
  49. package/dist/openai-agents.cjs +19 -4
  50. package/dist/openai-agents.cjs.map +1 -1
  51. package/dist/openai-agents.mjs +19 -4
  52. package/dist/openai-agents.mjs.map +1 -1
  53. package/dist/openai.cjs +1375 -196
  54. package/dist/openai.cjs.map +1 -1
  55. package/dist/openai.mjs +1379 -197
  56. package/dist/openai.mjs.map +1 -1
  57. package/dist/opencode-plugin.cjs +8 -7
  58. package/dist/opencode-plugin.cjs.map +1 -1
  59. package/dist/opencode-plugin.d.ts +0 -2
  60. package/dist/opencode-plugin.mjs +8 -7
  61. package/dist/opencode-plugin.mjs.map +1 -1
  62. package/dist/openrouter-agent.cjs +75 -5
  63. package/dist/openrouter-agent.cjs.map +1 -1
  64. package/dist/openrouter-agent.mjs +79 -6
  65. package/dist/openrouter-agent.mjs.map +1 -1
  66. package/dist/pi-agent.cjs +21 -6
  67. package/dist/pi-agent.cjs.map +1 -1
  68. package/dist/pi-agent.mjs +21 -6
  69. package/dist/pi-agent.mjs.map +1 -1
  70. package/dist/vertex-ai.cjs +836 -32
  71. package/dist/vertex-ai.cjs.map +1 -1
  72. package/dist/vertex-ai.mjs +840 -33
  73. package/dist/vertex-ai.mjs.map +1 -1
  74. package/package.json +10 -43
package/README.md CHANGED
@@ -7,18 +7,15 @@ Automatically trace LLM calls, agent workflows, tool invocations, and retrieval
7
7
  ## Quick Start
8
8
 
9
9
  ```typescript
10
- import { init, span, shutdown } from 'neatlogs';
10
+ import { init, span, shutdown, wrapOpenAI } from 'neatlogs';
11
11
  import OpenAI from 'openai';
12
12
 
13
13
  async function main() {
14
14
  // 1. Initialize the SDK
15
- await init({
16
- apiKey: process.env.NEATLOGS_API_KEY,
17
- instrumentations: ['openai'],
18
- });
15
+ await init({ apiKey: process.env.NEATLOGS_API_KEY });
19
16
 
20
- // 2. Create your LLM client AFTER init()
21
- const client = new OpenAI();
17
+ // 2. Explicitly wrap the provider client
18
+ const client = wrapOpenAI(new OpenAI());
22
19
 
23
20
  // 3. Wrap functions with span() for observability
24
21
  const myWorkflow = span({ kind: 'WORKFLOW', name: 'qa-bot' }, async (query: string) => {
@@ -44,56 +41,49 @@ main().catch(console.error);
44
41
  npm install neatlogs
45
42
  ```
46
43
 
47
- For auto-instrumentation of specific LLM providers, install the corresponding peer dependency:
48
-
49
- ```bash
50
- # OpenAI
51
- npm install @arizeai/openinference-instrumentation-openai
52
-
53
- # Anthropic
54
- npm install @arizeai/openinference-instrumentation-anthropic
55
-
56
- # AWS Bedrock
57
- npm install @arizeai/openinference-instrumentation-bedrock
58
-
59
- # LangChain
60
- npm install @arizeai/openinference-instrumentation-langchain
61
-
62
- # MCP (Model Context Protocol)
63
- npm install @arizeai/openinference-instrumentation-mcp
64
-
65
- # BeeAI
66
- npm install @arizeai/openinference-instrumentation-beeai
67
-
68
- # Claude Agent SDK
69
- npm install @arizeai/openinference-instrumentation-claude-agent-sdk
70
-
71
- # Google GenAI (@google/genai)
72
- npm install @google/genai
73
- ```
44
+ Install the provider or framework package you already use, then apply its
45
+ documented Neatlogs wrapper, hook, processor, or telemetry helper.
74
46
 
75
47
  ## Core Concepts
76
48
 
77
49
  | Function | Purpose |
78
50
  |----------|---------|
79
- | `init()` | Initialize the SDK — sets up OTel providers, exporters, and instrumentation |
51
+ | `init()` | Initialize the SDK — sets up private OTel providers and exporters |
80
52
  | `span()` | Wrap a function with observability — captures inputs, outputs, timing, and errors |
81
53
  | `trace()` | Create a manual span with prompt template tracking and multi-turn session support |
82
54
  | `log()` | Capture timestamped log steps within the active trace |
83
55
  | `shutdown()` | Flush all pending data and shut down the SDK gracefully |
84
56
 
85
- ### Important: Initialization Order
57
+ ## Doctor v2
86
58
 
87
- `init()` is **async** and must be called **before** creating any LLM client instances. This is because instrumentation works by monkey-patching libraries at init time.
59
+ Run the local SDK pipeline check without credentials or network access:
88
60
 
89
- ```typescript
90
- // Correct
91
- await init({ instrumentations: ['openai'] });
92
- const client = new OpenAI(); // patched
61
+ ```bash
62
+ npx neatlogs doctor --local --json
63
+ ```
93
64
 
94
- // Wrong client created before patching
95
- const client = new OpenAI(); // NOT patched
96
- await init({ instrumentations: ['openai'] });
65
+ Run the controlled end-to-end probe explicitly:
66
+
67
+ ```bash
68
+ NEATLOGS_API_KEY=<project-key> \
69
+ NEATLOGS_ENDPOINT=https://ingest.neatlogs.com \
70
+ npx neatlogs doctor --probe --json
71
+ ```
72
+
73
+ Probe mode exports four generated spans through the normal `/v1/traces` route
74
+ with `x-neatlogs-doctor: v1`, flushes, and polls
75
+ `/api/traces/v3/:traceId` for that exact trace. It passes only after persisted
76
+ hierarchy, span semantics, input/output, versioned metadata, and numeric token
77
+ totals validate. It does not call an LLM or inspect user data.
78
+
79
+ ### Important: Explicit integration
80
+
81
+ `init()` does not monkey-patch provider libraries. Use the documented explicit
82
+ wrapper, hook, processor, or telemetry helper for each integration.
83
+
84
+ ```typescript
85
+ await init({ apiKey: process.env.NEATLOGS_API_KEY });
86
+ const client = wrapOpenAI(new OpenAI());
97
87
  ```
98
88
 
99
89
  ### Important: No Top-Level Await
@@ -119,7 +109,6 @@ Initialize the Neatlogs SDK. Returns `Promise<void>`.
119
109
  ```typescript
120
110
  await init({
121
111
  apiKey: process.env.NEATLOGS_API_KEY,
122
- instrumentations: ['openai', 'anthropic'],
123
112
  debug: true,
124
113
  });
125
114
  ```
@@ -129,7 +118,6 @@ await init({
129
118
  | Option | Type | Default | Description |
130
119
  |--------|------|---------|-------------|
131
120
  | `apiKey` | `string` | `process.env.NEATLOGS_API_KEY` | Neatlogs API key. Export disabled if not set. |
132
- | `baseUrl` | `string` | `'https://app.neatlogs.com'` | Base URL for the Neatlogs API. |
133
121
  | `workflowName` | `string` | Derived from `process.argv[1]` | Name of the workflow being traced. |
134
122
  | `sessionId` | `string` | — | Explicit session ID for grouping traces. |
135
123
  | `autoSession` | `boolean` | `false` | Auto-generate a session ID if none provided. |
@@ -138,19 +126,49 @@ await init({
138
126
  | `metadata` | `Record<string, any>` | — | Custom metadata attached to all spans. |
139
127
  | `debug` | `boolean` | `false` | Enable debug logging. |
140
128
  | `disableExport` | `boolean` | `false` | Disable export to Neatlogs backend. |
141
- | `instrumentations` | `string[]` | — | Legacy manager path. Instrumentors that depend on global OTel context are rejected; use explicit wrappers below. |
142
129
  | `tracerProvider` | `BasicTracerProvider` | Private SDK provider | Optional caller-owned private provider. It is never registered globally or shut down by Neatlogs. |
143
130
  | `registerShutdownHandlers` | `boolean` | `true` for SDK-owned provider | Register process exit/signal handlers. Set `false` when the host application owns shutdown. |
144
131
  | `mask` | `MaskFunction` | — | Global mask function applied to all spans. |
145
132
  | `sampleRate` | `number` | `1.0` | Sampling rate (0.0 to 1.0). |
146
133
  | `captureLogs` | `boolean` | `false` | Capture log records via OTel LoggerProvider. |
147
- | `traceContent` | `boolean` | `true` | Capture input/output content on spans. |
148
134
  | `pii` | `'redact' &#124; 'hash' &#124; false` | — | PII detection mode. |
149
135
  | `endpoint` | `string` | `'https://ingest.neatlogs.com'` | Base ingest endpoint. The SDK sends traces to `/v1/traces` and logs to `/v1/logs`. |
150
136
  | `batchSize` | `number` | `100` | Maximum spans per export batch. |
151
137
  | `flushInterval` | `number` | `5` | Seconds between batch flushes. |
152
138
  | `piiEnabled` | `boolean` | — | Override team-level PII redaction toggle. |
153
139
  | `piiSpanTypes` | `string[]` | — | Override which span types have server-side PII redaction. |
140
+ | `uploadAuthority` | `boolean \| UploadAuthority` | `false` | Enable the authenticated typed-media/oversized-OTLP upload contract, or inject an implementation. Keep disabled until the backend contract is deployed. |
141
+
142
+ ---
143
+
144
+ ### Independent `Client` pipelines
145
+
146
+ Use `Client` when one process must send different executions to different
147
+ Neatlogs projects. Each Client owns an isolated provider/export queue; the
148
+ active Client is scoped to its synchronous or asynchronous `activate()` call.
149
+
150
+ ```typescript
151
+ import { Client, trace, wrapOpenAI } from 'neatlogs';
152
+
153
+ const project = new Client({
154
+ apiKey: process.env.NEATLOGS_API_KEY!,
155
+ workflowName: 'support-agent',
156
+ captureLogs: true,
157
+ });
158
+ const openai = wrapOpenAI(rawOpenAI); // reusable; routing occurs at invocation
159
+
160
+ await project.activate(async () => {
161
+ await trace({ name: 'answer', kind: 'WORKFLOW' }, async () => {
162
+ return openai.responses.create({ model: 'gpt-5', input: 'Hello' });
163
+ });
164
+ });
165
+
166
+ await project.shutdown();
167
+ ```
168
+
169
+ Do not share one activation across unrelated concurrent jobs. Create one Client
170
+ per destination, use `activate()` around each execution, and always await
171
+ `shutdown()` when that Client is no longer needed.
154
172
 
155
173
  ---
156
174
 
@@ -345,8 +363,12 @@ Server-side prompt management for storing, versioning, and retrieving prompts fr
345
363
  import { PromptClient } from 'neatlogs';
346
364
 
347
365
  const client = new PromptClient({
348
- baseUrl: 'https://app.neatlogs.com',
366
+ baseUrl: 'https://ingest.neatlogs.com',
349
367
  apiKey: process.env.NEATLOGS_API_KEY!,
368
+ cacheTtlMs: 60_000, // fresh lifetime for latest/label lookups
369
+ staleWhileRevalidateMs: 300_000, // bounded stale fallback during refresh
370
+ requestTimeoutMs: 10_000, // deadline for each prompt API request
371
+ maxCacheEntries: 100, // LRU bound for process memory
350
372
  });
351
373
 
352
374
  // Create a prompt
@@ -359,9 +381,17 @@ const prompt = await client.createPrompt({
359
381
  // Fetch by name (returns latest version)
360
382
  const handle = await client.getPrompt('qa-system');
361
383
 
384
+ // Per-key cache policy is retained across refreshes. During the stale window,
385
+ // getPrompt returns the last known value and starts one coalesced refresh.
386
+ const fastRefresh = await client.getPrompt('qa-system', {
387
+ cacheTtlMs: 5_000,
388
+ staleWhileRevalidateMs: 60_000,
389
+ });
390
+
362
391
  // Fetch by label or version
363
392
  const prod = await client.getPrompt('qa-system', { label: 'production' });
364
393
  const v2 = await client.getPrompt('qa-system', { version: 2 });
394
+ const v3 = await client.getPrompt('qa-system', { version: 3 });
365
395
 
366
396
  // Compile with variables
367
397
  const rendered = handle.compile({ role: 'helpful', company: 'Acme' });
@@ -372,20 +402,62 @@ const messages = handle.compileMessages({ role: 'helpful', company: 'Acme' });
372
402
  // List all prompts
373
403
  const all = await client.listPrompts();
374
404
 
375
- // Update prompt content
405
+ // Backward-compatible alias: managed prompts are immutable, so this creates a version
376
406
  await client.updatePrompt('qa-system', { content: 'Updated: {{role}} for {{company}}.' });
377
407
 
378
- // Save a new version
379
- await client.saveAsVersion('qa-system', { label: 'v2' });
408
+ // Save a new version. Content or messages is required by the backend contract.
409
+ await client.saveAsVersion('qa-system', {
410
+ content: 'Version 2: {{role}} for {{company}}.',
411
+ labels: ['staging'],
412
+ commitMessage: 'Try the revised system prompt',
413
+ });
380
414
 
381
- // Delete a prompt
382
- await client.deletePrompt('qa-system');
415
+ // Mutations target immutable version UUIDs. Name + version/label is resolved first.
416
+ await client.setLabel('qa-system', 'production', { version: 2 });
417
+ await client.addTag('qa-system', 'release-candidate', { version: 2 });
418
+ await client.removeTag('qa-system', 'release-candidate', { version: 2 });
419
+ await client.deletePrompt('qa-system', { version: 1 });
420
+
421
+ // Explicit PromptClient instances own their cache and prompt requests.
422
+ client.close();
383
423
  ```
384
424
 
425
+ Each prompt version may have zero or one active label. Accordingly, `labels`
426
+ accepts at most one value on create/save, and `setLabel()` replaces or moves
427
+ that label rather than adding a second simultaneous label.
428
+
429
+ Latest and label selectors are fresh for `cacheTtlMs`. After that, they may be
430
+ served only for the bounded `staleWhileRevalidateMs` window while one shared
431
+ same-key refresh runs. Refresh failure leaves the last known value available
432
+ until that stale window ends; after it ends, the next lookup waits for the
433
+ backend and reports a typed error. A version selector is immutable in-process:
434
+ `{ version: 2 }` never changes into another version. Request a different
435
+ version explicitly, call `clearCache()`, or create a new client.
436
+
437
+ Every request has a finite `requestTimeoutMs`. `close()` aborts in-flight prompt
438
+ requests and releases the cache; calls after close raise
439
+ `PromptClientClosedError`. The shared prompt client created by `init()` is
440
+ closed by `shutdown()`, without making prompt failures part of telemetry flush
441
+ success. An explicitly constructed `PromptClient` must be closed by its owner.
442
+
443
+ #### Prompt privacy and ownership
444
+
445
+ Prompt CRUD is intentional product-data transfer, separate from trace
446
+ telemetry. The API key selects the Neatlogs project and authenticates prompt
447
+ requests to `baseUrl`; the in-memory cache retains prompt content only until
448
+ eviction, `clearCache()`, or `close()`. Server retention follows the managed
449
+ prompt service policy for that project.
450
+
451
+ Telemetry `mask=`, `pii`, and `piiSpanTypes` settings do **not** transform prompt
452
+ content sent to the prompt-management API. If prompt content must be redacted,
453
+ transform it explicitly before calling prompt CRUD. The SDK does not currently
454
+ provide a prompt transform and does not claim that telemetry masking protects
455
+ managed prompt payloads.
456
+
385
457
  Module-level convenience functions are also available after `init()`:
386
458
 
387
459
  ```typescript
388
- import { init, getPrompt, fetchPrompt, listPrompts, createPrompt, updatePrompt, saveAsVersion, deletePrompt, removeTag } from 'neatlogs';
460
+ import { init, getPrompt, fetchPrompt, listPrompts, createPrompt, updatePrompt, saveAsVersion, deletePrompt, setLabel, addTag, removeTag } from 'neatlogs';
389
461
 
390
462
  await init({ apiKey: process.env.NEATLOGS_API_KEY });
391
463
 
@@ -395,16 +467,28 @@ const rendered = handle.compile({ name: 'world' });
395
467
 
396
468
  ---
397
469
 
398
- ### `flush()` / `shutdown()`
470
+ ### `flush()` / `flushAll()` / `flushAllDetailed()` / `shutdown()`
399
471
 
400
472
  ```typescript
401
473
  // Flush pending spans without shutting down
402
474
  await flush();
403
475
 
476
+ // Flush the default pipeline and every live Neatlogs Client under one deadline
477
+ const flushed = await flushAll(30_000);
478
+ if (!flushed) console.error('One or more Neatlogs pipelines failed to flush');
479
+
480
+ // Inspect per-pipeline timeout and failure details when needed
481
+ const result = await flushAllDetailed(30_000);
482
+ if (!result.success) console.error(result.outcomes);
483
+
404
484
  // Flush and shut down — call before process exit
405
485
  await shutdown();
406
486
  ```
407
487
 
488
+ `flushAll()` and `flushAllDetailed()` only know about Neatlogs-owned pipelines.
489
+ They do not discover or flush Datadog, Langfuse, Braintrust, or a global
490
+ OpenTelemetry provider.
491
+
408
492
  `shutdown()` resets all SDK state so `init()` can be called again if needed.
409
493
 
410
494
  ---
@@ -435,30 +519,36 @@ import { registerCrewaiTask } from 'neatlogs';
435
519
  registerCrewaiTask('research-task', 'Research the latest AI developments');
436
520
  ```
437
521
 
438
- ## Supported Instrumentations
439
-
440
- ### Isolation policy
441
-
442
- Neatlogs always runs on a private provider and private async context. Third-party
443
- auto-instrumentors that call the global OpenTelemetry context API cannot provide
444
- bidirectional isolation, so the manager rejects them at initialization before
445
- creating any provider state. Use the explicit provider/framework wrappers
446
- instead.
447
-
448
- ### Registry Entries (not yet instrumented in TypeScript)
449
-
450
- The following libraries are registered in the instrumentation registry for future support. Passing them to `instrumentations` will log a debug message and skip gracefully:
451
-
452
- `cohere`, `groq`, `together`, `vertexai`, `google_generativeai`, `mistralai`, `ollama`, `watsonx`, `alephalpha`, `replicate`, `sagemaker`, `huggingface_hub`, `litellm`, `langgraph`, `llamaindex`, `autogen`, `haystack`, `dspy`, `chromadb`, `pinecone`, `weaviate`, `qdrant`, `milvus`, `opensearch`, `elasticsearch`, `redis`, `marqo`, `instructor`, `guardrails`, `google_adk`, `agno`, `openai_agents`, `pydantic_ai`, `smolagents`, `strands`, `pipecat`, `portkey`, `promptflow`
453
-
454
- ## Framework Integrations
455
-
456
- For frameworks that don't fit the auto-instrument-on-init pattern, use the SDK's explicit wrappers. These wrappers use Neatlogs' private context and remain isolated from other tracing SDKs:
457
-
458
- | Framework | Helper |
459
- |-----------|--------|
460
- | Mastra (`@mastra/core`) | `wrapMastra()` from `neatlogs/mastra` |
461
- | Vercel AI SDK (`ai`) | `wrapAISDK()` from `neatlogs/ai` |
522
+ ## Supported TypeScript Integrations
523
+
524
+ For `neatlogs >=1.1.19 <2.0.0`, use only the explicit helper shown below. The
525
+ SDK has no `instrumentations: [...]` loader. These helpers attach to the object,
526
+ callback surface, processor, or plugin you pass and use Neatlogs' private
527
+ context. Versioned rows below name the dependencies installed by this repository's test
528
+ matrix; API-shaped rows deliberately make no blanket semver claim.
529
+
530
+ | Library | Repository test/API baseline | Explicit helper | Import path |
531
+ |---|---|---|---|
532
+ | OpenAI | `openai` 6.34.x | `wrapOpenAI(client)` | `neatlogs` or `neatlogs/openai` |
533
+ | Anthropic | `@anthropic-ai/sdk` 0.68.x | `wrapAnthropic(client)` | `neatlogs` or `neatlogs/anthropic` |
534
+ | Azure OpenAI | `openai` 6.34.x | `wrapAzureOpenAI(client)` | `neatlogs/azure-openai` |
535
+ | AWS Bedrock Runtime | AWS SDK v3 command API | `wrapBedrock(client)` | `neatlogs/bedrock` |
536
+ | Google GenAI | `@google/genai` 1.34.x | `wrapGoogleGenAI(client)` / `wrapGoogleGenAIChat(chat)` | `neatlogs/google-genai` |
537
+ | Vertex AI through `@google/genai` | `@google/genai` 1.34.x | `wrapVertexAI(client)` / `wrapVertexAIChat(chat)` | `neatlogs/vertex-ai` |
538
+ | OpenRouter Agent | `@openrouter/agent` 0.7.x | `wrapOpenRouterAgent(client)` / `wrapCallModel(fn)` | `neatlogs/openrouter-agent` |
539
+ | Vercel AI SDK | `ai` 6.x | `wrapAISDK(ai)` | `neatlogs/ai` |
540
+ | Mastra | `@mastra/core` 1.32.x | `wrapMastra(entity)` / `wrapMastraRerank(fn)` | `neatlogs/mastra` |
541
+ | Claude Agent SDK | documented `query()` API | `wrapClaudeAgentSDK(sdk)` | `neatlogs/claude-agent-sdk` |
542
+ | LangChain / LangGraph | `@langchain/core` 0.3.x | `langchainHandler()` callback | `neatlogs` or `neatlogs/langchain` |
543
+ | OpenAI Agents SDK | documented `addTraceProcessor()` API | `openaiAgentsProcessor()` | `neatlogs` or `neatlogs/openai-agents` |
544
+ | Pi Agent | `agent-core` 0.73.x and 0.83.x | `piAgentHooks(agent)` / `tracePiAgentEvents(...)` / `tracePiStream(...)` | `neatlogs` or `neatlogs/pi-agent` |
545
+ | OpenCode | current plugin API | `NeatlogsOpencodePlugin` | `neatlogs/opencode` |
546
+ | Browser client | browser SDK API in this release | `Neatlogs` | `neatlogs/browser` |
547
+
548
+ Edge runtime packaging, the removed `instrumentations` init option, and Strands
549
+ global-context hooks are not supported. `strandsHooks()` remains an explicit
550
+ runtime rejection so an application cannot silently believe it is isolated or
551
+ instrumented.
462
552
 
463
553
  ```typescript
464
554
  // Vercel AI SDK
@@ -486,6 +576,7 @@ await shutdown();
486
576
  |----------|-------------|
487
577
  | `NEATLOGS_API_KEY` | API key (fallback when `apiKey` option is not provided) |
488
578
  | `NEATLOGS_DISABLE_EXPORT` | Set to `true`, `1`, or `yes` to disable export |
579
+ | `NEATLOGS_UPLOADS_ENABLED` | Set to `true`, `1`, or `yes` to enable authenticated typed-media and oversized-OTLP uploads |
489
580
 
490
581
  ### Programmatic Configuration
491
582
 
@@ -499,7 +590,6 @@ await init({
499
590
  userId: 'user-456',
500
591
  tags: ['production', 'v2'],
501
592
  metadata: { environment: 'prod' },
502
- instrumentations: ['openai', 'anthropic'],
503
593
  sampleRate: 0.5,
504
594
  captureLogs: true,
505
595
  debug: true,
@@ -575,7 +665,7 @@ See the [`examples/`](./examples/) directory for complete, runnable examples:
575
665
 
576
666
  | File | Description |
577
667
  |------|-------------|
578
- | [`basic-openai.ts`](./examples/basic-openai.ts) | Basic OpenAI usage with auto-instrumentation |
668
+ | [`basic-openai.ts`](./examples/basic-openai.ts) | Basic OpenAI usage with an explicit wrapper |
579
669
  | [`prompt-management.ts`](./examples/prompt-management.ts) | PromptTemplate + trace() for prompt versioning |
580
670
  | [`multi-agent-workflow.ts`](./examples/multi-agent-workflow.ts) | Nested spans: WORKFLOW → AGENT → TOOL |
581
671
  | [`custom-spans.ts`](./examples/custom-spans.ts) | All span kinds: WORKFLOW, CHAIN, AGENT, TOOL, RETRIEVER, EMBEDDING, GUARDRAIL |
package/dist/ai-sdk.cjs CHANGED
@@ -27,8 +27,21 @@ module.exports = __toCommonJS(ai_sdk_exports);
27
27
  var import_api2 = require("@opentelemetry/api");
28
28
 
29
29
  // src/core/provider.ts
30
- var import_node_async_hooks = require("async_hooks");
30
+ var import_node_async_hooks2 = require("async_hooks");
31
31
  var import_api = require("@opentelemetry/api");
32
+
33
+ // src/core/active-client.ts
34
+ var import_node_async_hooks = require("async_hooks");
35
+ var ACTIVE_CLIENT_STORAGE_KEY = /* @__PURE__ */ Symbol.for(
36
+ "neatlogs.active_client_async_local_storage"
37
+ );
38
+ var neatlogsGlobal = globalThis;
39
+ var storage = neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] ?? (neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] = new import_node_async_hooks.AsyncLocalStorage());
40
+ function getActiveClient() {
41
+ return storage.getStore();
42
+ }
43
+
44
+ // src/core/provider.ts
32
45
  var NEATLOGS_ROOT_SPAN_KEY = (0, import_api.createContextKey)("neatlogs.root_span");
33
46
  var PRIVATE_SPAN_STORAGE_KEY = /* @__PURE__ */ Symbol.for(
34
47
  "neatlogs.private_span_async_local_storage"
@@ -36,9 +49,9 @@ var PRIVATE_SPAN_STORAGE_KEY = /* @__PURE__ */ Symbol.for(
36
49
  var PRIVATE_PROVIDER_STATE_KEY = /* @__PURE__ */ Symbol.for(
37
50
  "neatlogs.private_provider_state"
38
51
  );
39
- var neatlogsGlobal = globalThis;
40
- var privateContextStorage = neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] ?? (neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] = new import_node_async_hooks.AsyncLocalStorage());
41
- var providerState = neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] ?? (neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] = {
52
+ var neatlogsGlobal2 = globalThis;
53
+ var privateContextStorage = neatlogsGlobal2[PRIVATE_SPAN_STORAGE_KEY] ?? (neatlogsGlobal2[PRIVATE_SPAN_STORAGE_KEY] = new import_node_async_hooks2.AsyncLocalStorage());
54
+ var providerState = neatlogsGlobal2[PRIVATE_PROVIDER_STATE_KEY] ?? (neatlogsGlobal2[PRIVATE_PROVIDER_STATE_KEY] = {
42
55
  provider: null
43
56
  });
44
57
  var preInitTracer = {
@@ -51,8 +64,24 @@ var preInitTracer = {
51
64
  }
52
65
  };
53
66
  function getNeatlogsTracer(name) {
67
+ const client = getActiveClient();
68
+ if (client) return client.getTracer(name);
54
69
  return providerState.provider?.getTracer(name) ?? preInitTracer;
55
70
  }
71
+ function getRoutingNeatlogsTracer(name) {
72
+ return {
73
+ startSpan(spanName, options, context) {
74
+ return isolateTracer(getNeatlogsTracer(name)).startSpan(
75
+ spanName,
76
+ options,
77
+ context
78
+ );
79
+ },
80
+ startActiveSpan: ((...args) => isolateTracer(getNeatlogsTracer(name)).startActiveSpan(
81
+ ...args
82
+ ))
83
+ };
84
+ }
56
85
  function isolateTracer(tracer) {
57
86
  const facade = {
58
87
  startSpan(name, options, context) {
@@ -107,7 +136,7 @@ function createAITelemetry(opts = {}) {
107
136
  // internally, which would otherwise parent its native spans from the foreign
108
137
  // global context AND push them onto it (so a co-tenant's next span inherits
109
138
  // ours). The facade routes both through the private Neatlogs context.
110
- tracer: isolateTracer(getNeatlogsTracer(TRACER_NAME)),
139
+ tracer: getRoutingNeatlogsTracer(TRACER_NAME),
111
140
  metadata: { ...userMeta, neatlogsWrapped: true }
112
141
  };
113
142
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ai-sdk.ts","../src/core/provider.ts"],"sourcesContent":["/**\n * Vercel AI SDK wrapper — inline implementation.\n *\n * Wraps `generateText`, `streamText`, `generateObject`, `streamObject` from\n * the `ai` package with OTel parent spans + forced telemetry. Static export,\n * no dynamic imports — bundler-friendly (works with Turbopack, webpack, esbuild).\n *\n * Usage:\n * import { wrapAISDK } from 'neatlogs';\n * import * as ai from 'ai';\n * const { streamText, generateText } = wrapAISDK(ai);\n */\n\nimport { SpanStatusCode, type Span, type Tracer } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsParentContext, isolateTracer, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.ai-sdk';\n\n// -- Telemetry config --------------------------------------------------------\n\nexport interface CreateAITelemetryOptions {\n metadata?: Record<string, unknown>;\n}\n\nexport interface AITelemetryConfig {\n isEnabled: true;\n recordInputs: true;\n recordOutputs: true;\n tracer: Tracer;\n metadata: Record<string, unknown>;\n}\n\nexport function createAITelemetry(\n opts: CreateAITelemetryOptions = {},\n): AITelemetryConfig {\n const userMeta = opts.metadata ?? {};\n return {\n isEnabled: true,\n recordInputs: true,\n recordOutputs: true,\n // Hand the AI SDK an isolation-aware tracer: it calls startActiveSpan()\n // internally, which would otherwise parent its native spans from the foreign\n // global context AND push them onto it (so a co-tenant's next span inherits\n // ours). The facade routes both through the private Neatlogs context.\n tracer: isolateTracer(getNeatlogsTracer(TRACER_NAME)),\n metadata: { ...userMeta, neatlogsWrapped: true },\n };\n}\n\n// -- Span attributes ---------------------------------------------------------\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction setInputValue(span: Span, opts: Record<string, unknown>): void {\n // For generateText/streamText — only capture prompt or messages, not full model config\n if (opts && ('prompt' in opts || 'messages' in opts)) {\n const input = opts.messages ?? opts.prompt;\n const stringified = safeStringify(input);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n return;\n }\n const stringified = safeStringify(opts);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n}\n\nfunction setOutputValue(span: Span, result: unknown): void {\n if (result && typeof result === 'object') {\n const r = result as Record<string, unknown>;\n // GenerateTextResult / StreamTextResult — extract meaningful fields\n if ('text' in r && 'finishReason' in r) {\n const text = String(r.text ?? '');\n if (text) {\n span.setAttribute('output.value', text);\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n // GenerateObjectResult — the structured object is the output, not `text`.\n // Without this the envelope (object+usage+response+…) gets stringified whole.\n if ('object' in r && 'finishReason' in r) {\n if (r.object !== undefined) {\n span.setAttribute('output.value', safeStringify(r.object));\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n }\n const stringified = safeStringify(result);\n if (stringified) {\n span.setAttribute('output.value', stringified);\n }\n}\n\n// Extract output from a streamText/streamObject `onFinish` event. The event\n// extends StepResult, so `text` (streamText) / `object` (streamObject) sit at\n// the top level alongside `finishReason` — but the event also carries `steps`,\n// `usage`, etc., so we pull only the meaningful fields instead of stringifying\n// the whole envelope.\nfunction setStreamOutputValue(span: Span, event: unknown): void {\n if (!event || typeof event !== 'object') return;\n const e = event as Record<string, unknown>;\n if (typeof e.text === 'string' && e.text) {\n span.setAttribute('output.value', e.text);\n } else if ('object' in e && e.object !== undefined) {\n const stringified = safeStringify(e.object);\n if (stringified) span.setAttribute('output.value', stringified);\n }\n if (e.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(e.finishReason));\n }\n}\n\n// -- Wrapping ----------------------------------------------------------------\n\ntype WrappedFunctionName = 'generateText' | 'streamText' | 'generateObject' | 'streamObject' | 'embed' | 'embedMany' | 'rerank';\n\nconst WRAPPED_FUNCTIONS: readonly WrappedFunctionName[] = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n 'rerank',\n] as const;\n\n/**\n * Wrap the `ai` module namespace so that every `generateText` / `streamText` /\n * `generateObject` / `streamObject` call:\n *\n * 1. Opens a parent OTel span on the active TracerProvider.\n * 2. Forces `experimental_telemetry: { isEnabled: true }`, merging user metadata.\n * 3. Records input/output on the parent span and propagates errors.\n *\n * Other exports (types, helpers) pass through unchanged.\n */\nexport function wrapAISDK<T extends Record<string, unknown>>(aiModule: T): T {\n const wrapped: Record<string, unknown> = { ...aiModule };\n\n for (const name of WRAPPED_FUNCTIONS) {\n const original = aiModule[name];\n if (typeof original !== 'function') continue;\n\n if (name === 'streamText' || name === 'streamObject') {\n wrapped[name] = createStreamWrapper(name, original as (opts: any) => unknown);\n } else {\n wrapped[name] = createAsyncWrapper(name, original as (opts: any) => Promise<unknown>);\n }\n }\n\n return wrapped as T;\n}\n\nfunction rootSpanKind(name: WrappedFunctionName): string {\n if (name === 'embed' || name === 'embedMany' || name === 'rerank') return 'CHAIN';\n return 'WORKFLOW';\n}\n\n\nfunction getParentContext() {\n // Our parent comes solely from the private span store; a\n // foreign provider's active span must never become our ancestor.\n return getNeatlogsParentContext();\n}\n\nfunction createAsyncWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => Promise<unknown>,\n): (opts: any) => Promise<unknown> {\n return async function wrappedAsyncFn(opts: any): Promise<unknown> {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan (NOT startActiveSpan) + withNeatlogsSpan: startActiveSpan would\n // push our span onto the GLOBAL OTel context, so a foreign tracer's\n // startSpan() inside generateText() would read it as parent and inherit our\n // trace id. withNeatlogsSpan carries the parent in the private store in\n // the private context, leaving the global context untouched.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n async () => {\n try {\n const isEmbedOrRerank = name === 'embed' || name === 'embedMany' || name === 'rerank';\n if (!isEmbedOrRerank) {\n setInputValue(span, opts);\n }\n if (name === 'rerank' && opts?.query) {\n span.setAttribute('ai.rerank.query', String(opts.query));\n }\n const merged = mergeTelemetry(opts);\n const result = await original(merged);\n if (!isEmbedOrRerank) {\n setOutputValue(span, result);\n }\n return result;\n } catch (err) {\n recordSpanError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n parentContext,\n );\n };\n}\n\n// streamText/streamObject return synchronously while the model keeps producing\n// tokens for seconds afterwards. Ending the span in a `finally` (as a plain sync\n// wrapper would) closes it in ~2ms with no output — the output only exists once\n// the stream finishes. Instead we keep the span open and end it from the AI SDK's\n// `onFinish` callback, where the final text/object is available. Any user-provided\n// `onFinish` is preserved and invoked first.\nfunction createStreamWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => unknown,\n): (opts: any) => unknown {\n return function wrappedStreamFn(opts: any): unknown {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan + withNeatlogsSpan (see createAsyncWrapper) so streamText's\n // internals never see our span on the global OTel context. The span stays\n // open past the run scope and is ended from onFinish/onError.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n () => {\n let spanEnded = false;\n const endOnce = () => {\n if (spanEnded) return;\n spanEnded = true;\n span.end();\n };\n try {\n setInputValue(span, opts);\n const merged = mergeTelemetry(opts);\n const userOnFinish = opts?.onFinish;\n const userOnError = opts?.onError;\n const wrappedOpts = {\n ...merged,\n onFinish: async (event: any) => {\n try {\n setStreamOutputValue(span, event);\n } finally {\n endOnce();\n }\n if (typeof userOnFinish === 'function') {\n return userOnFinish(event);\n }\n },\n onError: (event: any) => {\n recordSpanError(span, (event && event.error) ?? event);\n endOnce();\n if (typeof userOnError === 'function') {\n return userOnError(event);\n }\n },\n };\n return original(wrappedOpts);\n } catch (err) {\n // Synchronous throw (e.g. bad arguments) — the stream never started.\n recordSpanError(span, err);\n endOnce();\n throw err;\n }\n },\n parentContext,\n );\n };\n}\n\nfunction mergeTelemetry(opts: any): any {\n const baseTelemetry: AITelemetryConfig = createAITelemetry({\n metadata: opts?.experimental_telemetry?.metadata,\n });\n return {\n ...opts,\n experimental_telemetry: {\n ...opts?.experimental_telemetry,\n ...baseTelemetry,\n },\n };\n}\n\n\nfunction recordSpanError(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}\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\n/**\n * @internal Run with a private Neatlogs parent without treating that parent as\n * a locally-recording trace root. This is used for an extracted remote parent:\n * the first local recording span should remain the target for local trace-level\n * output, while still inheriting the remote trace/span IDs.\n */\nexport function withNeatlogsRemoteParent<T>(span: Span, fn: () => T): T {\n return privateContextStorage.run(otelTrace.setSpan(ROOT_CONTEXT, span), fn);\n}\n"],"mappings":"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","names":["import_api","otelTrace","otelTrace","stringified"]}
1
+ {"version":3,"sources":["../src/ai-sdk.ts","../src/core/provider.ts","../src/core/active-client.ts"],"sourcesContent":["/**\n * Vercel AI SDK wrapper — inline implementation.\n *\n * Wraps `generateText`, `streamText`, `generateObject`, `streamObject` from\n * the `ai` package with OTel parent spans + forced telemetry. Static export,\n * no dynamic imports — bundler-friendly (works with Turbopack, webpack, esbuild).\n *\n * Usage:\n * import { wrapAISDK } from 'neatlogs';\n * import * as ai from 'ai';\n * const { streamText, generateText } = wrapAISDK(ai);\n */\n\nimport { SpanStatusCode, type Span, type Tracer } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsParentContext, getRoutingNeatlogsTracer, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.ai-sdk';\n\n// -- Telemetry config --------------------------------------------------------\n\nexport interface CreateAITelemetryOptions {\n metadata?: Record<string, unknown>;\n}\n\nexport interface AITelemetryConfig {\n isEnabled: true;\n recordInputs: true;\n recordOutputs: true;\n tracer: Tracer;\n metadata: Record<string, unknown>;\n}\n\nexport function createAITelemetry(\n opts: CreateAITelemetryOptions = {},\n): AITelemetryConfig {\n const userMeta = opts.metadata ?? {};\n return {\n isEnabled: true,\n recordInputs: true,\n recordOutputs: true,\n // Hand the AI SDK an isolation-aware tracer: it calls startActiveSpan()\n // internally, which would otherwise parent its native spans from the foreign\n // global context AND push them onto it (so a co-tenant's next span inherits\n // ours). The facade routes both through the private Neatlogs context.\n tracer: getRoutingNeatlogsTracer(TRACER_NAME),\n metadata: { ...userMeta, neatlogsWrapped: true },\n };\n}\n\n// -- Span attributes ---------------------------------------------------------\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction setInputValue(span: Span, opts: Record<string, unknown>): void {\n // For generateText/streamText — only capture prompt or messages, not full model config\n if (opts && ('prompt' in opts || 'messages' in opts)) {\n const input = opts.messages ?? opts.prompt;\n const stringified = safeStringify(input);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n return;\n }\n const stringified = safeStringify(opts);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n}\n\nfunction setOutputValue(span: Span, result: unknown): void {\n if (result && typeof result === 'object') {\n const r = result as Record<string, unknown>;\n // GenerateTextResult / StreamTextResult — extract meaningful fields\n if ('text' in r && 'finishReason' in r) {\n const text = String(r.text ?? '');\n if (text) {\n span.setAttribute('output.value', text);\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n // GenerateObjectResult — the structured object is the output, not `text`.\n // Without this the envelope (object+usage+response+…) gets stringified whole.\n if ('object' in r && 'finishReason' in r) {\n if (r.object !== undefined) {\n span.setAttribute('output.value', safeStringify(r.object));\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n }\n const stringified = safeStringify(result);\n if (stringified) {\n span.setAttribute('output.value', stringified);\n }\n}\n\n// Extract output from a streamText/streamObject `onFinish` event. The event\n// extends StepResult, so `text` (streamText) / `object` (streamObject) sit at\n// the top level alongside `finishReason` — but the event also carries `steps`,\n// `usage`, etc., so we pull only the meaningful fields instead of stringifying\n// the whole envelope.\nfunction setStreamOutputValue(span: Span, event: unknown): void {\n if (!event || typeof event !== 'object') return;\n const e = event as Record<string, unknown>;\n if (typeof e.text === 'string' && e.text) {\n span.setAttribute('output.value', e.text);\n } else if ('object' in e && e.object !== undefined) {\n const stringified = safeStringify(e.object);\n if (stringified) span.setAttribute('output.value', stringified);\n }\n if (e.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(e.finishReason));\n }\n}\n\n// -- Wrapping ----------------------------------------------------------------\n\ntype WrappedFunctionName = 'generateText' | 'streamText' | 'generateObject' | 'streamObject' | 'embed' | 'embedMany' | 'rerank';\n\nconst WRAPPED_FUNCTIONS: readonly WrappedFunctionName[] = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n 'rerank',\n] as const;\n\n/**\n * Wrap the `ai` module namespace so that every `generateText` / `streamText` /\n * `generateObject` / `streamObject` call:\n *\n * 1. Opens a parent OTel span on the active TracerProvider.\n * 2. Forces `experimental_telemetry: { isEnabled: true }`, merging user metadata.\n * 3. Records input/output on the parent span and propagates errors.\n *\n * Other exports (types, helpers) pass through unchanged.\n */\nexport function wrapAISDK<T extends Record<string, unknown>>(aiModule: T): T {\n const wrapped: Record<string, unknown> = { ...aiModule };\n\n for (const name of WRAPPED_FUNCTIONS) {\n const original = aiModule[name];\n if (typeof original !== 'function') continue;\n\n if (name === 'streamText' || name === 'streamObject') {\n wrapped[name] = createStreamWrapper(name, original as (opts: any) => unknown);\n } else {\n wrapped[name] = createAsyncWrapper(name, original as (opts: any) => Promise<unknown>);\n }\n }\n\n return wrapped as T;\n}\n\nfunction rootSpanKind(name: WrappedFunctionName): string {\n if (name === 'embed' || name === 'embedMany' || name === 'rerank') return 'CHAIN';\n return 'WORKFLOW';\n}\n\n\nfunction getParentContext() {\n // Our parent comes solely from the private span store; a\n // foreign provider's active span must never become our ancestor.\n return getNeatlogsParentContext();\n}\n\nfunction createAsyncWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => Promise<unknown>,\n): (opts: any) => Promise<unknown> {\n return async function wrappedAsyncFn(opts: any): Promise<unknown> {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan (NOT startActiveSpan) + withNeatlogsSpan: startActiveSpan would\n // push our span onto the GLOBAL OTel context, so a foreign tracer's\n // startSpan() inside generateText() would read it as parent and inherit our\n // trace id. withNeatlogsSpan carries the parent in the private store in\n // the private context, leaving the global context untouched.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n async () => {\n try {\n const isEmbedOrRerank = name === 'embed' || name === 'embedMany' || name === 'rerank';\n if (!isEmbedOrRerank) {\n setInputValue(span, opts);\n }\n if (name === 'rerank' && opts?.query) {\n span.setAttribute('ai.rerank.query', String(opts.query));\n }\n const merged = mergeTelemetry(opts);\n const result = await original(merged);\n if (!isEmbedOrRerank) {\n setOutputValue(span, result);\n }\n return result;\n } catch (err) {\n recordSpanError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n parentContext,\n );\n };\n}\n\n// streamText/streamObject return synchronously while the model keeps producing\n// tokens for seconds afterwards. Ending the span in a `finally` (as a plain sync\n// wrapper would) closes it in ~2ms with no output — the output only exists once\n// the stream finishes. Instead we keep the span open and end it from the AI SDK's\n// `onFinish` callback, where the final text/object is available. Any user-provided\n// `onFinish` is preserved and invoked first.\nfunction createStreamWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => unknown,\n): (opts: any) => unknown {\n return function wrappedStreamFn(opts: any): unknown {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan + withNeatlogsSpan (see createAsyncWrapper) so streamText's\n // internals never see our span on the global OTel context. The span stays\n // open past the run scope and is ended from onFinish/onError.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n () => {\n let spanEnded = false;\n const endOnce = () => {\n if (spanEnded) return;\n spanEnded = true;\n span.end();\n };\n try {\n setInputValue(span, opts);\n const merged = mergeTelemetry(opts);\n const userOnFinish = opts?.onFinish;\n const userOnError = opts?.onError;\n const wrappedOpts = {\n ...merged,\n onFinish: async (event: any) => {\n try {\n setStreamOutputValue(span, event);\n } finally {\n endOnce();\n }\n if (typeof userOnFinish === 'function') {\n return userOnFinish(event);\n }\n },\n onError: (event: any) => {\n recordSpanError(span, (event && event.error) ?? event);\n endOnce();\n if (typeof userOnError === 'function') {\n return userOnError(event);\n }\n },\n };\n return original(wrappedOpts);\n } catch (err) {\n // Synchronous throw (e.g. bad arguments) — the stream never started.\n recordSpanError(span, err);\n endOnce();\n throw err;\n }\n },\n parentContext,\n );\n };\n}\n\nfunction mergeTelemetry(opts: any): any {\n const baseTelemetry: AITelemetryConfig = createAITelemetry({\n metadata: opts?.experimental_telemetry?.metadata,\n });\n return {\n ...opts,\n experimental_telemetry: {\n ...opts?.experimental_telemetry,\n ...baseTelemetry,\n },\n };\n}\n\n\nfunction recordSpanError(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}\n","/**\n * Neatlogs-owned tracing state.\n *\n * Spans are created by the private Neatlogs provider and their parent is carried\n * in a private context key. The process-global OpenTelemetry span is\n * deliberately left untouched so other observability SDKs cannot export or\n * become parents of Neatlogs spans.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n ROOT_CONTEXT,\n INVALID_SPAN_CONTEXT,\n createContextKey,\n trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\nimport { getActiveClient } from './active-client.js';\n\n// Carries the trace-ROOT span down the private context so descendants can target\n// it (e.g. setTraceOutput). getActiveNeatlogsSpan() returns the innermost span,\n// which is not the root once nested; this key preserves the root reference.\nconst NEATLOGS_ROOT_SPAN_KEY = createContextKey('neatlogs.root_span');\n\n// Entry points are bundled independently (`neatlogs`, `neatlogs/openai`,\n// `neatlogs/ai`, `neatlogs/mastra`, … each in both CJS and ESM), so every piece\n// of shared tracing state — the private span store AND the resolved provider /\n// provider — must live on `globalThis` behind a `Symbol.for` key.\n// Otherwise `init()` (run from the `neatlogs` bundle) sets `_provider` in ITS\n// module copy while a wrapper imported from `neatlogs/openai` reads a different,\n// still-null copy and silently falls back to the foreign global provider.\nconst PRIVATE_SPAN_STORAGE_KEY = Symbol.for(\n 'neatlogs.private_span_async_local_storage',\n);\nconst PRIVATE_PROVIDER_STATE_KEY = Symbol.for(\n 'neatlogs.private_provider_state',\n);\ninterface PrivateProviderState {\n provider: TracerProvider | null;\n}\ntype NeatlogsGlobal = typeof globalThis & {\n [PRIVATE_SPAN_STORAGE_KEY]?: AsyncLocalStorage<Context>;\n [PRIVATE_PROVIDER_STATE_KEY]?: PrivateProviderState;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\n// Stores the full Neatlogs Context (parent span PLUS any threaded values such as\n// trace()'s prompt-template keys), not just the span. We never\n// activate the OTel global context, so this private store is the ONLY channel\n// through which those values propagate down to descendant spans.\nconst privateContextStorage =\n neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] ??\n (neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] = new AsyncLocalStorage<Context>());\nconst providerState: PrivateProviderState =\n neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] ??\n (neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] = {\n provider: null,\n });\n// Wrappers may be constructed or accidentally invoked before init(). They must\n// never fall back to a foreign process-global provider, so pre-init calls use a\n// local no-op tracer and safely emit no exported spans.\nconst preInitTracer: Tracer = {\n startSpan(): Span {\n return otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n _name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n const fn =\n typeof arg2 === 'function'\n ? arg2\n : typeof arg3 === 'function'\n ? arg3\n : arg4;\n return fn!(otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT)) as ReturnType<F>;\n },\n};\n\n/** @internal Configure the provider used by Neatlogs-created spans. */\nexport function _setNeatlogsProvider(provider: TracerProvider | null): void {\n providerState.provider = provider;\n}\n\n/** Resolve a tracer from the private provider when one is configured. */\nexport function getNeatlogsTracer(name: string): Tracer {\n const client = getActiveClient();\n if (client) return client.getTracer(name);\n return providerState.provider?.getTracer(name) ?? preInitTracer;\n}\n\n/** A reusable tracer facade that resolves the active Client on every call. */\nexport function getRoutingNeatlogsTracer(name: string): Tracer {\n return {\n startSpan(spanName: string, options?: SpanOptions, context?: Context): Span {\n return isolateTracer(getNeatlogsTracer(name)).startSpan(\n spanName,\n options,\n context,\n );\n },\n startActiveSpan: ((...args: any[]) =>\n (isolateTracer(getNeatlogsTracer(name)).startActiveSpan as (...inner: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'],\n };\n}\n\n/**\n * @internal The private Neatlogs provider, or null before\n * init(). Used by integrations that must repoint a self-instrumenting library's\n * captured provider onto ours.\n */\nexport function getNeatlogsProvider(): TracerProvider | null {\n const client = getActiveClient();\n if (client) return client.tracerProvider;\n return providerState.provider;\n}\n\n/** Run a Client callback without inheriting another pipeline's private span. */\nexport function runWithFreshNeatlogsContext<T>(fn: () => T): T {\n return privateContextStorage.run(ROOT_CONTEXT, fn);\n}\n\n/**\n * Wrap a tracer so that spans it creates parent from — and, for\n * `startActiveSpan`, activate on — the PRIVATE Neatlogs context instead of the\n * global one.\n *\n * We hand this to libraries that create their own spans off a tracer we give\n * them (the Vercel AI SDK's `experimental_telemetry.tracer`, which calls\n * `tracer.startActiveSpan()` internally). Without the facade the AI SDK's native\n * spans would parent from `context.active()` — the foreign co-tenant's context —\n * and `startActiveSpan` would push them onto the GLOBAL context, so a foreign\n * tracer's next span reads our native span as its parent. Both directions leak.\n *\n */\nexport function isolateTracer(tracer: Tracer): Tracer {\n const facade: Tracer = {\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const parent = context ?? getNeatlogsActiveContext();\n return tracer.startSpan(name, options, parent);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n // Normalize the 2/3/4-arg overloads of startActiveSpan.\n let options: SpanOptions | undefined;\n let context: Context | undefined;\n let fn: F;\n if (typeof arg2 === 'function') {\n fn = arg2 as F;\n } else if (typeof arg3 === 'function') {\n options = arg2 as SpanOptions;\n fn = arg3 as F;\n } else {\n options = arg2 as SpanOptions;\n context = arg3 as Context;\n fn = arg4 as F;\n }\n const parent = context ?? getNeatlogsActiveContext();\n const span = tracer.startSpan(name, options, parent);\n return withNeatlogsSpan(span, () => fn(span), parent) as ReturnType<F>;\n },\n };\n return facade;\n}\n\n/**\n * The base context to build new Neatlogs spans and values on. NEVER contains a\n * foreign provider's span: it reads our private store, or ROOT_CONTEXT when\n * nothing is active.\n */\nexport function getNeatlogsActiveContext(): Context {\n return privateContextStorage.getStore() ?? ROOT_CONTEXT;\n}\n\n/** Return only the active Neatlogs span (never a foreign provider's span). */\nexport function getActiveNeatlogsSpan(): Span | undefined {\n return otelTrace.getSpan(getNeatlogsActiveContext());\n}\n\n/**\n * Return the trace-ROOT Neatlogs span, or undefined when no trace is active.\n *\n * Unlike {@link getActiveNeatlogsSpan} (innermost), this is the outermost span\n * of the current trace — the one the backend derives trace-level output from\n * (`parent_span_id=''`). It is stashed on the private context the first time a\n * span becomes active, so nested calls still resolve to the root.\n */\nexport function getNeatlogsRootSpan(): Span | undefined {\n return getNeatlogsActiveContext().getValue(NEATLOGS_ROOT_SPAN_KEY) as\n | Span\n | undefined;\n}\n\n/**\n * Build a parent context that cannot contain a foreign provider's span.\n *\n * The active Neatlogs context already carries the parent span AND any values\n * the caller threaded in upstream (e.g. `trace()`'s prompt-template values), so\n * those values reach the span processor via `onStart(parentContext)`. An\n * explicit `baseContext` (a caller's own value-carrying context) is honored as-is.\n */\nexport function getNeatlogsParentContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * A base context for callers that thread parent linkage themselves\n * (callback/event handlers that keep their own run-id → span map: LangChain,\n * OpenAI-Agents, Claude Agent SDK).\n *\n * This is the ACTIVE Neatlogs context — the private store's\n * context if a Neatlogs `trace()`/`span()` encloses this call, else\n * ROOT_CONTEXT. So a handler's own root/entry span nests under an enclosing\n * Neatlogs trace (preserving its session + end-user id) when one exists, and\n * auto-roots cleanly when one doesn't. A foreign provider's active span can\n * never leak in as an ancestor because the global context is never read.\n */\nexport function getNeatlogsBaseContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * Build an execution context for a Neatlogs span while preserving the active\n * foreign context. The span rides our private context store.\n */\nexport function getNeatlogsExecutionContext(\n span: Span,\n baseContext: Context = ROOT_CONTEXT,\n): Context {\n return otelTrace.setSpan(baseContext, span);\n}\n\n/**\n * Run a callback with a Neatlogs span active under the appropriate policy.\n *\n * The stored context is `setSpan(base, span)` — carrying the span PLUS whatever\n * values `base` holds — so threaded values (prompt templates)\n * propagate to descendant spans through our private store instead of the global\n * OTel context we deliberately never touch.\n */\nexport function withNeatlogsSpan<T>(\n span: Span,\n fn: () => T,\n baseContext?: Context,\n): T {\n const base = baseContext ?? getNeatlogsActiveContext();\n let ctx = otelTrace.setSpan(base, span);\n // The first span activated in a context with no root recorded IS the root of\n // this trace; remember it so descendants (setTraceOutput) can target it.\n if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === undefined) {\n ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);\n }\n return privateContextStorage.run(ctx, fn);\n}\n\n/**\n * @internal Run with a private Neatlogs parent without treating that parent as\n * a locally-recording trace root. This is used for an extracted remote parent:\n * the first local recording span should remain the target for local trace-level\n * output, while still inheriting the remote trace/span IDs.\n */\nexport function withNeatlogsRemoteParent<T>(span: Span, fn: () => T): T {\n return privateContextStorage.run(otelTrace.setSpan(ROOT_CONTEXT, span), fn);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Tracer, TracerProvider } from '@opentelemetry/api';\n\nexport interface ActiveNeatlogsClient {\n readonly workflowName: string;\n readonly tracerProvider: TracerProvider;\n getTracer(scope: string): Tracer;\n getLogger(): any | null;\n}\n\nconst ACTIVE_CLIENT_STORAGE_KEY = Symbol.for(\n 'neatlogs.active_client_async_local_storage',\n);\ntype NeatlogsGlobal = typeof globalThis & {\n [ACTIVE_CLIENT_STORAGE_KEY]?: AsyncLocalStorage<ActiveNeatlogsClient>;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\nconst storage =\n neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] ??\n (neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] =\n new AsyncLocalStorage<ActiveNeatlogsClient>());\n\nexport function getActiveClient(): ActiveNeatlogsClient | undefined {\n return storage.getStore();\n}\n\nexport function runWithClient<T>(\n client: ActiveNeatlogsClient,\n fn: () => T,\n): T {\n return storage.run(client, fn);\n}\n"],"mappings":"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","names":["import_api","import_node_async_hooks","neatlogsGlobal","otelTrace","otelTrace","stringified"]}