{"_id":"@agentreplay/agentreplay","name":"@agentreplay/agentreplay","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@agentreplay/agentreplay","version":"0.1.0","description":"Agentreplay SDK for JavaScript/TypeScript - High-performance observability for LLM agents","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean","dev":"tsup src/index.ts --format cjs,esm --dts --watch","test":"vitest run --passWithNoTests","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"eslint src --ext .ts","lint:fix":"eslint src --ext .ts --fix","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","size":"size-limit","analyze":"size-limit --why"},"keywords":["agentreplay","llm","observability","tracing","agents","opentelemetry","genai","langchain","openai","anthropic","ai","monitoring","apm"],"author":{"name":"Sushanth","url":"https://github.com/sushanthpy"},"license":"Apache-2.0","homepage":"https://agentreplay.dev","repository":{"type":"git","url":"git+https://github.com/agentreplay/agentreplay.git","directory":"sdks/js"},"bugs":{"url":"https://github.com/agentreplay/agentreplay/issues"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"dependencies":{},"devDependencies":{"@types/node":"^20.10.0","@size-limit/preset-small-lib":"^11.0.0","size-limit":"^11.0.0","tsup":"^8.0.0","typescript":"^5.3.0","vitest":"^1.0.0"},"peerDependencies":{"openai":">=4.0.0","@anthropic-ai/sdk":">=0.20.0"},"peerDependenciesMeta":{"openai":{"optional":true},"@anthropic-ai/sdk":{"optional":true}},"engines":{"node":">=18.0.0"},"size-limit":[{"path":"dist/index.mjs","limit":"15 KB"}],"_id":"@agentreplay/agentreplay@0.1.0","gitHead":"54b5013de7f23e702f0880e3d9d1e0275197f436","_nodeVersion":"20.20.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-tOGI8rkoh5gBP3FgWnIy459qGcPECQ6UycJaND8rfzHW4oyqUNzS+FHrXTwgooPMWCdhdT3H92mt6PAqd4I0gQ==","shasum":"b9802811065af7355d67fea522936a931a151f22","tarball":"https://registry.npmjs.org/@agentreplay/agentreplay/-/agentreplay-0.1.0.tgz","fileCount":19,"unpackedSize":340494,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCQfmOueNBej+L1LDtRlui5mATLAz3T3NVBd0IcX9eRSQIhAOVid6DQMYXjrIA/r2jfg//vxnMbz7ZNxBU6c9FmnKzn"}]},"_npmUser":{"name":"sushanth","email":"sushanth53@gmail.com"},"directories":{},"maintainers":[{"name":"sushanth","email":"sushanth53@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agentreplay_0.1.0_1770224687175_0.029225926207288255"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-04T17:04:47.121Z","0.1.0":"2026-02-04T17:04:47.349Z","modified":"2026-02-04T17:04:47.520Z"},"maintainers":[{"name":"sushanth","email":"sushanth53@gmail.com"}],"description":"Agentreplay SDK for JavaScript/TypeScript - High-performance observability for LLM agents","homepage":"https://agentreplay.dev","keywords":["agentreplay","llm","observability","tracing","agents","opentelemetry","genai","langchain","openai","anthropic","ai","monitoring","apm"],"repository":{"type":"git","url":"git+https://github.com/agentreplay/agentreplay.git","directory":"sdks/js"},"author":{"name":"Sushanth","url":"https://github.com/sushanthpy"},"bugs":{"url":"https://github.com/agentreplay/agentreplay/issues"},"license":"Apache-2.0","readme":"# Agentreplay JavaScript/TypeScript SDK\n\n[![npm version](https://badge.fury.io/js/@agentreplay%2Fagentreplay.svg)](https://badge.fury.io/js/@agentreplay%2Fagentreplay)\n[![Node.js 18+](https://img.shields.io/badge/node-18+-green.svg)](https://nodejs.org/)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![CI](https://github.com/agentreplay/agentreplay/actions/workflows/ci-nodejs.yaml/badge.svg)](https://github.com/agentreplay/agentreplay/actions/workflows/ci-nodejs.yaml)\n\n**The observability platform for LLM agents and AI applications.** Trace every LLM call, tool invocation, and agent step with minimal code changes.\n\n---\n\n## ✨ Features\n\n| Feature | Description |\n|---------|-------------|\n| 🚀 **Zero-Config Setup** | Works out of the box with environment variables |\n| 🎯 **One-Liner Instrumentation** | Wrap OpenAI/Anthropic clients in one line |\n| 🔧 **Function Wrapping** | `traceable()` for any function |\n| 🔄 **Async Native** | Full support for async/await and Promises |\n| 🔒 **Privacy First** | Built-in PII redaction and scrubbing |\n| 📊 **Token Tracking** | Automatic token usage capture |\n| 🌐 **Framework Agnostic** | Works with LangChain.js, Vercel AI SDK, etc. |\n| ⚡ **Batched Transport** | Efficient background sending with retry |\n| 📦 **Dual Package** | ESM and CommonJS support |\n| 🎨 **TypeScript First** | Full type safety and IntelliSense |\n\n---\n\n## 📦 Installation\n\n```bash\n# npm\nnpm install @agentreplay/agentreplay\n\n# yarn\nyarn add @agentreplay/agentreplay\n\n# pnpm\npnpm add @agentreplay/agentreplay\n```\n\n---\n\n## 🚀 Quick Start\n\n### 1. Set Environment Variables\n\n```bash\nexport AGENTREPLAY_API_KEY=\"your-api-key\"\nexport AGENTREPLAY_PROJECT_ID=\"my-project\"\n# Optional\nexport AGENTREPLAY_BASE_URL=\"https://api.agentreplay.io\"\n```\n\n### 2. Initialize and Trace\n\n```typescript\nimport { init, traceable, flush } from '@agentreplay/agentreplay';\n\n// Initialize (reads from env vars automatically)\ninit();\n\n// Wrap any function for tracing\nconst myAiFunction = traceable(\n  async (query: string) => {\n    // Your AI logic here\n    return `Response to: ${query}`;\n  },\n  { name: 'myAiFunction' }\n);\n\n// Call your function - it's automatically traced!\nconst result = await myAiFunction(\"What is the capital of France?\");\n\n// Ensure all traces are sent before exit\nawait flush();\n```\n\nThat's it! Your function calls are now being traced and sent to Agentreplay.\n\n---\n\n## 🔧 Core API Reference\n\n### Initialization\n\n```typescript\nimport { init, getConfig, resetConfig } from '@agentreplay/agentreplay';\n\n// Option 1: Environment variables (recommended for production)\ninit();\n\n// Option 2: Explicit configuration\ninit({\n  apiKey: 'your-api-key',\n  projectId: 'my-project',\n  baseUrl: 'https://api.agentreplay.io',\n  \n  // Optional settings\n  tenantId: 'default',        // Multi-tenant identifier\n  agentId: 'default',         // Default agent ID\n  enabled: true,              // Set false to disable in tests\n  captureInput: true,         // Capture function inputs\n  captureOutput: true,        // Capture function outputs\n  batchSize: 100,             // Batch size before sending\n  flushInterval: 5000,        // Auto-flush interval in ms\n  debug: false,               // Enable debug logging\n});\n\n// Get current configuration\nconst config = getConfig();\nconsole.log(`Project: ${config.projectId}`);\n\n// Reset to defaults\nresetConfig();\n```\n\n---\n\n## 🎯 The `traceable()` Function\n\nThe primary way to instrument your code:\n\n### Basic Usage\n\n```typescript\nimport { traceable } from '@agentreplay/agentreplay';\n\n// Wrap any async function\nconst processQuery = traceable(\n  async (query: string) => {\n    return await callLlm(query);\n  },\n  { name: 'processQuery' }\n);\n\n// Wrap sync functions too\nconst parseInput = traceable(\n  (input: string) => {\n    return JSON.parse(input);\n  },\n  { name: 'parseInput' }\n);\n```\n\n### With Options\n\n```typescript\nimport { traceable, SpanKind } from '@agentreplay/agentreplay';\n\n// Custom span kind for LLM calls\nconst callOpenAI = traceable(\n  async (messages: Message[]) => {\n    return await openai.chat.completions.create({\n      model: 'gpt-4',\n      messages,\n    });\n  },\n  { \n    name: 'callOpenAI',\n    kind: SpanKind.LLM,\n  }\n);\n\n// Disable input capture for sensitive functions\nconst authenticate = traceable(\n  async (password: string) => {\n    return await verifyPassword(password);\n  },\n  { \n    name: 'authenticate',\n    captureInput: false,\n  }\n);\n\n// Add static metadata\nconst enhancedQuery = traceable(\n  async (query: string) => {\n    return await process(query);\n  },\n  {\n    name: 'enhancedQuery',\n    metadata: { version: '2.0', model: 'gpt-4', team: 'ml' },\n  }\n);\n```\n\n### Type Safety\n\nFull TypeScript support with preserved function signatures:\n\n```typescript\nimport { traceable } from '@agentreplay/agentreplay';\n\ninterface ChatMessage {\n  role: 'user' | 'assistant';\n  content: string;\n}\n\n// Types are preserved\nconst chat = traceable(\n  async (messages: ChatMessage[]): Promise<string> => {\n    // Implementation\n    return response;\n  },\n  { name: 'chat' }\n);\n\n// TypeScript knows the types!\nconst result: string = await chat([\n  { role: 'user', content: 'Hello' }\n]);\n```\n\n---\n\n## 📐 Context Manager: `withSpan()`\n\nFor more control over span attributes and timing:\n\n```typescript\nimport { withSpan, SpanKind } from '@agentreplay/agentreplay';\n\nasync function complexOperation(query: string) {\n  return await withSpan('process_query', async (span) => {\n    // Set input data\n    span.setInput({ query, timestamp: Date.now() });\n    \n    // Nested span for document retrieval\n    const docs = await withSpan('retrieve_documents', async (retrieverSpan) => {\n      const results = await vectorDb.search(query, { topK: 5 });\n      retrieverSpan.setOutput({ documentCount: results.length });\n      retrieverSpan.setAttribute('vectorDb', 'pinecone');\n      return results;\n    }, { kind: SpanKind.RETRIEVER });\n    \n    // Nested span for LLM generation\n    const response = await withSpan('generate_response', async (llmSpan) => {\n      llmSpan.setModel('gpt-4', 'openai');\n      const result = await generateResponse(query, docs);\n      llmSpan.setTokenUsage({\n        promptTokens: 150,\n        completionTokens: 200,\n        totalTokens: 350,\n      });\n      return result;\n    }, { kind: SpanKind.LLM });\n    \n    // Add events for debugging\n    span.addEvent('processing_complete', { docCount: docs.length });\n    \n    // Set final output\n    span.setOutput({ response, sourceCount: docs.length });\n    \n    return { response, sources: docs };\n  }, { kind: SpanKind.CHAIN });\n}\n```\n\n### Manual Span Control\n\nFor cases where you need explicit control over span lifecycle:\n\n```typescript\nimport { startSpan, SpanKind } from '@agentreplay/agentreplay';\n\nasync function longRunningOperation() {\n  const span = startSpan('background_job', {\n    kind: SpanKind.TOOL,\n    input: { jobType: 'data_sync' },\n  });\n  \n  try {\n    // Long running work...\n    for (let i = 0; i < 100; i++) {\n      await processItem(i);\n      if (i % 10 === 0) {\n        span.addEvent('progress', { completed: i });\n      }\n    }\n    \n    span.setOutput({ itemsProcessed: 100 });\n    span.setStatus('ok');\n    \n  } catch (error) {\n    span.captureException(error as Error);\n    span.setStatus('error');\n    throw error;\n    \n  } finally {\n    span.end();  // Always call end()\n  }\n}\n```\n\n---\n\n## 🔌 LLM Client Wrappers\n\n### OpenAI (Recommended)\n\nOne line to instrument all OpenAI calls:\n\n```typescript\nimport OpenAI from 'openai';\nimport { init, wrapOpenAI, flush } from '@agentreplay/agentreplay';\n\ninit();\n\n// Wrap the client - all calls are now traced automatically!\nconst openai = wrapOpenAI(new OpenAI());\n\n// Use normally - tracing happens in the background\nconst response = await openai.chat.completions.create({\n  model: 'gpt-4',\n  messages: [\n    { role: 'system', content: 'You are a helpful assistant.' },\n    { role: 'user', content: 'Explain quantum computing in simple terms.' },\n  ],\n  temperature: 0.7,\n});\n\nconsole.log(response.choices[0].message.content);\n\n// Embeddings are traced too\nconst embedding = await openai.embeddings.create({\n  model: 'text-embedding-ada-002',\n  input: 'Hello world',\n});\n\nawait flush();\n```\n\n**Automatically captured:**\n- Model name\n- Input messages\n- Output content\n- Token usage (prompt, completion, total)\n- Latency\n- Finish reason\n- Errors\n\n### Anthropic\n\n```typescript\nimport Anthropic from '@anthropic-ai/sdk';\nimport { init, wrapAnthropic, flush } from '@agentreplay/agentreplay';\n\ninit();\n\n// Wrap the Anthropic client\nconst anthropic = wrapAnthropic(new Anthropic());\n\n// Use normally\nconst message = await anthropic.messages.create({\n  model: 'claude-3-opus-20240229',\n  max_tokens: 1024,\n  messages: [\n    { role: 'user', content: 'Explain the theory of relativity.' },\n  ],\n});\n\nconsole.log(message.content[0].text);\nawait flush();\n```\n\n### Disable Content Capture\n\nFor privacy-sensitive applications:\n\n```typescript\n// Don't capture message content, only metadata\nconst openai = wrapOpenAI(new OpenAI(), { captureContent: false });\n\n// Traces will still include:\n// - Model name\n// - Token counts\n// - Latency\n// - Error information\n// But NOT the actual messages or responses\n```\n\n### Fetch Instrumentation\n\nTrace all HTTP requests:\n\n```typescript\nimport { init, wrapFetch, installFetchTracing } from '@agentreplay/agentreplay';\n\ninit();\n\n// Option 1: Wrap specific fetch instance\nconst tracedFetch = wrapFetch(fetch);\nconst response = await tracedFetch('https://api.example.com/data');\n\n// Option 2: Install globally (affects all fetch calls)\ninstallFetchTracing();\n\n// Now all fetch calls are traced automatically\nconst data = await fetch('https://api.example.com/users');\n```\n\n---\n\n## 🏷️ Context Management\n\n### Global Context\n\nSet context that applies to ALL subsequent traces:\n\n```typescript\nimport { setGlobalContext, getGlobalContext } from '@agentreplay/agentreplay';\n\n// Set user context (persists until cleared)\nsetGlobalContext({\n  userId: 'user-123',\n  sessionId: 'session-456',\n  agentId: 'support-bot',\n});\n\n// Add more context later (merges with existing)\nsetGlobalContext({\n  environment: 'production',\n  version: '1.2.0',\n  region: 'us-west-2',\n});\n\n// Get current global context\nconst context = getGlobalContext();\nconsole.log(context);\n// { userId: 'user-123', sessionId: 'session-456', ... }\n```\n\n### Request-Scoped Context\n\nFor web applications with per-request context:\n\n```typescript\nimport { withContext } from '@agentreplay/agentreplay';\n\nasync function handleApiRequest(request: Request) {\n  // Context only applies within this callback\n  return await withContext(\n    {\n      userId: request.userId,\n      requestId: request.headers.get('X-Request-ID'),\n      path: new URL(request.url).pathname,\n    },\n    async () => {\n      // All traces in here include this context\n      const result = await processRequest(request);\n      return result;\n    }\n  );\n  // Context automatically cleared after callback\n}\n```\n\n### Bind Context for Callbacks\n\nPreserve context across async boundaries:\n\n```typescript\nimport { bindContext, setGlobalContext } from '@agentreplay/agentreplay';\n\nsetGlobalContext({ requestId: 'req-123' });\n\n// Bind current context to a callback\nconst boundCallback = bindContext(async () => {\n  // This runs with the context from when bindContext was called\n  // Even if called later in a different async context\n  return await processAsync();\n});\n\n// Later, even in a different async context\nsetTimeout(boundCallback, 1000);\n\n// Or with event emitters\nemitter.on('data', bindContext(async (data) => {\n  // Context preserved here\n  await handleData(data);\n}));\n```\n\n---\n\n## 🔒 Privacy & Data Redaction\n\n### Configure Privacy Settings\n\n```typescript\nimport { configurePrivacy } from '@agentreplay/agentreplay';\n\nconfigurePrivacy({\n  // Enable built-in scrubbers for common PII\n  enableBuiltinScrubbers: true,  // Emails, credit cards, SSNs, phones, API keys\n  \n  // Add custom regex patterns\n  customPatterns: [\n    /secret-\\w+/gi,           // Custom secret format\n    /internal-id-\\d+/gi,      // Internal IDs\n    /password:\\s*\\S+/gi,      // Password fields\n  ],\n  \n  // Completely scrub these JSON paths\n  scrubPaths: [\n    'input.password',\n    'input.credentials.apiKey',\n    'output.user.ssn',\n    'metadata.internalToken',\n  ],\n  \n  // Hash PII instead of replacing with [REDACTED]\n  // Allows tracking unique values without exposing data\n  hashPii: true,\n  hashSalt: 'your-secret-salt-here',\n});\n```\n\n### Built-in Scrubbers\n\nThe SDK includes patterns for:\n\n| Type | Example | Redacted As |\n|------|---------|-------------|\n| Email | user@example.com | [REDACTED] |\n| Credit Card | 4111-1111-1111-1111 | [REDACTED] |\n| SSN | 123-45-6789 | [REDACTED] |\n| Phone (US) | +1-555-123-4567 | [REDACTED] |\n| Phone (Intl) | +44-20-1234-5678 | [REDACTED] |\n| API Key | sk-proj-abc123... | [REDACTED] |\n| Bearer Token | Bearer eyJ... | [REDACTED] |\n| JWT | eyJhbG... | [REDACTED] |\n| IP Address | 192.168.1.1 | [REDACTED] |\n\n### Manual Redaction\n\n```typescript\nimport { redactPayload, hashPII } from '@agentreplay/agentreplay';\n\n// Redact an entire payload\nconst data = {\n  user: {\n    email: 'john@example.com',\n    phone: '+1-555-123-4567',\n  },\n  message: 'My credit card is 4111-1111-1111-1111',\n  apiKey: 'sk-proj-abcdefghijk',\n};\n\nconst safeData = redactPayload(data);\n// Result:\n// {\n//   user: {\n//     email: '[REDACTED]',\n//     phone: '[REDACTED]',\n//   },\n//   message: 'My credit card is [REDACTED]',\n//   apiKey: '[REDACTED]',\n// }\n\n// Hash for consistent anonymization (same input = same hash)\nconst userHash = hashPII('user@example.com');\n// '[HASH:a1b2c3d4]'\n\n// Useful for analytics without exposing PII\nconsole.log(`User ${userHash} performed action`);\n```\n\n---\n\n## 📊 Sampling\n\nControl which traces are captured to manage costs and volume:\n\n```typescript\nimport { configureSampling } from '@agentreplay/agentreplay';\n\nconfigureSampling({\n  // Sample 10% of traces (0.0 to 1.0)\n  sampleRate: 0.1,\n  \n  // Always sample errors regardless of rate\n  alwaysSampleErrors: true,\n  \n  // Always sample slow operations (>5 seconds)\n  alwaysSampleSlowThreshold: 5000,\n  \n  // Fine-grained rules for specific operations\n  rules: [\n    // Always sample LLM calls (100%)\n    { match: { kind: 'llm' }, sampleRate: 1.0 },\n    \n    // Sample 50% of retriever calls\n    { match: { kind: 'retriever' }, sampleRate: 0.5 },\n    \n    // Never sample health checks\n    { match: { name: /health|ping|ready/i }, sampleRate: 0 },\n    \n    // Sample by user for consistent experience\n    { match: { userId: '*' }, sampleRate: 0.1, deterministic: true },\n  ],\n});\n```\n\n---\n\n## 📊 Span Kinds\n\nUse semantic span kinds for better visualization and filtering:\n\n```typescript\nimport { SpanKind } from '@agentreplay/agentreplay';\n\n// Available span kinds\nSpanKind.CHAIN       // Orchestration, workflows, pipelines\nSpanKind.LLM         // LLM API calls (OpenAI, Anthropic, etc.)\nSpanKind.TOOL        // Tool/function calls, actions\nSpanKind.RETRIEVER   // Vector DB search, document retrieval\nSpanKind.EMBEDDING   // Embedding generation\nSpanKind.GUARDRAIL   // Safety checks, content filtering\nSpanKind.CACHE       // Cache operations\nSpanKind.HTTP        // HTTP requests\nSpanKind.DB          // Database queries\n```\n\nExample usage:\n\n```typescript\nimport { traceable, SpanKind } from '@agentreplay/agentreplay';\n\nconst searchDocuments = traceable(\n  async (query: string) => {\n    return await vectorDb.similaritySearch(query, { k: 5 });\n  },\n  { name: 'searchDocuments', kind: SpanKind.RETRIEVER }\n);\n\nconst generateAnswer = traceable(\n  async (query: string, docs: Document[]) => {\n    return await llm.generate(query, { context: docs });\n  },\n  { name: 'generateAnswer', kind: SpanKind.LLM }\n);\n\nconst ragPipeline = traceable(\n  async (query: string) => {\n    const docs = await searchDocuments(query);\n    return await generateAnswer(query, docs);\n  },\n  { name: 'ragPipeline', kind: SpanKind.CHAIN }\n);\n```\n\n---\n\n## ⚙️ Lifecycle Management\n\n### Flushing Traces\n\nAlways ensure traces are sent before your application exits:\n\n```typescript\nimport { init, flush, shutdown } from '@agentreplay/agentreplay';\n\ninit();\n\n// Your application code...\n\n// Option 1: Manual flush with timeout\nawait flush(10000);  // Wait up to 10 seconds\n\n// Option 2: Full graceful shutdown\nawait shutdown(30000);  // Flush and cleanup\n\n// Option 3: Process handlers (added automatically by init())\nprocess.on('beforeExit', async () => {\n  await flush();\n});\n```\n\n### Serverless / AWS Lambda\n\n**Critical**: Always flush explicitly before the function returns!\n\n```typescript\nimport { init, traceable, flush } from '@agentreplay/agentreplay';\n\ninit();\n\nconst processEvent = traceable(\n  async (event: any) => {\n    // Your logic here\n    return { processed: true };\n  },\n  { name: 'processEvent' }\n);\n\nexport const handler = async (event: any, context: any) => {\n  try {\n    const result = await processEvent(event);\n    return {\n      statusCode: 200,\n      body: JSON.stringify(result),\n    };\n  } finally {\n    // CRITICAL: Flush before Lambda freezes\n    await flush(5000);\n  }\n};\n```\n\n### Next.js / Vercel\n\n```typescript\n// instrumentation.ts (Next.js 13+)\nimport { init } from '@agentreplay/agentreplay';\n\nexport function register() {\n  init();\n}\n\n// In your API route or Server Component\nimport { flush, withSpan } from '@agentreplay/agentreplay';\n\nexport async function POST(request: Request) {\n  try {\n    return await withSpan('api_chat', async (span) => {\n      const body = await request.json();\n      span.setInput(body);\n      \n      const result = await processChat(body);\n      span.setOutput(result);\n      \n      return Response.json(result);\n    });\n  } finally {\n    // Flush in edge/serverless\n    await flush(5000);\n  }\n}\n```\n\n### Express.js Middleware\n\n```typescript\nimport express from 'express';\nimport { init, withContext, flush } from '@agentreplay/agentreplay';\n\ninit();\n\nconst app = express();\n\n// Add tracing context for each request\napp.use((req, res, next) => {\n  const requestId = req.headers['x-request-id'] as string || crypto.randomUUID();\n  \n  withContext(\n    { \n      requestId, \n      path: req.path,\n      method: req.method,\n    },\n    () => next()\n  );\n});\n\n// Graceful shutdown\nconst server = app.listen(3000);\n\nprocess.on('SIGTERM', async () => {\n  server.close();\n  await flush(10000);\n  process.exit(0);\n});\n```\n\n---\n\n## 🔗 Framework Integrations\n\n### LangChain.js\n\n```typescript\nimport { ChatOpenAI } from '@langchain/openai';\nimport { init, traceable, SpanKind, flush } from '@agentreplay/agentreplay';\n\ninit();\n\nconst answerQuestion = traceable(\n  async (question: string) => {\n    const llm = new ChatOpenAI({ modelName: 'gpt-4', temperature: 0 });\n    const response = await llm.invoke(question);\n    return response.content;\n  },\n  { name: 'langchain_qa', kind: SpanKind.CHAIN }\n);\n\nconst result = await answerQuestion('What is machine learning?');\nawait flush();\n```\n\n### Vercel AI SDK\n\n```typescript\nimport { streamText } from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { init, withSpan, SpanKind, flush } from '@agentreplay/agentreplay';\n\ninit();\n\nexport async function POST(request: Request) {\n  const { prompt } = await request.json();\n  \n  return await withSpan('ai_stream', async (span) => {\n    span.setInput({ prompt });\n    span.setModel('gpt-4', 'openai');\n    \n    const result = await streamText({\n      model: openai('gpt-4'),\n      prompt,\n      onFinish: async ({ usage }) => {\n        span.setTokenUsage({\n          promptTokens: usage.promptTokens,\n          completionTokens: usage.completionTokens,\n          totalTokens: usage.totalTokens,\n        });\n        await flush(2000);\n      },\n    });\n    \n    return result.toDataStreamResponse();\n  }, { kind: SpanKind.LLM });\n}\n```\n\n### Hono (Edge Runtime)\n\n```typescript\nimport { Hono } from 'hono';\nimport { init, traceable, flush } from '@agentreplay/agentreplay';\n\ninit();\n\nconst app = new Hono();\n\nconst processMessage = traceable(\n  async (message: string) => {\n    return await callLLM(message);\n  },\n  { name: 'processMessage' }\n);\n\napp.post('/chat', async (c) => {\n  try {\n    const { message } = await c.req.json();\n    const response = await processMessage(message);\n    return c.json({ response });\n  } finally {\n    await flush(3000);\n  }\n});\n\nexport default app;\n```\n\n---\n\n## 🌐 Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `AGENTREPLAY_API_KEY` | API key for authentication | **Required** |\n| `AGENTREPLAY_PROJECT_ID` | Project identifier | **Required** |\n| `AGENTREPLAY_BASE_URL` | API base URL | `https://api.agentreplay.io` |\n| `AGENTREPLAY_TENANT_ID` | Tenant identifier | `default` |\n| `AGENTREPLAY_AGENT_ID` | Default agent ID | `default` |\n| `AGENTREPLAY_ENABLED` | Enable/disable tracing | `true` |\n| `AGENTREPLAY_DEBUG` | Enable debug logging | `false` |\n| `AGENTREPLAY_BATCH_SIZE` | Spans per batch | `100` |\n| `AGENTREPLAY_FLUSH_INTERVAL` | Auto-flush interval (ms) | `5000` |\n| `AGENTREPLAY_CAPTURE_INPUT` | Capture function inputs | `true` |\n| `AGENTREPLAY_CAPTURE_OUTPUT` | Capture function outputs | `true` |\n\n---\n\n## 🧪 Testing\n\n### Disable Tracing in Tests\n\n```typescript\nimport { init, resetConfig } from '@agentreplay/agentreplay';\n\nbeforeAll(() => {\n  init({ enabled: false });\n});\n\nafterAll(() => {\n  resetConfig();\n});\n\ntest('my function works', async () => {\n  // Tracing is disabled, no network calls\n  const result = await myTracedFunction('test');\n  expect(result).toBe(expected);\n});\n```\n\nOr use environment variable:\n\n```bash\nAGENTREPLAY_ENABLED=false npm test\n```\n\n### Mock the SDK\n\n```typescript\nimport { jest } from '@jest/globals';\n\njest.mock('@agentreplay/agentreplay', () => ({\n  init: jest.fn(),\n  traceable: (fn: Function) => fn,  // Pass-through\n  flush: jest.fn().mockResolvedValue(undefined),\n}));\n```\n\n---\n\n## 📦 Package Exports\n\nThe SDK provides multiple entry points for different use cases:\n\n```typescript\n// Main entry (recommended)\nimport { \n  init, \n  traceable, \n  wrapOpenAI,\n  flush,\n} from '@agentreplay/agentreplay';\n\n// Types only (for TypeScript)\nimport type { \n  Span, \n  SpanKind, \n  Config,\n  SamplingConfig,\n  PrivacyConfig,\n} from '@agentreplay/agentreplay';\n```\n\n---\n\n## 📚 Complete API Reference\n\n### Initialization\n\n| Function | Description |\n|----------|-------------|\n| `init(config?)` | Initialize the SDK with configuration |\n| `getConfig()` | Get current configuration |\n| `resetConfig()` | Reset to defaults |\n\n### Tracing\n\n| Function | Description |\n|----------|-------------|\n| `traceable(fn, opts)` | Wrap a function for tracing |\n| `withSpan(name, fn, opts)` | Execute callback with a span context |\n| `startSpan(name, opts)` | Create a manual span |\n| `captureException(error)` | Capture an error in current span |\n\n### Client Wrappers\n\n| Function | Description |\n|----------|-------------|\n| `wrapOpenAI(client, opts)` | Wrap OpenAI client |\n| `wrapAnthropic(client, opts)` | Wrap Anthropic client |\n| `wrapFetch(fetch, opts)` | Wrap fetch function |\n| `installFetchTracing()` | Install global fetch tracing |\n\n### Context\n\n| Function | Description |\n|----------|-------------|\n| `setGlobalContext(ctx)` | Set global context |\n| `getGlobalContext()` | Get current global context |\n| `withContext(ctx, fn)` | Run callback with scoped context |\n| `bindContext(fn)` | Bind current context to callback |\n\n### Transport\n\n| Function | Description |\n|----------|-------------|\n| `flush(timeout?)` | Flush pending spans |\n| `shutdown(timeout?)` | Graceful shutdown |\n\n### Privacy\n\n| Function | Description |\n|----------|-------------|\n| `configurePrivacy(opts)` | Configure redaction settings |\n| `redactPayload(data)` | Redact sensitive data from object |\n| `hashPII(value, salt?)` | Hash PII for anonymization |\n\n### Sampling\n\n| Function | Description |\n|----------|-------------|\n| `configureSampling(opts)` | Configure sampling rules |\n\n### Span Methods\n\n| Method | Description |\n|--------|-------------|\n| `setInput(data)` | Set span input data |\n| `setOutput(data)` | Set span output data |\n| `setAttribute(key, value)` | Set a single attribute |\n| `setAttributes(obj)` | Set multiple attributes |\n| `addEvent(name, attrs)` | Add a timestamped event |\n| `captureException(error)` | Record an error |\n| `setTokenUsage(usage)` | Set LLM token counts |\n| `setModel(model, provider)` | Set model information |\n| `setStatus(status)` | Set span status |\n| `end()` | End the span |\n\n---\n\n## 🤝 Contributing\n\nWe welcome contributions! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.\n\n```bash\n# Clone the repository\ngit clone https://github.com/agentreplay/agentreplay.git\ncd agentreplay/sdks/js\n\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Run tests\nnpm test\n\n# Run linter\nnpm run lint\n\n# Type check\nnpm run typecheck\n\n# Format code\nnpm run format\n```\n\n---\n\n## 📄 License\n\nApache 2.0 - see [LICENSE](../../LICENSE) for details.\n\n---\n\n## 🔗 Links\n\n- 📖 [Documentation](https://docs.agentreplay.io)\n- 💻 [GitHub Repository](https://github.com/agentreplay/agentreplay)\n- 📦 [npm Package](https://www.npmjs.com/package/@agentreplay/agentreplay)\n- 💬 [Discord Community](https://discord.gg/agentreplay)\n- 🐦 [Twitter](https://twitter.com/agentreplay)\n\n---\n\n<p align=\"center\">\n  Made with ❤️ by the Agentreplay team\n</p>\n","readmeFilename":"README.md","_rev":"1-5435f0f612a00d7570109b253fb2e444"}