@sentrial/sdk 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1345 -641
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +343 -142
- package/dist/index.d.ts +343 -142
- package/dist/index.js +1341 -640
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/redact.ts","../src/cost.ts","../src/types.ts","../src/client.ts","../src/vercel.ts","../src/wrappers.ts","../src/decorators.ts","../src/context.ts","../src/experiment.ts"],"sourcesContent":["/**\n * Sentrial SDK for TypeScript/Node.js\n *\n * AI agent observability and monitoring. Track sessions, tool calls, and metrics\n * to power signal detection, root cause analysis, and AI-suggested fixes.\n *\n * @packageDocumentation\n *\n * @example Basic Usage\n * ```ts\n * import { SentrialClient } from '@sentrial/sdk';\n *\n * const client = new SentrialClient({\n * apiKey: process.env.SENTRIAL_API_KEY!,\n * });\n *\n * // Create session\n * const sessionId = await client.createSession({\n * name: 'Support Request',\n * agentName: 'support-agent',\n * userId: 'user_123',\n * });\n *\n * // Track tool calls\n * await client.trackToolCall({\n * sessionId,\n * toolName: 'search_kb',\n * toolInput: { query: 'help' },\n * toolOutput: { results: ['...'] },\n * });\n *\n * // Complete session\n * await client.completeSession({\n * sessionId,\n * success: true,\n * customMetrics: { satisfaction: 4.5 },\n * });\n * ```\n *\n * @example Simple begin/finish Pattern\n * ```ts\n * import { sentrial } from '@sentrial/sdk';\n *\n * sentrial.configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const interaction = await sentrial.begin({\n * userId: 'user_123',\n * event: 'chat_message',\n * input: message,\n * });\n *\n * // ... agent work ...\n *\n * await interaction.finish({ output: response, success: true });\n * ```\n */\n\n// Main client\nexport { SentrialClient, Interaction, configure, begin, sentrial } from './client.js';\n\n// Types\nexport type {\n SentrialClientConfig,\n CreateSessionParams,\n CompleteSessionParams,\n TrackToolCallParams,\n TrackDecisionParams,\n TrackErrorParams,\n BeginParams,\n FinishParams,\n Session,\n Event,\n SessionStatus,\n CostParams,\n ApiResponse,\n} from './types.js';\n\n// Enums\nexport { EventType } from './types.js';\n\n// Cost calculation utilities\nexport {\n calculateOpenAICost,\n calculateAnthropicCost,\n calculateGoogleCost,\n} from './cost.js';\n\n// Errors\nexport {\n SentrialError,\n ApiError,\n NetworkError,\n ValidationError,\n} from './errors.js';\n\n// Vercel AI SDK Integration\nexport {\n wrapAISDK,\n configureVercel,\n} from './vercel.js';\n\nexport type {\n GenerateTextParams,\n GenerateTextResult,\n StreamTextResult,\n} from './vercel.js';\n\n// LLM Auto-Wrappers\nexport {\n wrapOpenAI,\n wrapAnthropic,\n wrapGoogle,\n wrapLLM,\n setSessionContext,\n clearSessionContext,\n getSessionContext,\n setDefaultClient,\n} from './wrappers.js';\n\n// Decorators & Higher-Order Functions\nexport {\n withTool,\n withSession,\n Tool,\n TrackSession,\n SessionContext,\n setClient,\n getCurrentSessionId,\n getCurrentInteraction,\n} from './decorators.js';\n\n// Experiment Context\nexport {\n getSystemPrompt,\n getExperimentContext,\n isExperimentMode,\n getVariantName,\n getExperimentId,\n setExperimentContext,\n clearExperimentContext,\n} from './context.js';\n\nexport type { ExperimentContext } from './context.js';\n\n// Experiments\nexport {\n Experiment,\n ExperimentRunTracker,\n} from './experiment.js';\n\nexport type {\n ExperimentVariant,\n ExperimentTestCase,\n ExperimentRunResult,\n ExperimentResults,\n} from './experiment.js';\n\n// PII Redaction\nexport { redactString, redactValue, redactPayload, hashValue, replaceMatch } from './redact.js';\n\nexport type {\n PiiMode,\n PiiField,\n PiiCustomPattern,\n PiiBuiltinPatterns,\n PiiConfig,\n} from './redact.js';\n","/**\n * Error classes for the Sentrial SDK\n */\n\n/**\n * Base error class for all Sentrial SDK errors\n */\nexport class SentrialError extends Error {\n /** HTTP status code (if applicable) */\n public readonly status?: number;\n /** Error code from the API */\n public readonly code?: string;\n /** Additional error details */\n public readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n options?: {\n status?: number;\n code?: string;\n details?: Record<string, unknown>;\n cause?: Error;\n }\n ) {\n super(message, { cause: options?.cause });\n this.name = 'SentrialError';\n this.status = options?.status;\n this.code = options?.code;\n this.details = options?.details;\n\n // Maintain proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SentrialError);\n }\n }\n\n /**\n * Check if the error is a rate limit error\n */\n isRateLimitError(): boolean {\n return this.status === 429;\n }\n\n /**\n * Check if the error is an authentication error\n */\n isAuthError(): boolean {\n return this.status === 401 || this.status === 403;\n }\n\n /**\n * Check if the error is a network/connection error\n */\n isNetworkError(): boolean {\n return this.code === 'NETWORK_ERROR' || this.code === 'ECONNREFUSED';\n }\n\n /**\n * Convert to a plain object for logging\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n code: this.code,\n details: this.details,\n stack: this.stack,\n };\n }\n}\n\n/**\n * Error thrown when the API returns an error response\n */\nexport class ApiError extends SentrialError {\n constructor(\n message: string,\n status: number,\n code?: string,\n details?: Record<string, unknown>\n ) {\n super(message, { status, code, details });\n this.name = 'ApiError';\n }\n}\n\n/**\n * Error thrown when there's a network/connection issue\n */\nexport class NetworkError extends SentrialError {\n constructor(message: string, cause?: Error) {\n super(message, { code: 'NETWORK_ERROR', cause });\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Error thrown when validation fails\n */\nexport class ValidationError extends SentrialError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, { code: 'VALIDATION_ERROR', details });\n this.name = 'ValidationError';\n }\n}\n","/**\n * PII Redaction Module\n *\n * Automatically detects and redacts personally identifiable information (PII)\n * from SDK payloads before they are sent to the Sentrial API.\n */\n\nimport { createHash } from 'crypto';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** How redacted values are replaced */\nexport type PiiMode = 'label' | 'hash' | 'remove';\n\n/** Fields that can be redacted in SDK payloads */\nexport type PiiField =\n | 'userInput'\n | 'assistantOutput'\n | 'toolInput'\n | 'toolOutput'\n | 'metadata'\n | 'reasoning'\n | 'failureReason';\n\n/** A custom regex pattern with a human-readable label */\nexport interface PiiCustomPattern {\n pattern: RegExp;\n label: string;\n}\n\n/** Toggle individual builtin pattern categories */\nexport interface PiiBuiltinPatterns {\n emails?: boolean;\n phones?: boolean;\n ssns?: boolean;\n creditCards?: boolean;\n ipAddresses?: boolean;\n}\n\n/** Full PII redaction configuration */\nexport interface PiiConfig {\n /** Enable PII redaction (default: false) */\n enabled: boolean;\n /** Which payload fields to redact (default: DEFAULT_FIELDS) */\n fields?: PiiField[];\n /** Replacement mode (default: 'label') */\n mode?: PiiMode;\n /** Enable enhanced detection heuristics (reserved for future use) */\n enhancedDetection?: boolean;\n /** Toggle builtin patterns (default: all enabled) */\n builtinPatterns?: PiiBuiltinPatterns;\n /** Additional custom patterns to apply */\n customPatterns?: PiiCustomPattern[];\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default fields that are redacted when PII redaction is enabled */\nexport const DEFAULT_FIELDS: PiiField[] = [\n 'userInput',\n 'assistantOutput',\n 'toolInput',\n 'toolOutput',\n];\n\n/** Default builtin pattern toggles (all enabled) */\nexport const DEFAULT_BUILTIN: Required<PiiBuiltinPatterns> = {\n emails: true,\n phones: true,\n ssns: true,\n creditCards: true,\n ipAddresses: true,\n};\n\n// ============================================================================\n// Builtin Regex Patterns\n// ============================================================================\n\n/** Email addresses (RFC 5322 simplified) */\nconst EMAIL_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n\n/** US phone numbers: (###) ###-####, ###-###-####, ### ### ####, +1########## */\nconst PHONE_PATTERN =\n /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/g;\n\n/** US Social Security Numbers: ###-##-#### */\nconst SSN_PATTERN = /\\b\\d{3}-\\d{2}-\\d{4}\\b/g;\n\n/** Credit card numbers: 13-19 digits, optionally separated by spaces or dashes */\nconst CREDIT_CARD_PATTERN = /\\b(?:\\d[-\\s]?){13,19}\\b/g;\n\n/** IPv4 addresses */\nconst IP_ADDRESS_PATTERN =\n /\\b(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\b/g;\n\n/** Map of builtin pattern keys to their regex and label */\nconst BUILTIN_PATTERNS: Record<keyof Required<PiiBuiltinPatterns>, { pattern: RegExp; label: string }> = {\n emails: { pattern: EMAIL_PATTERN, label: 'EMAIL' },\n phones: { pattern: PHONE_PATTERN, label: 'PHONE' },\n ssns: { pattern: SSN_PATTERN, label: 'SSN' },\n creditCards: { pattern: CREDIT_CARD_PATTERN, label: 'CREDIT_CARD' },\n ipAddresses: { pattern: IP_ADDRESS_PATTERN, label: 'IP_ADDRESS' },\n};\n\n// ============================================================================\n// Core Functions\n// ============================================================================\n\n/**\n * Hash a value using SHA-256 and return the first 6 hex characters.\n * Used in 'hash' mode to produce a short, deterministic pseudonym.\n */\nexport function hashValue(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 6);\n}\n\n/**\n * Produce the replacement string for a matched PII value.\n *\n * - 'label' mode: [LABEL]\n * - 'hash' mode: [PII:abcdef]\n * - 'remove' mode: '' (empty string)\n */\nexport function replaceMatch(match: string, label: string, mode: PiiMode): string {\n switch (mode) {\n case 'label':\n return `[${label}]`;\n case 'hash':\n return `[PII:${hashValue(match)}]`;\n case 'remove':\n return '';\n }\n}\n\n/**\n * Run all enabled builtin and custom patterns against a single string,\n * replacing every match according to the specified mode.\n */\nexport function redactString(\n value: string,\n mode: PiiMode,\n builtinPatterns: Required<PiiBuiltinPatterns>,\n customPatterns: PiiCustomPattern[],\n): string {\n let result = value;\n\n // Apply enabled builtin patterns\n for (const [key, config] of Object.entries(BUILTIN_PATTERNS)) {\n if (builtinPatterns[key as keyof PiiBuiltinPatterns]) {\n // Create a fresh regex instance to reset lastIndex\n const regex = new RegExp(config.pattern.source, config.pattern.flags);\n result = result.replace(regex, (match) => replaceMatch(match, config.label, mode));\n }\n }\n\n // Apply custom patterns\n for (const custom of customPatterns) {\n const regex = new RegExp(custom.pattern.source, custom.pattern.flags);\n result = result.replace(regex, (match) => replaceMatch(match, custom.label, mode));\n }\n\n return result;\n}\n\n/**\n * Recursively redact PII from a value. Handles strings, arrays, plain objects,\n * and passes through primitives (numbers, booleans, null, undefined) unchanged.\n */\nexport function redactValue(\n value: unknown,\n mode: PiiMode,\n builtinPatterns: Required<PiiBuiltinPatterns>,\n customPatterns: PiiCustomPattern[],\n): unknown {\n if (typeof value === 'string') {\n return redactString(value, mode, builtinPatterns, customPatterns);\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => redactValue(item, mode, builtinPatterns, customPatterns));\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n result[key] = redactValue(val, mode, builtinPatterns, customPatterns);\n }\n return result;\n }\n\n // Primitives (number, boolean, null, undefined) pass through unchanged\n return value;\n}\n\n/**\n * Main entry point: redact configured fields in a payload object.\n *\n * Only fields listed in the PiiConfig (or DEFAULT_FIELDS) are redacted.\n * Other fields are returned unchanged.\n *\n * @param payload - The data payload (e.g., session params, tool call params)\n * @param config - PII configuration from the client\n * @returns A new payload object with PII redacted from the configured fields\n */\nexport function redactPayload(\n payload: Record<string, unknown>,\n config: PiiConfig,\n): Record<string, unknown> {\n if (!config.enabled) {\n return payload;\n }\n\n const mode = config.mode ?? 'label';\n const fields = config.fields ?? DEFAULT_FIELDS;\n const builtinPatterns: Required<PiiBuiltinPatterns> = {\n ...DEFAULT_BUILTIN,\n ...config.builtinPatterns,\n };\n const customPatterns = config.customPatterns ?? [];\n\n const result: Record<string, unknown> = { ...payload };\n\n for (const field of fields) {\n if (field in result && result[field] !== undefined && result[field] !== null) {\n result[field] = redactValue(result[field], mode, builtinPatterns, customPatterns);\n }\n }\n\n return result;\n}\n","/**\n * Cost calculation utilities for various LLM providers\n */\n\nimport type { CostParams } from './types.js';\n\n// ============================================================================\n// Pricing Data (per 1M tokens) - Updated Jan 2026\n// ============================================================================\n\nconst OPENAI_PRICING: Record<string, { input: number; output: number }> = {\n 'gpt-5.2': { input: 5.0, output: 15.0 },\n 'gpt-5': { input: 4.0, output: 12.0 },\n 'gpt-4.1': { input: 2.0, output: 8.0 },\n 'gpt-4.1-mini': { input: 0.4, output: 1.6 },\n 'gpt-4.1-nano': { input: 0.1, output: 0.4 },\n 'gpt-4o': { input: 2.5, output: 10.0 },\n 'gpt-4o-mini': { input: 0.15, output: 0.6 },\n 'gpt-4-turbo': { input: 10.0, output: 30.0 },\n 'gpt-4': { input: 30.0, output: 60.0 },\n 'gpt-3.5-turbo': { input: 0.5, output: 1.5 },\n 'o4-mini': { input: 1.1, output: 4.4 },\n 'o3': { input: 10.0, output: 40.0 },\n 'o3-mini': { input: 1.1, output: 4.4 },\n 'o3-pro': { input: 20.0, output: 80.0 },\n 'o1': { input: 15.0, output: 60.0 },\n 'o1-preview': { input: 15.0, output: 60.0 },\n 'o1-mini': { input: 3.0, output: 12.0 },\n};\n\nconst ANTHROPIC_PRICING: Record<string, { input: number; output: number }> = {\n 'claude-opus-4': { input: 15.0, output: 75.0 },\n 'claude-sonnet-4': { input: 3.0, output: 15.0 },\n 'claude-4.5-opus': { input: 20.0, output: 100.0 },\n 'claude-4.5-sonnet': { input: 4.0, output: 20.0 },\n 'claude-4-opus': { input: 18.0, output: 90.0 },\n 'claude-4-sonnet': { input: 3.5, output: 17.5 },\n 'claude-3-7-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-5-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-5-haiku': { input: 0.8, output: 4.0 },\n 'claude-3-opus': { input: 15.0, output: 75.0 },\n 'claude-3-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-haiku': { input: 0.25, output: 1.25 },\n};\n\nconst GOOGLE_PRICING: Record<string, { input: number; output: number }> = {\n // Gemini 3 - Preview (Jan 2026)\n 'gemini-3-pro': { input: 2.0, output: 12.0 },\n 'gemini-3-flash': { input: 0.5, output: 3.0 },\n // Gemini 2.5\n 'gemini-2.5-pro': { input: 1.25, output: 5.0 },\n 'gemini-2.5-flash': { input: 0.15, output: 0.6 },\n // Gemini 2.0\n 'gemini-2.0-flash': { input: 0.1, output: 0.4 },\n 'gemini-2.0-flash-lite': { input: 0.075, output: 0.3 },\n // Gemini 1.5\n 'gemini-1.5-pro': { input: 1.25, output: 5.0 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.3 },\n // Gemini 1.0\n 'gemini-1.0-pro': { input: 0.5, output: 1.5 },\n};\n\n// ============================================================================\n// Helper Function\n// ============================================================================\n\nfunction findModelKey(\n model: string,\n pricing: Record<string, { input: number; output: number }>\n): string | null {\n for (const key of Object.keys(pricing)) {\n if (model.startsWith(key)) {\n return key;\n }\n }\n return null;\n}\n\nfunction calculateCost(\n inputTokens: number,\n outputTokens: number,\n rates: { input: number; output: number }\n): number {\n const inputCost = (inputTokens / 1_000_000) * rates.input;\n const outputCost = (outputTokens / 1_000_000) * rates.output;\n return inputCost + outputCost;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Calculate cost for OpenAI API calls\n *\n * @param params - Cost calculation parameters\n * @returns Estimated cost in USD\n *\n * @example\n * ```ts\n * const cost = calculateOpenAICost({\n * model: 'gpt-4o',\n * inputTokens: 1000,\n * outputTokens: 500,\n * });\n * console.log(`Cost: $${cost.toFixed(4)}`);\n * ```\n */\nexport function calculateOpenAICost(params: CostParams): number {\n const { model, inputTokens, outputTokens } = params;\n const modelKey = findModelKey(model, OPENAI_PRICING) ?? 'gpt-4';\n return calculateCost(inputTokens, outputTokens, OPENAI_PRICING[modelKey]!);\n}\n\n/**\n * Calculate cost for Anthropic API calls\n *\n * @param params - Cost calculation parameters\n * @returns Estimated cost in USD\n *\n * @example\n * ```ts\n * const cost = calculateAnthropicCost({\n * model: 'claude-3-5-sonnet',\n * inputTokens: 1000,\n * outputTokens: 500,\n * });\n * console.log(`Cost: $${cost.toFixed(4)}`);\n * ```\n */\nexport function calculateAnthropicCost(params: CostParams): number {\n const { model, inputTokens, outputTokens } = params;\n const modelKey = findModelKey(model, ANTHROPIC_PRICING) ?? 'claude-3-sonnet';\n return calculateCost(inputTokens, outputTokens, ANTHROPIC_PRICING[modelKey]!);\n}\n\n/**\n * Calculate cost for Google/Gemini API calls\n *\n * @param params - Cost calculation parameters\n * @returns Estimated cost in USD\n *\n * @example\n * ```ts\n * const cost = calculateGoogleCost({\n * model: 'gemini-2.0-flash',\n * inputTokens: 1000,\n * outputTokens: 500,\n * });\n * console.log(`Cost: $${cost.toFixed(4)}`);\n * ```\n */\nexport function calculateGoogleCost(params: CostParams): number {\n const { model, inputTokens, outputTokens } = params;\n const modelKey = findModelKey(model, GOOGLE_PRICING) ?? 'gemini-2.0-flash';\n return calculateCost(inputTokens, outputTokens, GOOGLE_PRICING[modelKey]!);\n}\n","/**\n * Type definitions for the Sentrial TypeScript SDK\n */\n\nimport type { PiiConfig } from './redact.js';\n\n// ============================================================================\n// Enums\n// ============================================================================\n\n/**\n * Event types that can be tracked in a session\n */\nexport enum EventType {\n TOOL_CALL = 'tool_call',\n LLM_DECISION = 'llm_decision',\n STATE_CHANGE = 'state_change',\n ERROR = 'error',\n}\n\n/**\n * Session status values\n */\nexport type SessionStatus = 'running' | 'completed' | 'failed' | 'cancelled';\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/**\n * Configuration options for SentrialClient\n */\nexport interface SentrialClientConfig {\n /** API key for authentication (defaults to SENTRIAL_API_KEY env var) */\n apiKey?: string;\n /** URL of the Sentrial API server (defaults to SENTRIAL_API_URL env var or production) */\n apiUrl?: string;\n /**\n * If true (default), SDK errors are logged but won't crash your app.\n * Set to false during development to see full errors.\n */\n failSilently?: boolean;\n /**\n * PII redaction configuration.\n * - Pass a PiiConfig object for full control.\n * - Pass `true` to fetch the org's PII config from the server automatically.\n */\n pii?: PiiConfig | boolean;\n}\n\n// ============================================================================\n// Session Types\n// ============================================================================\n\n/**\n * Parameters for creating a new session\n */\nexport interface CreateSessionParams {\n /** Name of the session */\n name: string;\n /** Identifier for the agent type (used for grouping) */\n agentName: string;\n /** External user ID (for grouping sessions by end-user) */\n userId: string;\n /** Optional parent session ID for multi-agent hierarchies */\n parentSessionId?: string;\n /** Optional conversation ID to group related sessions into a thread */\n convoId?: string;\n /** Optional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for completing a session\n */\nexport interface CompleteSessionParams {\n /** Session ID */\n sessionId: string;\n /** Whether the session successfully completed its goal (default: true) */\n success?: boolean;\n /** If success=false, why did it fail? */\n failureReason?: string;\n /** Total estimated cost in USD for this session */\n estimatedCost?: number;\n /** Custom metrics (e.g., { customer_satisfaction: 4.5, steps_taken: 3 }) */\n customMetrics?: Record<string, number>;\n /** Duration in milliseconds (auto-calculated if not provided) */\n durationMs?: number;\n /** Number of prompt/input tokens used */\n promptTokens?: number;\n /** Number of completion/output tokens used */\n completionTokens?: number;\n /** Total tokens used (prompt + completion) */\n totalTokens?: number;\n /** The user's original query/input for this session */\n userInput?: string;\n /** The final assistant response for this session */\n assistantOutput?: string;\n /** Alias for assistantOutput - the final output text */\n output?: string;\n}\n\n/**\n * Session data returned from the API\n */\nexport interface Session {\n id: string;\n agentId?: string;\n parentSessionId?: string;\n convoId?: string;\n name: string;\n agentName: string;\n userId: string;\n status: SessionStatus;\n totalEvents: number;\n\n // User interaction tracking\n userInput?: string;\n assistantOutput?: string;\n\n // Performance tracking\n success?: boolean;\n failureReason?: string;\n estimatedCost?: number;\n customMetrics?: Record<string, unknown>;\n score?: number;\n metricScores?: Record<string, unknown>;\n metricReasonings?: Record<string, string>;\n\n // Token tracking\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n\n // Timestamps\n durationMs?: number;\n metadata?: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n completedAt?: Date;\n}\n\n// ============================================================================\n// Event Types\n// ============================================================================\n\n/**\n * Parameters for tracking a tool call event\n */\nexport interface TrackToolCallParams {\n /** Session ID */\n sessionId: string;\n /** Name of the tool */\n toolName: string;\n /** Tool input data */\n toolInput: Record<string, unknown>;\n /** Tool output data */\n toolOutput: Record<string, unknown>;\n /** Optional reasoning for why this tool was called */\n reasoning?: string;\n /** Estimated cost in USD for this tool call */\n estimatedCost?: number;\n /** Error details if the tool failed */\n toolError?: Record<string, unknown>;\n /** Number of tokens used by this tool call (for LLM-based tools) */\n tokenCount?: number;\n /** OpenTelemetry trace ID for distributed tracing */\n traceId?: string;\n /** OpenTelemetry span ID for distributed tracing */\n spanId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for tracking an LLM decision event\n */\nexport interface TrackDecisionParams {\n /** Session ID */\n sessionId: string;\n /** Decision reasoning */\n reasoning: string;\n /** Alternative options considered */\n alternatives?: string[];\n /** Confidence score (0.0 to 1.0) */\n confidence?: number;\n /** Estimated cost in USD for this decision */\n estimatedCost?: number;\n /** Number of tokens used */\n tokenCount?: number;\n /** OpenTelemetry trace ID for distributed tracing */\n traceId?: string;\n /** OpenTelemetry span ID for distributed tracing */\n spanId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for tracking an error event\n */\nexport interface TrackErrorParams {\n /** Session ID */\n sessionId: string;\n /** Error message */\n errorMessage: string;\n /** Type of error (e.g., \"ValueError\", \"APIError\") */\n errorType?: string;\n /** Name of the tool that caused the error (if applicable) */\n toolName?: string;\n /** Stack trace for debugging */\n stackTrace?: string;\n /** OpenTelemetry trace ID for distributed tracing */\n traceId?: string;\n /** OpenTelemetry span ID for distributed tracing */\n spanId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Event data returned from the API\n */\nexport interface Event {\n id: string;\n sessionId: string;\n eventType: EventType;\n timestamp: Date;\n sequenceNumber: number;\n\n // Tool call data\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: Record<string, unknown>;\n toolError?: Record<string, unknown>;\n\n // LLM decision data\n reasoning?: string;\n alternativesConsidered?: string[];\n confidence?: number;\n\n // State tracking\n stateBefore?: Record<string, unknown>;\n stateAfter?: Record<string, unknown>;\n stateDiff?: Record<string, unknown>;\n\n // Performance tracking\n estimatedCost?: number;\n tokenCount?: number;\n\n // Metadata\n metadata?: Record<string, unknown>;\n traceId?: string;\n spanId?: string;\n}\n\n// ============================================================================\n// Interaction Types (Simple API)\n// ============================================================================\n\n/**\n * Parameters for beginning an interaction\n */\nexport interface BeginParams {\n /** External user ID for grouping sessions */\n userId: string;\n /** Event type/name (e.g., \"chat_message\", \"search_query\") */\n event: string;\n /** Optional input data for the interaction */\n input?: string;\n /** Optional custom event ID (auto-generated UUID if not provided) */\n eventId?: string;\n /** Optional conversation ID to group related interactions */\n convoId?: string;\n /** Optional additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for finishing an interaction\n */\nexport interface FinishParams {\n /** Output/response from the interaction */\n output?: string;\n /** Whether the interaction succeeded (default: true) */\n success?: boolean;\n /** Reason for failure if success=false */\n failureReason?: string;\n /** Total estimated cost in USD */\n estimatedCost?: number;\n /** Custom metrics */\n customMetrics?: Record<string, number>;\n /** Number of prompt/input tokens used */\n promptTokens?: number;\n /** Number of completion/output tokens used */\n completionTokens?: number;\n /** Total tokens used */\n totalTokens?: number;\n}\n\n// ============================================================================\n// Cost Calculation Types\n// ============================================================================\n\n/**\n * Parameters for cost calculation\n */\nexport interface CostParams {\n /** Model name (e.g., \"gpt-4\", \"claude-3-sonnet\") */\n model: string;\n /** Number of input tokens */\n inputTokens: number;\n /** Number of output tokens */\n outputTokens: number;\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\n/**\n * Standard API response wrapper\n */\nexport interface ApiResponse<T> {\n data?: T;\n error?: {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n };\n}\n","/**\n * Sentrial Client - Main SDK interface\n *\n * Captures agent sessions, tool calls, and metrics. This data powers:\n * - Signal detection (auto-detect patterns/anomalies)\n * - Root cause analysis (understand WHY agents fail)\n * - Code fixer (AI-suggested fixes with GitHub PRs)\n */\n\nimport { ApiError, NetworkError } from './errors.js';\nimport { redactPayload } from './redact.js';\nimport type { PiiConfig } from './redact.js';\nimport type {\n SentrialClientConfig,\n CreateSessionParams,\n CompleteSessionParams,\n TrackToolCallParams,\n TrackDecisionParams,\n TrackErrorParams,\n BeginParams,\n FinishParams,\n Session,\n Event,\n EventType,\n} from './types.js';\nimport { calculateOpenAICost, calculateAnthropicCost, calculateGoogleCost } from './cost.js';\n\n// Re-export EventType enum for convenience\nexport { EventType } from './types.js';\n\nconst DEFAULT_API_URL = 'https://api.sentrial.com';\n\n// Retry configuration (mirrors Python SDK)\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 500;\nconst MAX_BACKOFF_MS = 8000;\nconst BACKOFF_MULTIPLIER = 2;\nconst RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);\nconst REQUEST_TIMEOUT_MS = 10_000;\n\n/**\n * Sentrial Client for agent observability.\n *\n * Captures the data needed for automated root cause analysis and fix suggestions.\n *\n * @example\n * ```ts\n * const client = new SentrialClient({\n * apiKey: 'sentrial_live_xxx',\n * apiUrl: 'https://api.sentrial.com',\n * });\n *\n * // Create a session for an agent run\n * const sessionId = await client.createSession({\n * name: 'Support Request #123',\n * agentName: 'support-agent',\n * userId: 'user_123',\n * });\n *\n * // Track events\n * await client.trackToolCall({\n * sessionId,\n * toolName: 'search_kb',\n * toolInput: { query: 'password reset' },\n * toolOutput: { articles: ['KB-001'] },\n * });\n *\n * // Complete session with metrics\n * await client.completeSession({\n * sessionId,\n * success: true,\n * customMetrics: { customer_satisfaction: 90 },\n * });\n * ```\n *\n * @remarks\n * By default, the SDK fails silently (failSilently=true) to ensure\n * monitoring never crashes your application. Set failSilently=false\n * during development to see errors.\n */\nexport class SentrialClient {\n private readonly apiUrl: string;\n private readonly apiKey?: string;\n private readonly failSilently: boolean;\n private piiConfig?: PiiConfig;\n private piiConfigNeedsHydration: boolean = false;\n private piiHydrationPromise?: Promise<void>;\n private currentState: Record<string, unknown> = {};\n\n constructor(config: SentrialClientConfig = {}) {\n this.apiUrl = (\n config.apiUrl ??\n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_URL : undefined) ??\n DEFAULT_API_URL\n ).replace(/\\/$/, '');\n\n this.apiKey =\n config.apiKey ??\n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_KEY : undefined);\n\n this.failSilently = config.failSilently ?? true;\n\n if (config.pii === true) {\n // Minimal default until we fetch the real config from the server\n this.piiConfig = { enabled: true };\n this.piiConfigNeedsHydration = true;\n } else if (config.pii && typeof config.pii === 'object') {\n this.piiConfig = config.pii;\n this.piiConfigNeedsHydration = false;\n }\n }\n\n /**\n * Fetch the organization's PII config from the server.\n *\n * Called lazily on the first request when `pii: true` was passed to the constructor.\n * Uses a single shared promise so concurrent requests don't trigger duplicate fetches.\n */\n private async hydratePiiConfig(): Promise<void> {\n if (!this.piiConfigNeedsHydration) return;\n\n // Deduplicate: if a fetch is already in flight, reuse its promise\n if (this.piiHydrationPromise) {\n await this.piiHydrationPromise;\n return;\n }\n\n this.piiHydrationPromise = (async () => {\n try {\n const headers: Record<string, string> = {};\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(`${this.apiUrl}/api/sdk/pii-config`, {\n method: 'GET',\n headers,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (response.ok) {\n const data = (await response.json()) as {\n config: {\n enabled: boolean;\n mode: string;\n fields: string[];\n builtinPatterns: Record<string, boolean>;\n customPatterns: Array<{ pattern: string; label: string }>;\n enhancedDetection: boolean;\n } | null;\n };\n if (data.config) {\n this.piiConfig = {\n enabled: data.config.enabled,\n mode: data.config.mode as PiiConfig['mode'],\n fields: data.config.fields as PiiConfig['fields'],\n builtinPatterns: data.config.builtinPatterns as PiiConfig['builtinPatterns'],\n customPatterns: (data.config.customPatterns || []).map(\n (cp: { pattern: string; label: string }) => ({\n pattern: new RegExp(cp.pattern, 'g'),\n label: cp.label,\n })\n ),\n enhancedDetection: data.config.enhancedDetection,\n };\n }\n }\n } catch {\n // Fail silently — keep the minimal default config (enabled: true)\n }\n this.piiConfigNeedsHydration = false;\n })();\n\n await this.piiHydrationPromise;\n }\n\n /**\n * Make an HTTP request with retry logic and exponential backoff.\n *\n * Retries on transient failures (network errors, timeouts, 429/5xx).\n * Up to MAX_RETRIES attempts with exponential backoff.\n */\n private async safeRequest<T>(\n method: string,\n url: string,\n body?: unknown\n ): Promise<T | null> {\n // Hydrate server-driven PII config before first request\n if (this.piiConfigNeedsHydration) {\n await this.hydratePiiConfig();\n }\n\n let lastError: Error | undefined;\n let backoff = INITIAL_BACKOFF_MS;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n // Apply PII redaction before serialization\n const finalBody =\n this.piiConfig && body && typeof body === 'object'\n ? redactPayload(body as Record<string, unknown>, this.piiConfig)\n : body;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: finalBody ? JSON.stringify(finalBody) : undefined,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n // Retryable HTTP status — back off and retry\n if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < MAX_RETRIES) {\n await this.sleep(backoff);\n backoff = Math.min(backoff * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n continue;\n }\n\n if (!response.ok) {\n const errorBody = await response.text();\n let errorData: { error?: { message?: string; code?: string } } = {};\n try {\n errorData = JSON.parse(errorBody);\n } catch {\n // Ignore JSON parse errors\n }\n\n const error = new ApiError(\n errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n errorData.error?.code\n );\n\n if (this.failSilently) {\n console.warn(`Sentrial: Request failed (${method} ${url}):`, error.message);\n return null;\n }\n throw error;\n }\n\n return (await response.json()) as T;\n } catch (error) {\n // Non-retryable Sentrial errors — throw immediately\n if (error instanceof ApiError) {\n throw error;\n }\n\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Network / timeout errors are retryable\n if (attempt < MAX_RETRIES) {\n await this.sleep(backoff);\n backoff = Math.min(backoff * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n continue;\n }\n }\n }\n\n // All retries exhausted\n const networkError = new NetworkError(\n lastError?.message ?? 'Unknown network error',\n lastError\n );\n\n if (this.failSilently) {\n console.warn(`Sentrial: Request failed after ${MAX_RETRIES + 1} attempts (${method} ${url}):`, networkError.message);\n return null;\n }\n throw networkError;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Create a new session\n *\n * @param params - Session creation parameters\n * @returns Session ID, or null if the request failed and failSilently is true\n */\n async createSession(params: CreateSessionParams): Promise<string | null> {\n // Clear state from previous sessions to prevent memory leaks\n this.currentState = {};\n\n const payload: Record<string, unknown> = {\n name: params.name,\n agentName: params.agentName,\n userId: params.userId,\n metadata: params.metadata,\n };\n\n if (params.parentSessionId) {\n payload.parentSessionId = params.parentSessionId;\n }\n\n if (params.convoId) {\n payload.convoId = params.convoId;\n }\n\n const response = await this.safeRequest<{ id: string }>(\n 'POST',\n `${this.apiUrl}/api/sdk/sessions`,\n payload\n );\n\n return response?.id ?? null;\n }\n\n /**\n * Track a tool call event\n *\n * @param params - Tool call parameters\n * @returns Event data\n */\n async trackToolCall(params: TrackToolCallParams): Promise<Event | null> {\n const stateBefore = { ...this.currentState };\n\n // Update current state\n this.currentState[`${params.toolName}_result`] = params.toolOutput;\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: 'tool_call' as EventType,\n toolName: params.toolName,\n toolInput: params.toolInput,\n toolOutput: params.toolOutput,\n reasoning: params.reasoning,\n stateBefore,\n stateAfter: { ...this.currentState },\n estimatedCost: params.estimatedCost ?? 0,\n };\n\n if (params.toolError !== undefined) payload.toolError = params.toolError;\n if (params.tokenCount !== undefined) payload.tokenCount = params.tokenCount;\n if (params.traceId !== undefined) payload.traceId = params.traceId;\n if (params.spanId !== undefined) payload.spanId = params.spanId;\n if (params.metadata !== undefined) payload.metadata = params.metadata;\n\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Track an LLM decision event\n *\n * @param params - Decision parameters\n * @returns Event data\n */\n async trackDecision(params: TrackDecisionParams): Promise<Event | null> {\n const stateBefore = { ...this.currentState };\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: 'llm_decision' as EventType,\n reasoning: params.reasoning,\n alternativesConsidered: params.alternatives,\n confidence: params.confidence,\n stateBefore,\n stateAfter: { ...this.currentState },\n estimatedCost: params.estimatedCost ?? 0,\n };\n\n if (params.tokenCount !== undefined) payload.tokenCount = params.tokenCount;\n if (params.traceId !== undefined) payload.traceId = params.traceId;\n if (params.spanId !== undefined) payload.spanId = params.spanId;\n if (params.metadata !== undefined) payload.metadata = params.metadata;\n\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Track an error event\n *\n * @param params - Error parameters\n * @returns Event data\n */\n async trackError(params: TrackErrorParams): Promise<Event | null> {\n const stateBefore = { ...this.currentState };\n\n const toolError: Record<string, unknown> = {\n message: params.errorMessage,\n };\n if (params.errorType) toolError.type = params.errorType;\n if (params.stackTrace) toolError.stack_trace = params.stackTrace;\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: 'error' as EventType,\n toolError,\n stateBefore,\n stateAfter: { ...this.currentState },\n };\n\n if (params.toolName !== undefined) payload.toolName = params.toolName;\n if (params.traceId !== undefined) payload.traceId = params.traceId;\n if (params.spanId !== undefined) payload.spanId = params.spanId;\n if (params.metadata !== undefined) payload.metadata = params.metadata;\n\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Update the current state\n *\n * @param key - State key\n * @param value - State value\n */\n updateState(key: string, value: unknown): void {\n this.currentState[key] = value;\n }\n\n /**\n * Set the user input for a session\n *\n * @param sessionId - Session ID\n * @param input - User input text\n * @returns Updated session or null on error\n */\n async setInput(sessionId: string, input: string): Promise<Session | null> {\n return this.safeRequest<Session>(\n 'PATCH',\n `${this.apiUrl}/api/sdk/sessions/${sessionId}`,\n { userInput: input }\n );\n }\n\n /**\n * Track a generic event\n *\n * @param params - Event parameters\n * @returns Event data or null on error\n */\n async trackEvent(params: {\n sessionId: string;\n eventType: string;\n eventData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n }): Promise<Event | null> {\n const stateBefore = { ...this.currentState };\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: params.eventType,\n stateBefore,\n stateAfter: { ...this.currentState },\n };\n\n if (params.eventData) {\n Object.assign(payload, params.eventData);\n }\n if (params.metadata) {\n payload.metadata = params.metadata;\n }\n\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Complete a session with performance metrics\n *\n * This is the recommended way to close sessions for performance monitoring.\n *\n * @param params - Session completion parameters\n * @returns Updated session data\n *\n * @example\n * ```ts\n * await client.completeSession({\n * sessionId,\n * success: true,\n * estimatedCost: 0.023,\n * promptTokens: 1500,\n * completionTokens: 500,\n * totalTokens: 2000,\n * userInput: \"What's the weather in San Francisco?\",\n * assistantOutput: \"The weather in San Francisco is 65°F and sunny.\",\n * customMetrics: {\n * customer_satisfaction: 4.5,\n * order_value: 129.99,\n * },\n * });\n * ```\n */\n async completeSession(params: CompleteSessionParams): Promise<Session | null> {\n const payload: Record<string, unknown> = {\n status: params.success !== false ? 'completed' : 'failed',\n success: params.success ?? true,\n };\n\n if (params.failureReason !== undefined) payload.failureReason = params.failureReason;\n if (params.estimatedCost !== undefined) payload.estimatedCost = params.estimatedCost;\n if (params.customMetrics !== undefined) payload.customMetrics = params.customMetrics;\n if (params.durationMs !== undefined) payload.durationMs = params.durationMs;\n if (params.promptTokens !== undefined) payload.promptTokens = params.promptTokens;\n if (params.completionTokens !== undefined) payload.completionTokens = params.completionTokens;\n if (params.totalTokens !== undefined) payload.totalTokens = params.totalTokens;\n if (params.userInput !== undefined) payload.userInput = params.userInput;\n // Support both 'assistantOutput' and 'output' (alias)\n const output = params.assistantOutput ?? params.output;\n if (output !== undefined) payload.assistantOutput = output;\n\n return this.safeRequest<Session>(\n 'PATCH',\n `${this.apiUrl}/api/sdk/sessions/${params.sessionId}`,\n payload\n );\n }\n\n /**\n * Begin tracking an interaction (simplified API)\n *\n * @param params - Interaction parameters\n * @returns Interaction object with finish() method\n *\n * @example\n * ```ts\n * const interaction = await client.begin({\n * userId: 'user_123',\n * event: 'chat_message',\n * input: message,\n * convoId: 'convo_456',\n * });\n *\n * // ... do your agent work ...\n *\n * await interaction.finish({ output: responseText });\n * ```\n */\n async begin(params: BeginParams): Promise<Interaction> {\n const eventId = params.eventId ?? crypto.randomUUID();\n\n // Build metadata with optional fields\n const fullMetadata: Record<string, unknown> = params.metadata ? { ...params.metadata } : {};\n if (params.input) fullMetadata.input = params.input;\n if (params.convoId) fullMetadata.convo_id = params.convoId;\n fullMetadata.event_id = eventId;\n\n // Create session first (clears old state)\n const sessionId = await this.createSession({\n name: `${params.event}:${eventId.slice(0, 8)}`,\n agentName: params.event,\n userId: params.userId,\n convoId: params.convoId,\n metadata: fullMetadata,\n });\n\n // Store input in current_state AFTER createSession clears old state\n if (params.input) {\n this.currentState.input = params.input;\n }\n\n return new Interaction({\n client: this,\n sessionId,\n eventId,\n userId: params.userId,\n event: params.event,\n userInput: params.input,\n });\n }\n\n // Cost calculation static methods for convenience\n static calculateOpenAICost = calculateOpenAICost;\n static calculateAnthropicCost = calculateAnthropicCost;\n static calculateGoogleCost = calculateGoogleCost;\n}\n\n// ============================================================================\n// Interaction Class\n// ============================================================================\n\ninterface InteractionConfig {\n client: SentrialClient;\n sessionId: string | null;\n eventId: string;\n userId: string;\n event: string;\n userInput?: string;\n}\n\n/**\n * Represents an in-progress interaction that can be finished.\n *\n * Created by SentrialClient.begin() - provides a clean begin/finish API pattern.\n *\n * @remarks\n * If session creation failed (sessionId is null), all tracking methods\n * become no-ops to ensure your application continues running.\n */\nexport class Interaction {\n private readonly client: SentrialClient;\n private readonly sessionId: string | null;\n /** Event ID for this interaction */\n public readonly eventId: string;\n /** User ID for this interaction */\n public readonly userId: string;\n /** Event name for this interaction */\n public readonly event: string;\n private finished = false;\n private success = true;\n private failureReason?: string;\n private output?: string;\n private readonly userInput?: string;\n private readonly degraded: boolean;\n\n constructor(config: InteractionConfig) {\n this.client = config.client;\n this.sessionId = config.sessionId;\n this.eventId = config.eventId;\n this.userId = config.userId;\n this.event = config.event;\n this.userInput = config.userInput;\n this.degraded = config.sessionId === null;\n }\n\n /**\n * Set the output for this interaction\n *\n * This will be used when finish() is called.\n * Useful when you want to set the output but call finish() later.\n *\n * @param output - The output/response text\n */\n setOutput(output: string): void {\n this.output = output;\n }\n\n /**\n * Finish the interaction and record metrics\n *\n * @param params - Finish parameters\n * @returns Updated session data, or null if degraded/already finished\n *\n * @example\n * ```ts\n * await interaction.finish({\n * output: \"Here's the answer to your question...\",\n * success: true,\n * customMetrics: { satisfaction: 4.5 },\n * });\n * ```\n */\n async finish(params: FinishParams = {}): Promise<Session | null> {\n if (this.finished) return null;\n if (this.degraded) {\n this.finished = true;\n return null;\n }\n\n this.finished = true;\n\n // Use stored output if not provided\n const finalOutput = params.output ?? this.output;\n\n return this.client.completeSession({\n sessionId: this.sessionId!,\n success: params.success ?? this.success,\n failureReason: params.failureReason ?? this.failureReason,\n estimatedCost: params.estimatedCost,\n customMetrics: params.customMetrics,\n promptTokens: params.promptTokens,\n completionTokens: params.completionTokens,\n totalTokens: params.totalTokens,\n userInput: this.userInput,\n assistantOutput: finalOutput,\n });\n }\n\n /**\n * Track a tool call within this interaction\n */\n async trackToolCall(\n params: Omit<TrackToolCallParams, 'sessionId'>\n ): Promise<Event | null> {\n if (this.degraded) return null;\n\n return this.client.trackToolCall({\n ...params,\n sessionId: this.sessionId!,\n });\n }\n\n /**\n * Track an LLM decision within this interaction\n */\n async trackDecision(\n params: Omit<TrackDecisionParams, 'sessionId'>\n ): Promise<Event | null> {\n if (this.degraded) return null;\n\n return this.client.trackDecision({\n ...params,\n sessionId: this.sessionId!,\n });\n }\n\n /**\n * Track an error within this interaction\n */\n async trackError(\n params: Omit<TrackErrorParams, 'sessionId'>\n ): Promise<Event | null> {\n // Store failure info\n this.success = false;\n this.failureReason = params.errorMessage;\n\n if (this.degraded) return null;\n\n return this.client.trackError({\n ...params,\n sessionId: this.sessionId!,\n });\n }\n\n /**\n * Get the session ID (null if session creation failed)\n */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Check if the interaction is in a degraded state (session creation failed)\n */\n isDegraded(): boolean {\n return this.degraded;\n }\n}\n\n// ============================================================================\n// Module-level Simple API\n// ============================================================================\n\nlet defaultClient: SentrialClient | null = null;\n\nfunction getClient(): SentrialClient {\n if (!defaultClient) {\n defaultClient = new SentrialClient();\n }\n return defaultClient;\n}\n\n/**\n * Configure the default Sentrial client\n *\n * @param config - Client configuration\n *\n * @example\n * ```ts\n * import { sentrial } from '@sentrial/sdk';\n *\n * sentrial.configure({\n * apiKey: 'sentrial_live_xxx',\n * apiUrl: 'https://api.sentrial.com',\n * });\n * ```\n */\nexport function configure(config: SentrialClientConfig): void {\n defaultClient = new SentrialClient(config);\n}\n\n/**\n * Begin tracking an interaction (module-level convenience function)\n *\n * This is a shorthand for new SentrialClient().begin(...).\n * Configure the client first with sentrial.configure() or set SENTRIAL_API_KEY env var.\n *\n * @param params - Interaction parameters\n * @returns Interaction object with finish() method\n *\n * @example\n * ```ts\n * import { sentrial } from '@sentrial/sdk';\n *\n * sentrial.configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const interaction = await sentrial.begin({\n * userId: 'user_123',\n * event: 'chat_message',\n * input: message,\n * });\n *\n * // ... do your agent work ...\n *\n * await interaction.finish({ output: responseText });\n * ```\n */\nexport function begin(params: BeginParams): Promise<Interaction> {\n return getClient().begin(params);\n}\n\n/**\n * Simple API namespace for module-level usage\n */\nexport const sentrial = {\n configure,\n begin,\n};\n","/**\n * Vercel AI SDK Integration for Sentrial\n *\n * Automatically traces AI SDK calls with full input/output logging, metrics, and tool execution.\n * Supports Vercel AI SDK v3, v4, v5, and v6.\n *\n * @example Simple Usage\n * ```ts\n * import { configureVercel, wrapAISDK } from '@sentrial/sdk';\n * import * as ai from 'ai';\n * import { openai } from '@ai-sdk/openai';\n *\n * configureVercel({\n * apiKey: process.env.SENTRIAL_API_KEY,\n * defaultAgent: 'my-ai-agent',\n * });\n *\n * const { generateText } = wrapAISDK(ai);\n *\n * // This automatically logs to Sentrial\n * const { text } = await generateText({\n * model: openai('gpt-4'),\n * prompt: 'What is the capital of France?',\n * });\n * ```\n *\n * @example Multi-Turn Conversations\n * ```ts\n * const { generateText } = wrapAISDK(ai, { convoId: 'convo_123' });\n *\n * // All calls are linked to the same conversation thread\n * const { text } = await generateText({\n * model: openai('gpt-4o'),\n * messages: conversationHistory,\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { SentrialClient } from './client.js';\nimport { calculateOpenAICost, calculateAnthropicCost, calculateGoogleCost } from './cost.js';\n\n// ============================================================================\n// Types for AI SDK (we don't import to keep it as peer dep)\n// ============================================================================\n\ntype AIModule = {\n generateText?: Function;\n streamText?: Function;\n generateObject?: Function;\n streamObject?: Function;\n experimental_generateText?: Function;\n experimental_streamText?: Function;\n};\n\ntype GenerateTextParams = {\n model: { modelId?: string; provider?: string } & Record<string, unknown>;\n prompt?: string;\n messages?: Array<{ role: string; content: string | unknown }>;\n system?: string;\n tools?: Record<string, { execute?: Function } & Record<string, unknown>>;\n maxTokens?: number;\n maxSteps?: number;\n temperature?: number;\n [key: string]: unknown;\n};\n\ntype UsageInfo = {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n};\n\ntype StepResult = {\n text?: string;\n toolCalls?: Array<{ toolName: string; args: unknown }>;\n toolResults?: Array<{ toolName: string; result: unknown }>;\n usage?: UsageInfo;\n finishReason?: string;\n};\n\ntype GenerateTextResult = {\n text: string;\n usage?: UsageInfo;\n finishReason?: string;\n toolCalls?: Array<{ toolName: string; args: unknown }>;\n toolResults?: Array<{ toolName: string; result: unknown }>;\n response?: { modelId?: string; id?: string };\n steps?: StepResult[];\n reasoning?: unknown;\n reasoningText?: string;\n sources?: unknown[];\n [key: string]: unknown;\n};\n\ntype StreamTextResult = {\n textStream: AsyncIterable<string>;\n fullStream?: AsyncIterable<unknown>;\n text?: string | Promise<string>;\n usage?: Promise<UsageInfo>;\n finishReason?: Promise<string>;\n toolCalls?: Promise<Array<{ toolName: string; args: unknown }>>;\n toolResults?: Promise<Array<{ toolName: string; result: unknown }>>;\n steps?: Promise<StepResult[]>;\n response?: Promise<{ modelId?: string; id?: string }>;\n [key: string]: unknown;\n};\n\n/** Per-instance config passed through closures (not global) */\ntype WrapperConfig = {\n defaultAgent?: string;\n userId?: string;\n convoId?: string;\n};\n\n// ============================================================================\n// Global Configuration (used only by configureVercel)\n// ============================================================================\n\nlet _defaultClient: SentrialClient | null = null;\nlet _globalConfig: WrapperConfig = {};\n\n/**\n * Configure the Sentrial SDK for Vercel AI SDK integration.\n *\n * @example\n * ```ts\n * import { configureVercel } from '@sentrial/sdk';\n *\n * configureVercel({\n * apiKey: process.env.SENTRIAL_API_KEY,\n * defaultAgent: 'my-chatbot',\n * userId: 'user_123',\n * });\n * ```\n */\nexport function configureVercel(config: {\n apiKey?: string;\n apiUrl?: string;\n defaultAgent?: string;\n userId?: string;\n convoId?: string;\n failSilently?: boolean;\n}): void {\n _defaultClient = new SentrialClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n failSilently: config.failSilently ?? true,\n });\n _globalConfig = {\n defaultAgent: config.defaultAgent,\n userId: config.userId,\n convoId: config.convoId,\n };\n}\n\nfunction getClient(): SentrialClient {\n if (!_defaultClient) {\n _defaultClient = new SentrialClient();\n }\n return _defaultClient;\n}\n\n// ============================================================================\n// Model & Provider Utilities\n// ============================================================================\n\nfunction extractModelInfo(model: GenerateTextParams['model']): {\n modelId: string;\n provider: string;\n} {\n const modelId =\n model.modelId || (model as Record<string, unknown>).id as string || 'unknown';\n const provider = model.provider || guessProvider(modelId);\n return { modelId, provider };\n}\n\nfunction guessProvider(modelId: string): string {\n const id = modelId.toLowerCase();\n if (id.includes('gpt') || id.includes('o1') || id.includes('o3') || id.includes('o4')\n || id.startsWith('chatgpt')) return 'openai';\n if (id.includes('claude')) return 'anthropic';\n if (id.includes('gemini')) return 'google';\n if (id.includes('mistral') || id.includes('mixtral') || id.includes('codestral')\n || id.includes('pixtral')) return 'mistral';\n if (id.includes('llama')) return 'meta';\n if (id.includes('deepseek')) return 'deepseek';\n if (id.includes('command')) return 'cohere';\n if (id.includes('qwen')) return 'alibaba';\n return 'unknown';\n}\n\nfunction calculateCostForCall(\n provider: string,\n modelId: string,\n promptTokens: number,\n completionTokens: number\n): number {\n const params = { model: modelId, inputTokens: promptTokens, outputTokens: completionTokens };\n switch (provider.toLowerCase()) {\n case 'openai':\n return calculateOpenAICost(params);\n case 'anthropic':\n return calculateAnthropicCost(params);\n case 'google':\n return calculateGoogleCost(params);\n case 'deepseek':\n return (promptTokens / 1_000_000) * 0.27 + (completionTokens / 1_000_000) * 1.10;\n case 'cohere':\n return (promptTokens / 1_000_000) * 0.50 + (completionTokens / 1_000_000) * 1.50;\n case 'mistral':\n return (promptTokens / 1_000_000) * 2.00 + (completionTokens / 1_000_000) * 6.00;\n default:\n return (promptTokens * 0.000003) + (completionTokens * 0.000006);\n }\n}\n\nfunction extractInput(params: GenerateTextParams): string {\n if (params.prompt) return params.prompt;\n if (params.messages && params.messages.length > 0) {\n const lastUserMessage = [...params.messages].reverse().find(m => m.role === 'user');\n if (lastUserMessage) {\n return typeof lastUserMessage.content === 'string'\n ? lastUserMessage.content\n : JSON.stringify(lastUserMessage.content);\n }\n return JSON.stringify(params.messages);\n }\n return '';\n}\n\n// ============================================================================\n// Tool Wrapping\n// ============================================================================\n\nfunction wrapTools(\n tools: Record<string, { execute?: Function } & Record<string, unknown>> | undefined,\n sessionId: string,\n client: SentrialClient\n): Record<string, { execute?: Function } & Record<string, unknown>> | undefined {\n if (!tools) return undefined;\n\n const wrappedTools: Record<string, { execute?: Function } & Record<string, unknown>> = {};\n\n for (const [toolName, tool] of Object.entries(tools)) {\n if (typeof tool.execute === 'function') {\n const originalExecute = tool.execute;\n wrappedTools[toolName] = {\n ...tool,\n execute: async (...args: unknown[]) => {\n const startTime = Date.now();\n try {\n const result = await originalExecute(...args);\n const durationMs = Date.now() - startTime;\n\n // Fire-and-forget: don't let tracking delay tool result\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: result as Record<string, unknown>,\n reasoning: `Tool executed in ${durationMs}ms`,\n }).catch(() => {});\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: {},\n toolError: { message: error instanceof Error ? error.message : 'Unknown error' },\n reasoning: `Tool failed after ${durationMs}ms`,\n }).catch(() => {});\n\n throw error;\n }\n },\n };\n } else {\n wrappedTools[toolName] = tool;\n }\n }\n\n return wrappedTools;\n}\n\nfunction wrapToolsAsync(\n tools: Record<string, { execute?: Function } & Record<string, unknown>>,\n sessionPromise: Promise<string | null>,\n client: SentrialClient\n): Record<string, { execute?: Function } & Record<string, unknown>> {\n const wrappedTools: Record<string, { execute?: Function } & Record<string, unknown>> = {};\n\n for (const [toolName, tool] of Object.entries(tools)) {\n if (typeof tool.execute === 'function') {\n const originalExecute = tool.execute;\n wrappedTools[toolName] = {\n ...tool,\n execute: async (...args: unknown[]) => {\n const startTime = Date.now();\n const sid = await sessionPromise;\n\n try {\n const result = await originalExecute(...args);\n const durationMs = Date.now() - startTime;\n\n if (sid) {\n client.trackToolCall({\n sessionId: sid,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: result as Record<string, unknown>,\n reasoning: `Tool executed in ${durationMs}ms`,\n }).catch(() => {});\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (sid) {\n client.trackToolCall({\n sessionId: sid,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: {},\n toolError: { message: error instanceof Error ? error.message : 'Unknown error' },\n reasoning: `Tool failed after ${durationMs}ms`,\n }).catch(() => {});\n }\n\n throw error;\n }\n },\n };\n } else {\n wrappedTools[toolName] = tool;\n }\n }\n\n return wrappedTools;\n}\n\n// ============================================================================\n// generateText Wrapper\n// ============================================================================\n\nfunction wrapGenerateText(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => Promise<GenerateTextResult> {\n return async (params: GenerateTextParams): Promise<GenerateTextResult> => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n const sessionId = await client.createSession({\n name: `generateText: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'generateText',\n ...(params.maxSteps ? { maxSteps: params.maxSteps } : {}),\n },\n });\n\n if (!sessionId) {\n return originalFn(params);\n }\n\n await client.setInput(sessionId, input);\n\n const wrappedParams = {\n ...params,\n tools: wrapTools(params.tools, sessionId, client),\n };\n\n try {\n const result: GenerateTextResult = await originalFn(wrappedParams);\n const durationMs = Date.now() - startTime;\n\n const resolvedModelId = result.response?.modelId || modelId;\n\n const promptTokens = result.usage?.promptTokens || 0;\n const completionTokens = result.usage?.completionTokens || 0;\n const totalTokens = result.usage?.totalTokens || promptTokens + completionTokens;\n const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);\n\n // Track multi-step results (v4+ with maxSteps)\n const steps = result.steps;\n if (steps && steps.length >= 1) {\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i]!;\n await client.trackEvent({\n sessionId,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n step: i + 1,\n total_steps: steps.length,\n prompt_tokens: step.usage?.promptTokens || 0,\n completion_tokens: step.usage?.completionTokens || 0,\n total_tokens: step.usage?.totalTokens || 0,\n finish_reason: step.finishReason,\n tool_calls: step.toolCalls?.map(tc => tc.toolName),\n },\n });\n }\n } else {\n await client.trackEvent({\n sessionId,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: totalTokens,\n finish_reason: result.finishReason,\n tool_calls: result.toolCalls?.map(tc => tc.toolName),\n },\n });\n }\n\n await client.completeSession({\n sessionId,\n success: true,\n output: result.text,\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n });\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n await client.trackError({\n sessionId,\n errorType: error instanceof Error ? error.name : 'Error',\n errorMessage: error instanceof Error ? error.message : 'Unknown error',\n });\n\n await client.completeSession({\n sessionId,\n success: false,\n failureReason: error instanceof Error ? error.message : 'Unknown error',\n durationMs,\n });\n\n throw error;\n }\n };\n}\n\n// ============================================================================\n// streamText Wrapper\n// ============================================================================\n\nfunction wrapStreamText(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => StreamTextResult {\n return (params: GenerateTextParams): StreamTextResult => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n let sessionId: string | null = null;\n const sessionPromise = (async () => {\n try {\n const id = await client.createSession({\n name: `streamText: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'streamText',\n },\n });\n sessionId = id;\n if (id) {\n client.setInput(id, input).catch(() => {});\n }\n return id;\n } catch {\n return null;\n }\n })();\n\n const wrappedParams = {\n ...params,\n tools: params.tools ? wrapToolsAsync(params.tools, sessionPromise, client) : undefined,\n };\n\n const result = originalFn(wrappedParams) as StreamTextResult;\n\n let tracked = false;\n\n async function trackCompletion(fullText: string, error?: Error) {\n if (tracked) return;\n tracked = true;\n\n const durationMs = Date.now() - startTime;\n const sid = sessionId || await sessionPromise;\n if (!sid) return;\n\n if (error) {\n await client.trackError({\n sessionId: sid,\n errorType: error.name || 'Error',\n errorMessage: error.message || 'Unknown error',\n });\n\n await client.completeSession({\n sessionId: sid,\n success: false,\n failureReason: error.message || 'Unknown error',\n durationMs,\n });\n return;\n }\n\n let usage: UsageInfo | undefined;\n try {\n usage = result.usage ? await result.usage : undefined;\n } catch {\n // Usage promise may reject if stream errors\n }\n\n const promptTokens = usage?.promptTokens || 0;\n const completionTokens = usage?.completionTokens || 0;\n const totalTokens = usage?.totalTokens || promptTokens + completionTokens;\n const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);\n\n await client.trackEvent({\n sessionId: sid,\n eventType: 'llm_call',\n eventData: {\n model: modelId,\n provider,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: totalTokens,\n },\n });\n\n await client.completeSession({\n sessionId: sid,\n success: true,\n output: fullText,\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n });\n }\n\n // Modern approach (v4+): use the `text` promise\n const textProp = result.text;\n if (typeof textProp === 'string') {\n // v3 edge case: text is already a resolved string\n trackCompletion(textProp).catch(() => {});\n } else if (textProp != null && typeof (textProp as Promise<string>).then === 'function') {\n // v4+: text is a promise — wrap it to ensure rejection is always handled\n const originalTextPromise = textProp as Promise<string>;\n result.text = originalTextPromise\n .then((text) => {\n trackCompletion(text).catch(() => {});\n return text;\n })\n .catch((err) => {\n trackCompletion('', err instanceof Error ? err : new Error(String(err))).catch(() => {});\n throw err;\n });\n } else {\n // Legacy fallback (v3): wrap textStream\n const originalTextStream = result.textStream;\n let fullText = '';\n\n result.textStream = (async function* () {\n try {\n for await (const chunk of originalTextStream) {\n fullText += chunk;\n yield chunk;\n }\n await trackCompletion(fullText);\n } catch (error) {\n await trackCompletion(\n '',\n error instanceof Error ? error : new Error(String(error))\n );\n throw error;\n }\n })();\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// generateObject Wrapper\n// ============================================================================\n\nfunction wrapGenerateObject(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => Promise<unknown> {\n return async (params: GenerateTextParams): Promise<unknown> => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n const sessionId = await client.createSession({\n name: `generateObject: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'generateObject',\n },\n });\n\n if (!sessionId) {\n return originalFn(params);\n }\n\n await client.setInput(sessionId, input);\n\n try {\n const result = await originalFn(params) as {\n object?: unknown;\n usage?: UsageInfo;\n response?: { modelId?: string };\n };\n const durationMs = Date.now() - startTime;\n\n const resolvedModelId = result.response?.modelId || modelId;\n const promptTokens = result.usage?.promptTokens || 0;\n const completionTokens = result.usage?.completionTokens || 0;\n const totalTokens = result.usage?.totalTokens || promptTokens + completionTokens;\n const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);\n\n await client.completeSession({\n sessionId,\n success: true,\n output: JSON.stringify(result.object),\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n });\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n await client.trackError({\n sessionId,\n errorType: error instanceof Error ? error.name : 'Error',\n errorMessage: error instanceof Error ? error.message : 'Unknown error',\n });\n\n await client.completeSession({\n sessionId,\n success: false,\n failureReason: error instanceof Error ? error.message : 'Unknown error',\n durationMs,\n });\n\n throw error;\n }\n };\n}\n\n// ============================================================================\n// streamObject Wrapper\n// ============================================================================\n\nfunction wrapStreamObject(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => unknown {\n return (params: GenerateTextParams): unknown => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n const sessionPromise = (async () => {\n try {\n const id = await client.createSession({\n name: `streamObject: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'streamObject',\n },\n });\n if (id) {\n client.setInput(id, input).catch(() => {});\n }\n return id;\n } catch {\n return null;\n }\n })();\n\n const result = originalFn(params) as {\n object?: Promise<unknown>;\n usage?: Promise<UsageInfo>;\n };\n\n if (result.object) {\n const originalObjectPromise = result.object;\n result.object = originalObjectPromise.then(async (obj) => {\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n\n if (sid) {\n let usage: UsageInfo | undefined;\n try {\n usage = result.usage ? await result.usage : undefined;\n } catch {\n // Ignore\n }\n\n const promptTokens = usage?.promptTokens || 0;\n const completionTokens = usage?.completionTokens || 0;\n const totalTokens = usage?.totalTokens || promptTokens + completionTokens;\n const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);\n\n await client.completeSession({\n sessionId: sid,\n success: true,\n output: JSON.stringify(obj),\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n });\n }\n\n return obj;\n }).catch(async (error) => {\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n\n if (sid) {\n await client.trackError({\n sessionId: sid,\n errorType: error instanceof Error ? error.name : 'Error',\n errorMessage: error instanceof Error ? error.message : 'Unknown error',\n });\n\n await client.completeSession({\n sessionId: sid,\n success: false,\n failureReason: error instanceof Error ? error.message : 'Unknown error',\n durationMs,\n });\n }\n\n throw error;\n });\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Wrap the Vercel AI SDK to automatically trace all AI calls.\n *\n * @param ai - The imported `ai` module from the Vercel AI SDK\n * @param options - Optional configuration (client, defaultAgent, userId, convoId)\n * @returns Wrapped versions of the AI SDK functions\n *\n * @example Using with configureVercel\n * ```ts\n * configureVercel({ apiKey: process.env.SENTRIAL_API_KEY, defaultAgent: 'my-agent' });\n * const { generateText, streamText } = wrapAISDK(ai);\n * ```\n *\n * @example Multi-turn conversations\n * ```ts\n * const { generateText } = wrapAISDK(ai, { convoId: 'convo_123' });\n * ```\n *\n * @example Using with existing SentrialClient\n * ```ts\n * const client = new SentrialClient({ apiKey: process.env.SENTRIAL_API_KEY });\n * const { generateText } = wrapAISDK(ai, { client, defaultAgent: 'my-agent' });\n * ```\n */\nexport function wrapAISDK(\n ai: AIModule,\n options?: {\n client?: SentrialClient;\n defaultAgent?: string;\n userId?: string;\n convoId?: string;\n }\n): {\n generateText: ReturnType<typeof wrapGenerateText>;\n streamText: ReturnType<typeof wrapStreamText>;\n generateObject: ReturnType<typeof wrapGenerateObject>;\n streamObject: ReturnType<typeof wrapStreamObject>;\n} {\n const client = options?.client ?? getClient();\n\n // Create per-instance config — never mutate global state\n const config: WrapperConfig = {\n defaultAgent: options?.defaultAgent ?? _globalConfig.defaultAgent,\n userId: options?.userId ?? _globalConfig.userId,\n convoId: options?.convoId ?? _globalConfig.convoId,\n };\n\n return {\n generateText: ai.generateText\n ? wrapGenerateText(ai.generateText, client, config)\n : wrapGenerateText(\n () => Promise.reject(new Error('generateText not available')),\n client,\n config\n ),\n streamText: ai.streamText\n ? wrapStreamText(ai.streamText, client, config)\n : wrapStreamText(() => ({ textStream: (async function* () {})() }), client, config),\n generateObject: ai.generateObject\n ? wrapGenerateObject(ai.generateObject, client, config)\n : wrapGenerateObject(\n () => Promise.reject(new Error('generateObject not available')),\n client,\n config\n ),\n streamObject: ai.streamObject\n ? wrapStreamObject(ai.streamObject, client, config)\n : wrapStreamObject(() => ({}), client, config),\n };\n}\n\n// Export types for consumers\nexport type { GenerateTextParams, GenerateTextResult, StreamTextResult };\n","/**\n * Sentrial LLM Wrappers - Auto-instrument LLM provider SDKs\n *\n * These wrappers automatically track all LLM calls with:\n * - Input messages\n * - Output responses\n * - Token counts\n * - Cost estimation\n * - Latency\n *\n * @example OpenAI\n * ```ts\n * import OpenAI from 'openai';\n * import { wrapOpenAI, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const openai = wrapOpenAI(new OpenAI());\n *\n * // All calls are now automatically tracked!\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n *\n * @example Anthropic\n * ```ts\n * import Anthropic from '@anthropic-ai/sdk';\n * import { wrapAnthropic, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const anthropic = wrapAnthropic(new Anthropic());\n *\n * const response = await anthropic.messages.create({\n * model: 'claude-3-5-sonnet-20241022',\n * max_tokens: 1024,\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n */\n\nimport { SentrialClient } from './client.js';\nimport { calculateOpenAICost, calculateAnthropicCost, calculateGoogleCost } from './cost.js';\n\n// ============================================================================\n// Session Context (async-safe via AsyncLocalStorage in Node.js)\n// ============================================================================\n\n// For browser compatibility, we use a simple variable\n// In Node.js, you'd want AsyncLocalStorage for true async safety\nlet _currentSessionId: string | null = null;\nlet _currentClient: SentrialClient | null = null;\nlet _defaultClient: SentrialClient | null = null;\n\n/**\n * Set the current session context for auto-tracking.\n *\n * Call this before making LLM calls to associate them with a session.\n * This is automatically called when using withSession() or decorators.\n *\n * @param sessionId - The session ID to track LLM calls under\n * @param client - Optional Sentrial client (uses default if not provided)\n */\nexport function setSessionContext(sessionId: string, client?: SentrialClient): void {\n _currentSessionId = sessionId;\n if (client) {\n _currentClient = client;\n }\n}\n\n/**\n * Clear the current session context.\n */\nexport function clearSessionContext(): void {\n _currentSessionId = null;\n _currentClient = null;\n}\n\n/**\n * Get the current session ID.\n */\nexport function getSessionContext(): string | null {\n return _currentSessionId;\n}\n\n/**\n * Set the default client for wrappers.\n */\nexport function setDefaultClient(client: SentrialClient): void {\n _defaultClient = client;\n}\n\nfunction getTrackingClient(): SentrialClient | null {\n return _currentClient ?? _defaultClient;\n}\n\n// ============================================================================\n// OpenAI Wrapper\n// ============================================================================\n\n/**\n * Wrap an OpenAI client to automatically track all LLM calls.\n *\n * @param client - OpenAI client instance\n * @param options - Wrapper options\n * @returns The same client, now with auto-tracking enabled\n *\n * @example\n * ```ts\n * import OpenAI from 'openai';\n * import { wrapOpenAI, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n * const openai = wrapOpenAI(new OpenAI());\n *\n * // Now use client normally - all calls are tracked!\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello' }],\n * });\n * ```\n */\nexport function wrapOpenAI<T extends object>(\n client: T,\n options: { trackWithoutSession?: boolean } = {}\n): T {\n const { trackWithoutSession = false } = options;\n\n // Access chat.completions.create\n const chat = (client as any).chat;\n if (!chat?.completions?.create) {\n console.warn('Sentrial: OpenAI client does not have chat.completions.create');\n return client;\n }\n\n const originalCreate = chat.completions.create.bind(chat.completions);\n\n chat.completions.create = async function (...args: any[]) {\n const startTime = Date.now();\n const params = args[0] ?? {};\n const messages = params.messages ?? [];\n const model = params.model ?? 'unknown';\n\n try {\n const response = await originalCreate(...args);\n const durationMs = Date.now() - startTime;\n\n // Extract token usage\n const promptTokens = response.usage?.prompt_tokens ?? 0;\n const completionTokens = response.usage?.completion_tokens ?? 0;\n const totalTokens = response.usage?.total_tokens ?? 0;\n\n // Extract output\n let outputContent = '';\n if (response.choices?.[0]?.message?.content) {\n outputContent = response.choices[0].message.content;\n }\n\n // Calculate cost\n const cost = calculateOpenAICost({ model, inputTokens: promptTokens, outputTokens: completionTokens });\n\n // Track the call\n trackLLMCall({\n provider: 'openai',\n model,\n messages,\n output: outputContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession,\n });\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n trackLLMError({\n provider: 'openai',\n model,\n messages,\n error: error as Error,\n durationMs,\n trackWithoutSession,\n });\n throw error;\n }\n };\n\n return client;\n}\n\n// ============================================================================\n// Anthropic Wrapper\n// ============================================================================\n\n/**\n * Wrap an Anthropic client to automatically track all LLM calls.\n *\n * @param client - Anthropic client instance\n * @param options - Wrapper options\n * @returns The same client, now with auto-tracking enabled\n *\n * @example\n * ```ts\n * import Anthropic from '@anthropic-ai/sdk';\n * import { wrapAnthropic, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n * const anthropic = wrapAnthropic(new Anthropic());\n *\n * const response = await anthropic.messages.create({\n * model: 'claude-3-5-sonnet-20241022',\n * max_tokens: 1024,\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n */\nexport function wrapAnthropic<T extends object>(\n client: T,\n options: { trackWithoutSession?: boolean } = {}\n): T {\n const { trackWithoutSession = false } = options;\n\n // Access messages.create\n const messages = (client as any).messages;\n if (!messages?.create) {\n console.warn('Sentrial: Anthropic client does not have messages.create');\n return client;\n }\n\n const originalCreate = messages.create.bind(messages);\n\n messages.create = async function (...args: any[]) {\n const startTime = Date.now();\n const params = args[0] ?? {};\n const inputMessages = params.messages ?? [];\n const model = params.model ?? 'unknown';\n const system = params.system ?? '';\n\n try {\n const response = await originalCreate(...args);\n const durationMs = Date.now() - startTime;\n\n // Anthropic uses input_tokens/output_tokens\n const promptTokens = response.usage?.input_tokens ?? 0;\n const completionTokens = response.usage?.output_tokens ?? 0;\n const totalTokens = promptTokens + completionTokens;\n\n // Extract output\n let outputContent = '';\n if (response.content) {\n for (const block of response.content) {\n if (block.type === 'text') {\n outputContent += block.text;\n }\n }\n }\n\n const cost = calculateAnthropicCost({ model, inputTokens: promptTokens, outputTokens: completionTokens });\n\n // Include system prompt in messages for tracking\n const fullMessages = system\n ? [{ role: 'system', content: system }, ...inputMessages]\n : inputMessages;\n\n trackLLMCall({\n provider: 'anthropic',\n model,\n messages: fullMessages,\n output: outputContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession,\n });\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n trackLLMError({\n provider: 'anthropic',\n model,\n messages: inputMessages,\n error: error as Error,\n durationMs,\n trackWithoutSession,\n });\n throw error;\n }\n };\n\n return client;\n}\n\n// ============================================================================\n// Google/Gemini Wrapper\n// ============================================================================\n\n/**\n * Wrap a Google GenerativeModel to automatically track all LLM calls.\n *\n * @param model - Google GenerativeModel instance\n * @param options - Wrapper options\n * @returns The same model, now with auto-tracking enabled\n *\n * @example\n * ```ts\n * import { GoogleGenerativeAI } from '@google/generative-ai';\n * import { wrapGoogle, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);\n * const model = wrapGoogle(genAI.getGenerativeModel({ model: 'gemini-2.0-flash' }));\n *\n * const response = await model.generateContent('Hello!');\n * ```\n */\nexport function wrapGoogle<T extends object>(\n model: T,\n options: { trackWithoutSession?: boolean } = {}\n): T {\n const { trackWithoutSession = false } = options;\n\n // Access generateContent\n const originalGenerate = (model as any).generateContent;\n if (!originalGenerate) {\n console.warn('Sentrial: Google model does not have generateContent');\n return model;\n }\n\n (model as any).generateContent = async function (...args: any[]) {\n const startTime = Date.now();\n const contents = args[0];\n const modelName = (model as any).model ?? 'gemini-unknown';\n\n // Convert contents to messages format\n const messages = googleContentsToMessages(contents);\n\n try {\n const response = await originalGenerate.apply(model, args);\n const durationMs = Date.now() - startTime;\n\n // Extract usage\n let promptTokens = 0;\n let completionTokens = 0;\n if (response.usageMetadata) {\n promptTokens = response.usageMetadata.promptTokenCount ?? 0;\n completionTokens = response.usageMetadata.candidatesTokenCount ?? 0;\n }\n const totalTokens = promptTokens + completionTokens;\n\n // Extract output\n let outputContent = '';\n try {\n outputContent = response.response?.text() ?? '';\n } catch {\n // text() might throw if no text content\n }\n\n const cost = calculateGoogleCost({ model: modelName, inputTokens: promptTokens, outputTokens: completionTokens });\n\n trackLLMCall({\n provider: 'google',\n model: modelName,\n messages,\n output: outputContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession,\n });\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n trackLLMError({\n provider: 'google',\n model: modelName,\n messages,\n error: error as Error,\n durationMs,\n trackWithoutSession,\n });\n throw error;\n }\n };\n\n return model;\n}\n\nfunction googleContentsToMessages(contents: unknown): Array<{ role: string; content: string }> {\n if (typeof contents === 'string') {\n return [{ role: 'user', content: contents }];\n }\n if (Array.isArray(contents)) {\n return contents.map((item) => {\n if (typeof item === 'string') {\n return { role: 'user', content: item };\n }\n if (item && typeof item === 'object') {\n return { role: (item as any).role ?? 'user', content: String((item as any).content ?? item) };\n }\n return { role: 'user', content: String(item) };\n });\n }\n return [{ role: 'user', content: String(contents) }];\n}\n\n// ============================================================================\n// Auto-detect Wrapper\n// ============================================================================\n\n/**\n * Auto-detect and wrap any supported LLM client.\n *\n * @param client - Any supported LLM client (OpenAI, Anthropic, Google)\n * @param provider - Optional provider hint\n * @returns The wrapped client\n *\n * @example\n * ```ts\n * import OpenAI from 'openai';\n * import { wrapLLM } from '@sentrial/sdk';\n *\n * const client = wrapLLM(new OpenAI()); // Auto-detected as OpenAI\n * ```\n */\nexport function wrapLLM<T extends object>(\n client: T,\n provider?: 'openai' | 'anthropic' | 'google'\n): T {\n // Try to auto-detect based on client shape\n if (provider === 'openai' || (client as any).chat?.completions?.create) {\n return wrapOpenAI(client);\n }\n if (provider === 'anthropic' || (client as any).messages?.create) {\n return wrapAnthropic(client);\n }\n if (provider === 'google' || (client as any).generateContent) {\n return wrapGoogle(client);\n }\n\n console.warn('Sentrial: Unknown LLM client type. No auto-tracking applied.');\n return client;\n}\n\n// ============================================================================\n// Common Tracking Functions\n// ============================================================================\n\ninterface TrackLLMCallParams {\n provider: string;\n model: string;\n messages: unknown[];\n output: string;\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n cost: number;\n durationMs: number;\n trackWithoutSession: boolean;\n}\n\nfunction trackLLMCall(params: TrackLLMCallParams): void {\n const client = getTrackingClient();\n if (!client) return;\n\n const sessionId = _currentSessionId;\n if (!sessionId && !params.trackWithoutSession) {\n return;\n }\n\n if (sessionId) {\n client.trackToolCall({\n sessionId,\n toolName: `llm:${params.provider}:${params.model}`,\n toolInput: {\n messages: params.messages,\n model: params.model,\n provider: params.provider,\n },\n toolOutput: {\n content: params.output,\n tokens: {\n prompt: params.promptTokens,\n completion: params.completionTokens,\n total: params.totalTokens,\n },\n cost_usd: params.cost,\n },\n reasoning: `LLM call to ${params.provider} ${params.model}`,\n estimatedCost: params.cost,\n tokenCount: params.totalTokens,\n metadata: {\n provider: params.provider,\n model: params.model,\n duration_ms: params.durationMs,\n prompt_tokens: params.promptTokens,\n completion_tokens: params.completionTokens,\n },\n }).catch((err) => {\n console.warn('Sentrial: Failed to track LLM call:', err.message);\n });\n }\n}\n\ninterface TrackLLMErrorParams {\n provider: string;\n model: string;\n messages: unknown[];\n error: Error;\n durationMs: number;\n trackWithoutSession: boolean;\n}\n\nfunction trackLLMError(params: TrackLLMErrorParams): void {\n const client = getTrackingClient();\n if (!client) return;\n\n const sessionId = _currentSessionId;\n if (!sessionId && !params.trackWithoutSession) {\n return;\n }\n\n if (sessionId) {\n client.trackError({\n sessionId,\n errorMessage: params.error.message,\n errorType: params.error.name,\n toolName: `llm:${params.provider}:${params.model}`,\n metadata: {\n provider: params.provider,\n model: params.model,\n duration_ms: params.durationMs,\n },\n }).catch((err) => {\n console.warn('Sentrial: Failed to track LLM error:', err.message);\n });\n }\n}\n","/**\n * Sentrial Decorators - Easy instrumentation for AI agents\n *\n * Provides decorators and higher-order functions for automatic tracking:\n * - withTool: Track tool/function calls\n * - withSession: Create session boundaries around agent runs\n *\n * @example Using higher-order functions (works everywhere)\n * ```ts\n * import { withTool, withSession, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * // Wrap a tool function\n * const searchWeb = withTool('search', async (query: string) => {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * });\n *\n * // Wrap an agent session\n * const handleRequest = withSession('support-agent', async (userId: string, message: string) => {\n * const results = await searchWeb(message);\n * return processResults(results);\n * });\n * ```\n *\n * @example Using TypeScript decorators (requires experimentalDecorators)\n * ```ts\n * import { Tool, Session } from '@sentrial/sdk';\n *\n * class MyAgent {\n * @Tool('search')\n * async searchWeb(query: string) {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * }\n *\n * @Session('support-agent')\n * async handleRequest(userId: string, message: string) {\n * const results = await this.searchWeb(message);\n * return processResults(results);\n * }\n * }\n * ```\n */\n\nimport { SentrialClient, Interaction } from './client.js';\nimport {\n setSessionContext,\n clearSessionContext,\n getSessionContext,\n setDefaultClient,\n} from './wrappers.js';\n\n// Re-export context functions\nexport { setSessionContext, clearSessionContext, getSessionContext };\n\n// ============================================================================\n// Context Management\n// ============================================================================\n\nlet _defaultClient: SentrialClient | null = null;\nlet _currentInteraction: Interaction | null = null;\n\n/**\n * Get the default client, creating one if needed\n */\nfunction getClient(): SentrialClient | null {\n if (!_defaultClient) {\n try {\n _defaultClient = new SentrialClient();\n setDefaultClient(_defaultClient);\n } catch {\n return null;\n }\n }\n return _defaultClient;\n}\n\n/**\n * Set the default client for decorators\n */\nexport function setClient(client: SentrialClient): void {\n _defaultClient = client;\n setDefaultClient(client);\n}\n\n/**\n * Get the current session ID (if inside a session context)\n */\nexport function getCurrentSessionId(): string | null {\n return getSessionContext();\n}\n\n/**\n * Get the current interaction (if inside a session context)\n */\nexport function getCurrentInteraction(): Interaction | null {\n return _currentInteraction;\n}\n\n// ============================================================================\n// withTool - Higher-order function for tool tracking\n// ============================================================================\n\ntype AsyncFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => Promise<TReturn>;\ntype SyncFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn;\n\n/**\n * Wrap a function to automatically track it as a tool call.\n *\n * When called within a session context, automatically tracks:\n * - tool_input: Function arguments\n * - tool_output: Return value\n * - duration: Execution time\n * - errors: Any exceptions raised\n *\n * @param name - Name of the tool\n * @param fn - The function to wrap\n * @returns Wrapped function with automatic tracking\n *\n * @example\n * ```ts\n * const searchWeb = withTool('search', async (query: string) => {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * });\n *\n * // Use within a session - automatically tracked\n * const results = await searchWeb('AI agents');\n * ```\n */\nexport function withTool<TArgs extends unknown[], TReturn>(\n name: string,\n fn: AsyncFunction<TArgs, TReturn>\n): AsyncFunction<TArgs, TReturn>;\nexport function withTool<TArgs extends unknown[], TReturn>(\n name: string,\n fn: SyncFunction<TArgs, TReturn>\n): SyncFunction<TArgs, TReturn>;\nexport function withTool<TArgs extends unknown[], TReturn>(\n name: string,\n fn: (...args: TArgs) => TReturn | Promise<TReturn>\n): (...args: TArgs) => TReturn | Promise<TReturn> {\n // Check if function is async\n const isAsync = fn.constructor.name === 'AsyncFunction';\n\n if (isAsync) {\n return async function (...args: TArgs): Promise<TReturn> {\n return trackToolAsync(name, fn as AsyncFunction<TArgs, TReturn>, args);\n };\n } else {\n return function (...args: TArgs): TReturn {\n return trackToolSync(name, fn as SyncFunction<TArgs, TReturn>, args);\n };\n }\n}\n\nasync function trackToolAsync<TArgs extends unknown[], TReturn>(\n toolName: string,\n fn: AsyncFunction<TArgs, TReturn>,\n args: TArgs\n): Promise<TReturn> {\n const startTime = Date.now();\n const toolInput = buildToolInput(args);\n\n const client = getClient();\n const sessionId = getSessionContext();\n\n try {\n const result = await fn(...args);\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n const toolOutput = serializeOutput(result);\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput,\n toolOutput,\n metadata: { duration_ms: durationMs },\n }).catch((err) => {\n console.warn(`Sentrial: Failed to track tool ${toolName}:`, err.message);\n });\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n client.trackError({\n sessionId,\n errorMessage: (error as Error).message,\n errorType: (error as Error).name,\n toolName,\n stackTrace: (error as Error).stack,\n metadata: { duration_ms: durationMs },\n }).catch(() => {});\n }\n\n throw error;\n }\n}\n\nfunction trackToolSync<TArgs extends unknown[], TReturn>(\n toolName: string,\n fn: SyncFunction<TArgs, TReturn>,\n args: TArgs\n): TReturn {\n const startTime = Date.now();\n const toolInput = buildToolInput(args);\n\n const client = getClient();\n const sessionId = getSessionContext();\n\n try {\n const result = fn(...args);\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n const toolOutput = serializeOutput(result);\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput,\n toolOutput,\n metadata: { duration_ms: durationMs },\n }).catch((err) => {\n console.warn(`Sentrial: Failed to track tool ${toolName}:`, err.message);\n });\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n client.trackError({\n sessionId,\n errorMessage: (error as Error).message,\n errorType: (error as Error).name,\n toolName,\n stackTrace: (error as Error).stack,\n metadata: { duration_ms: durationMs },\n }).catch(() => {});\n }\n\n throw error;\n }\n}\n\n// ============================================================================\n// withSession - Higher-order function for session management\n// ============================================================================\n\ninterface SessionOptions {\n /** Parameter index or name for user ID (default: looks for 'userId' or first param) */\n userIdParam?: string | number;\n /** Parameter index or name for input (default: looks for 'input', 'message', 'query') */\n inputParam?: string | number;\n}\n\n/**\n * Wrap a function to automatically manage a session around it.\n *\n * Automatically:\n * - Creates a session when the function is called\n * - Sets up context for withTool and wrapped LLM calls\n * - Captures output from return value\n * - Completes the session when the function returns\n * - Marks as failed if an exception is raised\n *\n * @param agentName - Name of the agent\n * @param fn - The function to wrap\n * @param options - Session options\n * @returns Wrapped function with automatic session management\n *\n * @example\n * ```ts\n * const handleRequest = withSession('support-agent', async (userId: string, message: string) => {\n * // Session automatically created\n * // All withTool calls and wrapped LLM calls are tracked\n * const response = await processRequest(message);\n * return response; // Captured as output\n * });\n *\n * await handleRequest('user_123', 'Hello!');\n * ```\n */\nexport function withSession<TArgs extends unknown[], TReturn>(\n agentName: string,\n fn: AsyncFunction<TArgs, TReturn>,\n options?: SessionOptions\n): AsyncFunction<TArgs, TReturn>;\nexport function withSession<TArgs extends unknown[], TReturn>(\n agentName: string,\n fn: SyncFunction<TArgs, TReturn>,\n options?: SessionOptions\n): AsyncFunction<TArgs, TReturn>; // Session always returns a Promise\nexport function withSession<TArgs extends unknown[], TReturn>(\n agentName: string,\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n options: SessionOptions = {}\n): (...args: TArgs) => Promise<TReturn> {\n return async function (...args: TArgs): Promise<TReturn> {\n const client = getClient();\n if (!client) {\n // No client, just run the function\n return fn(...args) as Promise<TReturn>;\n }\n\n // Extract userId and input from args\n const { userId, userInput } = extractParams(args, options);\n\n // Create interaction\n const interaction = await client.begin({\n userId,\n event: agentName,\n input: userInput,\n });\n\n // Set context\n const sessionId = interaction.getSessionId();\n if (sessionId) {\n setSessionContext(sessionId, client);\n }\n _currentInteraction = interaction;\n\n try {\n const result = await fn(...args);\n\n // Capture output\n let output: string | undefined;\n if (typeof result === 'string') {\n output = result;\n } else if (result && typeof result === 'object') {\n if ('response' in result) {\n output = String((result as any).response);\n } else if ('output' in result) {\n output = String((result as any).output);\n }\n }\n if (output === undefined && result !== null && result !== undefined) {\n output = String(result).slice(0, 1000);\n }\n\n await interaction.finish({ output, success: true });\n return result;\n } catch (error) {\n await interaction.finish({\n success: false,\n failureReason: `${(error as Error).name}: ${(error as Error).message}`,\n });\n throw error;\n } finally {\n clearSessionContext();\n _currentInteraction = null;\n }\n };\n}\n\nfunction extractParams(\n args: unknown[],\n options: SessionOptions\n): { userId: string; userInput: string | undefined } {\n let userId = 'anonymous';\n let userInput: string | undefined;\n\n // Extract userId\n if (typeof options.userIdParam === 'number') {\n userId = String(args[options.userIdParam] ?? 'anonymous');\n } else if (typeof options.userIdParam === 'string') {\n // Can't access by name in JS, use index 0\n userId = String(args[0] ?? 'anonymous');\n } else {\n // Default: first arg is userId\n userId = String(args[0] ?? 'anonymous');\n }\n\n // Extract input\n if (typeof options.inputParam === 'number') {\n userInput = String(args[options.inputParam] ?? '');\n } else {\n // Default: second arg is input\n const input = args[1];\n if (typeof input === 'string') {\n userInput = input;\n } else if (Array.isArray(input)) {\n userInput = JSON.stringify(input).slice(0, 500);\n }\n }\n\n return { userId, userInput };\n}\n\n// ============================================================================\n// TypeScript Decorators (requires experimentalDecorators)\n// ============================================================================\n\n/**\n * Method decorator to track a method as a tool call.\n *\n * @param name - Name of the tool (optional, defaults to method name)\n *\n * @example\n * ```ts\n * class MyAgent {\n * @Tool('search')\n * async searchWeb(query: string) {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * }\n * }\n * ```\n */\nexport function Tool(name?: string): MethodDecorator {\n return function (\n _target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const originalMethod = descriptor.value;\n const toolName = name ?? String(propertyKey);\n\n descriptor.value = async function (...args: unknown[]) {\n const startTime = Date.now();\n const toolInput = buildToolInput(args);\n\n const client = getClient();\n const sessionId = getSessionContext();\n\n try {\n const result = await originalMethod.apply(this, args);\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n const toolOutput = serializeOutput(result);\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput,\n toolOutput,\n metadata: { duration_ms: durationMs },\n }).catch((err) => {\n console.warn(`Sentrial: Failed to track tool ${toolName}:`, err.message);\n });\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n client.trackError({\n sessionId,\n errorMessage: (error as Error).message,\n errorType: (error as Error).name,\n toolName,\n stackTrace: (error as Error).stack,\n metadata: { duration_ms: durationMs },\n }).catch(() => {});\n }\n\n throw error;\n }\n };\n\n return descriptor;\n };\n}\n\n/**\n * Method decorator to wrap a method in a session.\n *\n * @param agentName - Name of the agent (optional, defaults to class name)\n * @param options - Session options\n *\n * @example\n * ```ts\n * class MyAgent {\n * @TrackSession('support-agent')\n * async handleRequest(userId: string, message: string) {\n * // Session automatically created\n * const results = await this.searchWeb(message);\n * return processResults(results);\n * }\n * }\n * ```\n */\nexport function TrackSession(agentName?: string, options?: SessionOptions): MethodDecorator {\n return function (\n target: object,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const originalMethod = descriptor.value;\n const agent = agentName ?? target.constructor.name;\n\n descriptor.value = async function (...args: unknown[]) {\n const client = getClient();\n if (!client) {\n return originalMethod.apply(this, args);\n }\n\n const { userId, userInput } = extractParams(args, options ?? {});\n\n const interaction = await client.begin({\n userId,\n event: agent,\n input: userInput,\n });\n\n const sessionId = interaction.getSessionId();\n if (sessionId) {\n setSessionContext(sessionId, client);\n }\n _currentInteraction = interaction;\n\n try {\n const result = await originalMethod.apply(this, args);\n\n let output: string | undefined;\n if (typeof result === 'string') {\n output = result;\n } else if (result && typeof result === 'object') {\n if ('response' in result) {\n output = String((result as any).response);\n } else if ('output' in result) {\n output = String((result as any).output);\n }\n }\n if (output === undefined && result !== null && result !== undefined) {\n output = String(result).slice(0, 1000);\n }\n\n await interaction.finish({ output, success: true });\n return result;\n } catch (error) {\n await interaction.finish({\n success: false,\n failureReason: `${(error as Error).name}: ${(error as Error).message}`,\n });\n throw error;\n } finally {\n clearSessionContext();\n _currentInteraction = null;\n }\n };\n\n return descriptor;\n };\n}\n\n// ============================================================================\n// SessionContext - Manual session control\n// ============================================================================\n\n/**\n * Manual session context for when you need more control.\n *\n * @example\n * ```ts\n * const ctx = new SessionContext({\n * userId: 'user_123',\n * agent: 'my-agent',\n * input: 'Hello!',\n * });\n *\n * await ctx.start();\n * try {\n * // All tool calls are tracked\n * const result = await myTool(...);\n * ctx.setOutput(result);\n * await ctx.finish();\n * } catch (error) {\n * await ctx.finish({ success: false, error: error.message });\n * }\n * ```\n */\nexport class SessionContext {\n private readonly userId: string;\n private readonly agent: string;\n private readonly input?: string;\n private readonly client: SentrialClient | null;\n private interaction: Interaction | null = null;\n private output?: string;\n\n constructor(options: { userId: string; agent: string; input?: string; client?: SentrialClient }) {\n this.userId = options.userId;\n this.agent = options.agent;\n this.input = options.input;\n this.client = options.client ?? getClient();\n }\n\n /**\n * Start the session\n */\n async start(): Promise<this> {\n if (!this.client) return this;\n\n this.interaction = await this.client.begin({\n userId: this.userId,\n event: this.agent,\n input: this.input,\n });\n\n const sessionId = this.interaction.getSessionId();\n if (sessionId) {\n setSessionContext(sessionId, this.client);\n }\n _currentInteraction = this.interaction;\n\n return this;\n }\n\n /**\n * Set the output for this session\n */\n setOutput(output: string): void {\n this.output = output;\n }\n\n /**\n * Finish the session\n */\n async finish(options?: { success?: boolean; error?: string }): Promise<void> {\n if (this.interaction) {\n await this.interaction.finish({\n output: this.output,\n success: options?.success ?? true,\n failureReason: options?.error,\n });\n }\n\n clearSessionContext();\n _currentInteraction = null;\n }\n\n /**\n * Get the session ID\n */\n get sessionId(): string | null {\n return this.interaction?.getSessionId() ?? null;\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction buildToolInput(args: unknown[]): Record<string, unknown> {\n if (args.length === 0) {\n return {};\n }\n if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) {\n return serializeValue(args[0]) as Record<string, unknown>;\n }\n return {\n args: args.map(serializeValue),\n };\n}\n\nfunction serializeValue(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(serializeValue);\n }\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = serializeValue(v);\n }\n return result;\n }\n try {\n return String(value).slice(0, 1000);\n } catch {\n return `<${typeof value}>`;\n }\n}\n\nfunction serializeOutput(value: unknown): Record<string, unknown> {\n if (value === null || value === undefined) {\n return { result: null };\n }\n if (typeof value === 'object' && !Array.isArray(value)) {\n return serializeValue(value) as Record<string, unknown>;\n }\n return { result: serializeValue(value) };\n}\n","/**\n * Sentrial Experiment Context\n *\n * Provides context-aware configuration for experiments. When running experiments\n * via the CLI, the system prompt and other experiment config is automatically\n * available to your agent code via simple function calls.\n *\n * @example\n * ```ts\n * import { getSystemPrompt } from '@sentrial/sdk';\n *\n * async function myAgent(query: string): Promise<string> {\n * // When running normally: returns your default\n * // When running experiment: returns the variant's system prompt\n * const systemPrompt = getSystemPrompt('You are a helpful assistant.');\n *\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [\n * { role: 'system', content: systemPrompt },\n * { role: 'user', content: query },\n * ],\n * });\n *\n * return response.choices[0].message.content!;\n * }\n * ```\n *\n * That's it! No other changes needed. The experiment runner handles setting the context.\n */\n\n// ============================================================================\n// Experiment Context Types\n// ============================================================================\n\n/**\n * Context for the current experiment run.\n */\nexport interface ExperimentContext {\n /** The system prompt for this variant */\n systemPrompt: string;\n /** Name of the variant being tested */\n variantName: string;\n /** The experiment ID */\n experimentId: string;\n /** The test case session ID (if applicable) */\n testCaseId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Context Storage\n// ============================================================================\n\n// The context variable - holds experiment state for current execution\nlet _experimentContext: ExperimentContext | null = null;\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Get the current system prompt.\n *\n * When running an experiment, this returns the variant's system prompt.\n * Otherwise, returns your default.\n *\n * @param defaultPrompt - The default system prompt to use when not in experiment mode.\n * This should be your normal production prompt.\n * @returns The system prompt to use.\n *\n * @example\n * ```ts\n * import { getSystemPrompt } from '@sentrial/sdk';\n *\n * async function myAgent(query: string) {\n * // Just wrap your existing prompt with getSystemPrompt()\n * const prompt = getSystemPrompt('You are a helpful weather assistant.');\n *\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [\n * { role: 'system', content: prompt },\n * { role: 'user', content: query },\n * ],\n * });\n *\n * return response.choices[0].message.content;\n * }\n * ```\n */\nexport function getSystemPrompt(defaultPrompt?: string): string {\n if (_experimentContext?.systemPrompt) {\n return _experimentContext.systemPrompt;\n }\n return defaultPrompt ?? '';\n}\n\n/**\n * Get the full experiment context if running in experiment mode.\n *\n * Returns null if not running an experiment.\n *\n * @returns ExperimentContext with variant info, or null.\n *\n * @example\n * ```ts\n * import { getExperimentContext } from '@sentrial/sdk';\n *\n * const ctx = getExperimentContext();\n * if (ctx) {\n * console.log(`Running variant: ${ctx.variantName}`);\n * }\n * ```\n */\nexport function getExperimentContext(): ExperimentContext | null {\n return _experimentContext;\n}\n\n/**\n * Check if currently running in experiment mode.\n *\n * @returns True if running via experiment runner, false otherwise.\n */\nexport function isExperimentMode(): boolean {\n return _experimentContext !== null;\n}\n\n/**\n * Get the current variant name if in experiment mode.\n *\n * @returns The variant name (e.g., \"Control\", \"Variant A\"), or null.\n */\nexport function getVariantName(): string | null {\n return _experimentContext?.variantName ?? null;\n}\n\n/**\n * Get the current experiment ID if in experiment mode.\n *\n * @returns The experiment ID, or null.\n */\nexport function getExperimentId(): string | null {\n return _experimentContext?.experimentId ?? null;\n}\n\n// ============================================================================\n// Internal Functions (used by experiment runner)\n// ============================================================================\n\n/**\n * Set the experiment context. Called by the experiment runner before running agent.\n *\n * @internal This is an internal function - users don't need to call this directly.\n */\nexport function setExperimentContext(context: ExperimentContext): void {\n _experimentContext = context;\n}\n\n/**\n * Clear the experiment context. Called by the experiment runner after agent completes.\n *\n * @internal This is an internal function - users don't need to call this directly.\n */\nexport function clearExperimentContext(): void {\n _experimentContext = null;\n}\n","/**\n * Sentrial Experiments - Run prompt A/B tests with results synced to Sentrial\n *\n * @example\n * ```ts\n * import { Experiment } from '@sentrial/sdk';\n *\n * const experiment = new Experiment('exp_abc123');\n *\n * // Get config\n * await experiment.load();\n * console.log(`Testing ${experiment.variants.length} variants on ${experiment.testCases.length} inputs`);\n *\n * // Run with your agent function\n * await experiment.run(async (testCase, variant, tracker) => {\n * // Create a session for this run\n * const interaction = await client.begin({\n * userId: 'experiment',\n * event: 'my-agent',\n * input: testCase.userInput,\n * });\n * tracker.setResultSessionId(interaction.getSessionId()!);\n *\n * // Run your agent with the variant's system prompt\n * const response = await runAgent(testCase.userInput, variant.systemPrompt);\n *\n * await interaction.finish({ output: response, success: true });\n * });\n * ```\n *\n * @example Manual run tracking\n * ```ts\n * const experiment = new Experiment('exp_abc123');\n * await experiment.load();\n *\n * for (const variant of experiment.variants) {\n * for (const testCase of experiment.testCases) {\n * const tracker = await experiment.trackRun(variant.name, testCase.sessionId);\n * try {\n * const sessionId = await runAgent(testCase.userInput, variant.systemPrompt);\n * tracker.setResultSessionId(sessionId);\n * await tracker.complete();\n * } catch (error) {\n * await tracker.fail(error.message);\n * }\n * }\n * }\n * ```\n */\n\nimport { SentrialClient } from './client.js';\nimport { setExperimentContext, clearExperimentContext } from './context.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A prompt variant to test.\n */\nexport interface ExperimentVariant {\n /** Name of the variant */\n name: string;\n /** The system prompt for this variant */\n systemPrompt: string;\n /** Optional description */\n description?: string;\n}\n\n/**\n * A test case (input) to run against each variant.\n */\nexport interface ExperimentTestCase {\n /** Session ID of the original session */\n sessionId: string;\n /** The user input to test */\n userInput: string;\n}\n\n/**\n * Result of a single experiment run.\n */\nexport interface ExperimentRunResult {\n /** Name of the variant */\n variantName: string;\n /** Session ID of the original test case */\n testCaseSessionId: string;\n /** Session ID of the result session (if created) */\n resultSessionId?: string;\n /** Whether the run succeeded */\n success: boolean;\n /** Error message if failed */\n errorMessage?: string;\n}\n\n/**\n * Aggregated experiment results.\n */\nexport interface ExperimentResults {\n experimentId: string;\n status: string;\n variants: Array<{\n name: string;\n successRate: number;\n avgScore?: number;\n avgCost?: number;\n avgDuration?: number;\n runCount: number;\n }>;\n winner?: string;\n}\n\n// ============================================================================\n// Run Tracker\n// ============================================================================\n\n/**\n * Tracks a single experiment run.\n *\n * Created by Experiment.trackRun() - use this to manually track runs.\n */\nexport class ExperimentRunTracker {\n private readonly experiment: Experiment;\n private readonly variantName: string;\n private readonly baseSessionId: string;\n private runId?: string;\n private resultSessionId?: string;\n private _success = true;\n private _errorMessage?: string;\n\n /** @internal */\n constructor(experiment: Experiment, variantName: string, baseSessionId: string) {\n this.experiment = experiment;\n this.variantName = variantName;\n this.baseSessionId = baseSessionId;\n }\n\n /**\n * Start the run - creates a run record via API.\n */\n async start(): Promise<this> {\n try {\n const response = await this.experiment.request<{ run?: { id: string } }>(\n 'POST',\n `/api/experiments/${this.experiment.experimentId}/runs`,\n {\n variantName: this.variantName,\n baseSessionId: this.baseSessionId,\n }\n );\n this.runId = response?.run?.id;\n } catch {\n // Ignore errors starting the run\n }\n return this;\n }\n\n /**\n * Set the session ID of the result session.\n */\n setResultSessionId(sessionId: string): void {\n this.resultSessionId = sessionId;\n }\n\n /**\n * Mark the run as complete.\n */\n async complete(): Promise<void> {\n if (!this.runId) return;\n\n try {\n await this.experiment.request(\n 'PATCH',\n `/api/experiments/${this.experiment.experimentId}/runs/${this.runId}`,\n {\n status: 'completed',\n resultSessionId: this.resultSessionId,\n }\n );\n } catch {\n // Ignore errors completing the run\n }\n }\n\n /**\n * Mark the run as failed.\n */\n async fail(errorMessage: string): Promise<void> {\n this._success = false;\n this._errorMessage = errorMessage;\n\n if (!this.runId) return;\n\n try {\n await this.experiment.request(\n 'PATCH',\n `/api/experiments/${this.experiment.experimentId}/runs/${this.runId}`,\n {\n status: 'failed',\n resultSessionId: this.resultSessionId,\n errorMessage,\n }\n );\n } catch {\n // Ignore errors failing the run\n }\n }\n\n /**\n * Get the result of this run.\n */\n getResult(): ExperimentRunResult {\n return {\n variantName: this.variantName,\n testCaseSessionId: this.baseSessionId,\n resultSessionId: this.resultSessionId,\n success: this._success,\n errorMessage: this._errorMessage,\n };\n }\n}\n\n// ============================================================================\n// Experiment Class\n// ============================================================================\n\ntype AgentFunction = (\n testCase: ExperimentTestCase,\n variant: ExperimentVariant,\n tracker: ExperimentRunTracker\n) => Promise<void>;\n\n/**\n * Run experiments with results synced to Sentrial.\n *\n * @example\n * ```ts\n * const experiment = new Experiment('exp_abc123');\n *\n * await experiment.load();\n * console.log(`Testing ${experiment.variants.length} variants`);\n *\n * await experiment.run(async (testCase, variant, tracker) => {\n * const sessionId = await runMyAgent(testCase.userInput, variant.systemPrompt);\n * tracker.setResultSessionId(sessionId);\n * });\n * ```\n */\nexport class Experiment {\n /** The experiment ID */\n readonly experimentId: string;\n\n /** @internal */\n readonly client: SentrialClient;\n /** @internal */\n readonly apiUrl: string;\n /** @internal */\n readonly apiKey?: string;\n \n private config?: Record<string, unknown>;\n private _variants?: ExperimentVariant[];\n private _testCases?: ExperimentTestCase[];\n\n /**\n * Create an experiment instance.\n *\n * @param experimentId - The experiment ID from Sentrial dashboard\n * @param options - Configuration options\n */\n constructor(\n experimentId: string,\n options: {\n apiKey?: string;\n apiUrl?: string;\n } = {}\n ) {\n this.experimentId = experimentId;\n this.apiUrl = (\n options.apiUrl ??\n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_URL : undefined) ??\n 'https://api.sentrial.com'\n ).replace(/\\/$/, '');\n this.apiKey = options.apiKey ?? \n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_KEY : undefined);\n this.client = new SentrialClient({\n apiKey: this.apiKey,\n apiUrl: this.apiUrl,\n failSilently: false, // We want errors for experiments\n });\n }\n\n /**\n * Make an HTTP request to the API\n * @internal\n */\n async request<T>(method: string, path: string, body?: unknown): Promise<T | null> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n const response = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n return null;\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Load the experiment configuration from the API.\n */\n async load(): Promise<this> {\n interface ExperimentConfig {\n experiment?: {\n name?: string;\n status?: string;\n variants?: Array<{\n name: string;\n systemPrompt?: string;\n description?: string;\n }>;\n testCases?: Array<{\n sessionId: string;\n userInput?: string;\n }>;\n };\n }\n\n const response = await this.request<ExperimentConfig>(\n 'GET',\n `/api/experiments/${this.experimentId}/runs`\n );\n\n if (!response?.experiment) {\n throw new Error(`Failed to load experiment config for ${this.experimentId}`);\n }\n\n this.config = response.experiment;\n\n // Parse variants\n const variants = this.config.variants as Array<{ name: string; systemPrompt?: string; description?: string }> | undefined;\n this._variants = variants?.map((v) => ({\n name: v.name ?? '',\n systemPrompt: v.systemPrompt ?? '',\n description: v.description,\n })) ?? [];\n\n // Parse test cases (only include those with input)\n const testCases = this.config.testCases as Array<{ sessionId: string; userInput?: string }> | undefined;\n this._testCases = testCases\n ?.filter((tc) => tc.userInput)\n ?.map((tc) => ({\n sessionId: tc.sessionId ?? '',\n userInput: tc.userInput ?? '',\n })) ?? [];\n\n return this;\n }\n\n /**\n * Get experiment name.\n */\n get name(): string {\n return (this.config?.name as string) ?? '';\n }\n\n /**\n * Get experiment status.\n */\n get status(): string {\n return (this.config?.status as string) ?? '';\n }\n\n /**\n * Get list of variants to test.\n */\n get variants(): ExperimentVariant[] {\n if (!this._variants) {\n throw new Error('Experiment not loaded. Call load() first.');\n }\n return this._variants;\n }\n\n /**\n * Get list of test cases.\n */\n get testCases(): ExperimentTestCase[] {\n if (!this._testCases) {\n throw new Error('Experiment not loaded. Call load() first.');\n }\n return this._testCases;\n }\n\n /**\n * Get a specific variant by name.\n */\n getVariant(name: string): ExperimentVariant | undefined {\n return this.variants.find((v) => v.name === name);\n }\n\n /**\n * Create a run tracker for manual experiment runs.\n */\n async trackRun(variantName: string, baseSessionId: string): Promise<ExperimentRunTracker> {\n const tracker = new ExperimentRunTracker(this, variantName, baseSessionId);\n await tracker.start();\n return tracker;\n }\n\n /**\n * Run the experiment with all variants and test cases.\n *\n * @param agentFn - Function that runs your agent with a test case and variant\n * @param options - Run options\n * @returns List of run results\n */\n async run(\n agentFn: AgentFunction,\n options: {\n /** Run test cases in parallel */\n parallel?: boolean;\n /** Max parallel workers */\n maxWorkers?: number;\n } = {}\n ): Promise<ExperimentRunResult[]> {\n const { parallel = false, maxWorkers = 4 } = options;\n\n if (!this._variants || !this._testCases) {\n await this.load();\n }\n\n const results: ExperimentRunResult[] = [];\n const totalRuns = this.variants.length * this.testCases.length;\n let completed = 0;\n\n console.log(`Running experiment: ${this.name || this.experimentId}`);\n console.log(` Variants: ${this.variants.length}`);\n console.log(` Test cases: ${this.testCases.length}`);\n console.log(` Total runs: ${totalRuns}`);\n console.log();\n\n const runSingle = async (\n variant: ExperimentVariant,\n testCase: ExperimentTestCase\n ): Promise<ExperimentRunResult> => {\n const tracker = await this.trackRun(variant.name, testCase.sessionId);\n\n // Set experiment context so getSystemPrompt() returns the variant prompt\n setExperimentContext({\n systemPrompt: variant.systemPrompt,\n variantName: variant.name,\n experimentId: this.experimentId,\n testCaseId: testCase.sessionId,\n });\n\n try {\n await agentFn(testCase, variant, tracker);\n await tracker.complete();\n return tracker.getResult();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n await tracker.fail(errorMessage);\n return tracker.getResult();\n } finally {\n clearExperimentContext();\n }\n };\n\n if (parallel) {\n // Run in parallel with limited concurrency\n const queue: Array<() => Promise<void>> = [];\n\n for (const variant of this.variants) {\n for (const testCase of this.testCases) {\n queue.push(async () => {\n const result = await runSingle(variant, testCase);\n results.push(result);\n completed++;\n const status = result.success ? '✓' : '✗';\n console.log(\n ` [${completed}/${totalRuns}] ${status} ${variant.name} x ${testCase.sessionId.slice(0, 8)}...`\n );\n });\n }\n }\n\n // Process queue with limited concurrency\n const executing: Promise<void>[] = [];\n for (const task of queue) {\n const promise = task().then(() => {\n executing.splice(executing.indexOf(promise), 1);\n });\n executing.push(promise);\n\n if (executing.length >= maxWorkers) {\n await Promise.race(executing);\n }\n }\n await Promise.all(executing);\n } else {\n // Run sequentially\n for (const variant of this.variants) {\n for (const testCase of this.testCases) {\n const result = await runSingle(variant, testCase);\n results.push(result);\n completed++;\n const status = result.success ? '✓' : '✗';\n console.log(\n ` [${completed}/${totalRuns}] ${status} ${variant.name} x ${testCase.sessionId.slice(0, 8)}...`\n );\n }\n }\n }\n\n console.log();\n console.log('Experiment complete!');\n console.log(` Successful: ${results.filter((r) => r.success).length}/${totalRuns}`);\n console.log(` Failed: ${results.filter((r) => !r.success).length}/${totalRuns}`);\n\n return results;\n }\n\n /**\n * Reset all runs for this experiment (for re-running).\n */\n async reset(): Promise<boolean> {\n try {\n const response = await this.request(\n 'DELETE',\n `/api/experiments/${this.experimentId}/runs`\n );\n\n if (response !== null) {\n // Clear cached config\n this.config = undefined;\n this._variants = undefined;\n this._testCases = undefined;\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Fetch aggregated results from the API.\n */\n async getResults(): Promise<ExperimentResults | null> {\n try {\n const response = await this.request<ExperimentResults>(\n 'GET',\n `/api/experiments/${this.experimentId}/results`\n );\n return response;\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"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","names":["EventType","getClient","_defaultClient","_defaultClient","getClient"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/redact.ts","../src/async-context.ts","../src/cost.ts","../src/wrappers.ts","../src/batcher.ts","../src/types.ts","../src/client.ts","../src/vercel.ts","../src/claude-code.ts","../src/decorators.ts","../src/context.ts","../src/experiment.ts"],"sourcesContent":["/**\n * Sentrial SDK for TypeScript/Node.js\n *\n * AI agent observability and monitoring. Track sessions, tool calls, and metrics\n * to power signal detection, root cause analysis, and AI-suggested fixes.\n *\n * @packageDocumentation\n *\n * @example Basic Usage\n * ```ts\n * import { SentrialClient } from '@sentrial/sdk';\n *\n * const client = new SentrialClient({\n * apiKey: process.env.SENTRIAL_API_KEY!,\n * });\n *\n * // Create session\n * const sessionId = await client.createSession({\n * name: 'Support Request',\n * agentName: 'support-agent',\n * userId: 'user_123',\n * });\n *\n * // Track tool calls\n * await client.trackToolCall({\n * sessionId,\n * toolName: 'search_kb',\n * toolInput: { query: 'help' },\n * toolOutput: { results: ['...'] },\n * });\n *\n * // Complete session\n * await client.completeSession({\n * sessionId,\n * success: true,\n * customMetrics: { satisfaction: 4.5 },\n * });\n * ```\n *\n * @example Simple begin/finish Pattern\n * ```ts\n * import { sentrial } from '@sentrial/sdk';\n *\n * sentrial.configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const interaction = await sentrial.begin({\n * userId: 'user_123',\n * event: 'chat_message',\n * input: message,\n * });\n *\n * // ... agent work ...\n *\n * await interaction.finish({ output: response, success: true });\n * ```\n */\n\n// Main client\nexport { SentrialClient, Interaction, configure, begin, sentrial } from './client.js';\n\n// Types\nexport type {\n SentrialClientConfig,\n CreateSessionParams,\n CompleteSessionParams,\n TrackToolCallParams,\n TrackDecisionParams,\n TrackErrorParams,\n BeginParams,\n FinishParams,\n Session,\n Event,\n SessionStatus,\n CostParams,\n ApiResponse,\n} from './types.js';\n\n// Enums\nexport { EventType } from './types.js';\n\n// Cost calculation utilities\nexport {\n calculateOpenAICost,\n calculateAnthropicCost,\n calculateGoogleCost,\n} from './cost.js';\n\n// Errors\nexport {\n SentrialError,\n ApiError,\n NetworkError,\n ValidationError,\n} from './errors.js';\n\n// Vercel AI SDK Integration\nexport {\n wrapAISDK,\n configureVercel,\n} from './vercel.js';\n\nexport type {\n GenerateTextParams,\n GenerateTextResult,\n StreamTextResult,\n} from './vercel.js';\n\n// Claude Agent SDK Integration\nexport { wrapClaudeAgent } from './claude-code.js';\nexport type { WrapClaudeAgentOptions } from './claude-code.js';\n\n// LLM Auto-Wrappers\nexport {\n wrapOpenAI,\n wrapAnthropic,\n wrapGoogle,\n wrapLLM,\n setSessionContext,\n clearSessionContext,\n getSessionContext,\n setDefaultClient,\n} from './wrappers.js';\n\n// Decorators & Higher-Order Functions\nexport {\n withTool,\n withSession,\n Tool,\n TrackSession,\n SessionContext,\n setClient,\n getCurrentSessionId,\n getCurrentInteraction,\n} from './decorators.js';\n\n// Experiment Context\nexport {\n getSystemPrompt,\n getExperimentContext,\n isExperimentMode,\n getVariantName,\n getExperimentId,\n setExperimentContext,\n clearExperimentContext,\n} from './context.js';\n\nexport type { ExperimentContext } from './context.js';\n\n// Experiments\nexport {\n Experiment,\n ExperimentRunTracker,\n} from './experiment.js';\n\nexport type {\n ExperimentVariant,\n ExperimentTestCase,\n ExperimentRunResult,\n ExperimentResults,\n} from './experiment.js';\n\n// Event Batching\nexport { EventBatcher } from './batcher.js';\nexport type { BatcherConfig } from './batcher.js';\n\n// Async Context (for advanced concurrency-safe usage)\nexport { createContextVar } from './async-context.js';\nexport type { ContextVar, ContextToken } from './async-context.js';\n\n// PII Redaction\nexport { redactString, redactValue, redactPayload, hashValue, replaceMatch } from './redact.js';\n\nexport type {\n PiiMode,\n PiiField,\n PiiCustomPattern,\n PiiBuiltinPatterns,\n PiiConfig,\n} from './redact.js';\n","/**\n * Error classes for the Sentrial SDK\n */\n\n/**\n * Base error class for all Sentrial SDK errors\n */\nexport class SentrialError extends Error {\n /** HTTP status code (if applicable) */\n public readonly status?: number;\n /** Error code from the API */\n public readonly code?: string;\n /** Additional error details */\n public readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n options?: {\n status?: number;\n code?: string;\n details?: Record<string, unknown>;\n cause?: Error;\n }\n ) {\n super(message, { cause: options?.cause });\n this.name = 'SentrialError';\n this.status = options?.status;\n this.code = options?.code;\n this.details = options?.details;\n\n // Maintain proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SentrialError);\n }\n }\n\n /**\n * Check if the error is a rate limit error\n */\n isRateLimitError(): boolean {\n return this.status === 429;\n }\n\n /**\n * Check if the error is an authentication error\n */\n isAuthError(): boolean {\n return this.status === 401 || this.status === 403;\n }\n\n /**\n * Check if the error is a network/connection error\n */\n isNetworkError(): boolean {\n return this.code === 'NETWORK_ERROR' || this.code === 'ECONNREFUSED';\n }\n\n /**\n * Convert to a plain object for logging\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n code: this.code,\n details: this.details,\n stack: this.stack,\n };\n }\n}\n\n/**\n * Error thrown when the API returns an error response\n */\nexport class ApiError extends SentrialError {\n constructor(\n message: string,\n status: number,\n code?: string,\n details?: Record<string, unknown>\n ) {\n super(message, { status, code, details });\n this.name = 'ApiError';\n }\n}\n\n/**\n * Error thrown when there's a network/connection issue\n */\nexport class NetworkError extends SentrialError {\n constructor(message: string, cause?: Error) {\n super(message, { code: 'NETWORK_ERROR', cause });\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Error thrown when validation fails\n */\nexport class ValidationError extends SentrialError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, { code: 'VALIDATION_ERROR', details });\n this.name = 'ValidationError';\n }\n}\n","/**\n * PII Redaction Module\n *\n * Automatically detects and redacts personally identifiable information (PII)\n * from SDK payloads before they are sent to the Sentrial API.\n */\n\n// Lazy-load Node.js crypto to avoid crashing in browser/edge runtimes.\n// Uses eval('require') to prevent bundlers from resolving the import at\n// build time (same pattern as async-context.ts for node:async_hooks).\nlet _createHash: typeof import('crypto').createHash | undefined;\n\ntry {\n // eslint-disable-next-line no-eval\n const mod = eval('require')('crypto');\n if (mod?.createHash) {\n _createHash = mod.createHash;\n }\n} catch {\n // Not in Node.js — hash mode will throw at call time.\n}\n\nfunction getCreateHash(): typeof import('crypto').createHash {\n if (!_createHash) {\n throw new Error(\n 'Sentrial PII hash mode requires Node.js crypto module. ' +\n 'Use mode \"label\" or \"remove\" in browser/edge environments.'\n );\n }\n return _createHash;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** How redacted values are replaced */\nexport type PiiMode = 'label' | 'hash' | 'remove';\n\n/** Fields that can be redacted in SDK payloads */\nexport type PiiField =\n | 'userInput'\n | 'assistantOutput'\n | 'toolInput'\n | 'toolOutput'\n | 'metadata'\n | 'reasoning'\n | 'failureReason';\n\n/** A custom regex pattern with a human-readable label */\nexport interface PiiCustomPattern {\n pattern: RegExp;\n label: string;\n}\n\n/** Toggle individual builtin pattern categories */\nexport interface PiiBuiltinPatterns {\n emails?: boolean;\n phones?: boolean;\n ssns?: boolean;\n creditCards?: boolean;\n ipAddresses?: boolean;\n}\n\n/** Full PII redaction configuration */\nexport interface PiiConfig {\n /** Enable PII redaction (default: false) */\n enabled: boolean;\n /** Which payload fields to redact (default: DEFAULT_FIELDS) */\n fields?: PiiField[];\n /** Replacement mode (default: 'label') */\n mode?: PiiMode;\n /** Enable enhanced detection heuristics (reserved for future use) */\n enhancedDetection?: boolean;\n /** Toggle builtin patterns (default: all enabled) */\n builtinPatterns?: PiiBuiltinPatterns;\n /** Additional custom patterns to apply */\n customPatterns?: PiiCustomPattern[];\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default fields that are redacted when PII redaction is enabled */\nexport const DEFAULT_FIELDS: PiiField[] = [\n 'userInput',\n 'assistantOutput',\n 'toolInput',\n 'toolOutput',\n];\n\n/** Default builtin pattern toggles (all enabled) */\nexport const DEFAULT_BUILTIN: Required<PiiBuiltinPatterns> = {\n emails: true,\n phones: true,\n ssns: true,\n creditCards: true,\n ipAddresses: true,\n};\n\n// ============================================================================\n// Builtin Regex Patterns\n// ============================================================================\n\n/** Email addresses (RFC 5322 simplified) */\nconst EMAIL_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n\n/** US phone numbers: (###) ###-####, ###-###-####, ### ### ####, +1########## */\nconst PHONE_PATTERN =\n /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/g;\n\n/** US Social Security Numbers: ###-##-#### */\nconst SSN_PATTERN = /\\b\\d{3}-\\d{2}-\\d{4}\\b/g;\n\n/** Credit card numbers: 13-19 digits, optionally separated by spaces or dashes */\nconst CREDIT_CARD_PATTERN = /\\b(?:\\d[-\\s]?){13,19}\\b/g;\n\n/** IPv4 addresses */\nconst IP_ADDRESS_PATTERN =\n /\\b(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\b/g;\n\n/** Map of builtin pattern keys to their regex and label */\nconst BUILTIN_PATTERNS: Record<keyof Required<PiiBuiltinPatterns>, { pattern: RegExp; label: string }> = {\n emails: { pattern: EMAIL_PATTERN, label: 'EMAIL' },\n phones: { pattern: PHONE_PATTERN, label: 'PHONE' },\n ssns: { pattern: SSN_PATTERN, label: 'SSN' },\n creditCards: { pattern: CREDIT_CARD_PATTERN, label: 'CREDIT_CARD' },\n ipAddresses: { pattern: IP_ADDRESS_PATTERN, label: 'IP_ADDRESS' },\n};\n\n// ============================================================================\n// Core Functions\n// ============================================================================\n\n/**\n * Hash a value using SHA-256 and return the first 6 hex characters.\n * Used in 'hash' mode to produce a short, deterministic pseudonym.\n */\nexport function hashValue(value: string): string {\n return getCreateHash()('sha256').update(value).digest('hex').slice(0, 6);\n}\n\n/**\n * Produce the replacement string for a matched PII value.\n *\n * - 'label' mode: [LABEL]\n * - 'hash' mode: [PII:abcdef]\n * - 'remove' mode: '' (empty string)\n */\nexport function replaceMatch(match: string, label: string, mode: PiiMode): string {\n switch (mode) {\n case 'label':\n return `[${label}]`;\n case 'hash':\n return `[PII:${hashValue(match)}]`;\n case 'remove':\n return '';\n }\n}\n\n/**\n * Run all enabled builtin and custom patterns against a single string,\n * replacing every match according to the specified mode.\n */\nexport function redactString(\n value: string,\n mode: PiiMode,\n builtinPatterns: Required<PiiBuiltinPatterns>,\n customPatterns: PiiCustomPattern[],\n): string {\n let result = value;\n\n // Apply enabled builtin patterns\n for (const [key, config] of Object.entries(BUILTIN_PATTERNS)) {\n if (builtinPatterns[key as keyof PiiBuiltinPatterns]) {\n // Create a fresh regex instance to reset lastIndex\n const regex = new RegExp(config.pattern.source, config.pattern.flags);\n result = result.replace(regex, (match) => replaceMatch(match, config.label, mode));\n }\n }\n\n // Apply custom patterns\n for (const custom of customPatterns) {\n const regex = new RegExp(custom.pattern.source, custom.pattern.flags);\n result = result.replace(regex, (match) => replaceMatch(match, custom.label, mode));\n }\n\n return result;\n}\n\n/**\n * Recursively redact PII from a value. Handles strings, arrays, plain objects,\n * and passes through primitives (numbers, booleans, null, undefined) unchanged.\n */\nexport function redactValue(\n value: unknown,\n mode: PiiMode,\n builtinPatterns: Required<PiiBuiltinPatterns>,\n customPatterns: PiiCustomPattern[],\n): unknown {\n if (typeof value === 'string') {\n return redactString(value, mode, builtinPatterns, customPatterns);\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => redactValue(item, mode, builtinPatterns, customPatterns));\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n result[key] = redactValue(val, mode, builtinPatterns, customPatterns);\n }\n return result;\n }\n\n // Primitives (number, boolean, null, undefined) pass through unchanged\n return value;\n}\n\n/**\n * Main entry point: redact configured fields in a payload object.\n *\n * Only fields listed in the PiiConfig (or DEFAULT_FIELDS) are redacted.\n * Other fields are returned unchanged.\n *\n * @param payload - The data payload (e.g., session params, tool call params)\n * @param config - PII configuration from the client\n * @returns A new payload object with PII redacted from the configured fields\n */\nexport function redactPayload(\n payload: Record<string, unknown>,\n config: PiiConfig,\n): Record<string, unknown> {\n if (!config.enabled) {\n return payload;\n }\n\n const mode = config.mode ?? 'label';\n const fields = config.fields ?? DEFAULT_FIELDS;\n const builtinPatterns: Required<PiiBuiltinPatterns> = {\n ...DEFAULT_BUILTIN,\n ...config.builtinPatterns,\n };\n const customPatterns = config.customPatterns ?? [];\n\n const result: Record<string, unknown> = { ...payload };\n\n for (const field of fields) {\n if (field in result && result[field] !== undefined && result[field] !== null) {\n result[field] = redactValue(result[field], mode, builtinPatterns, customPatterns);\n }\n }\n\n return result;\n}\n","/**\n * Async-safe context variables for the Sentrial SDK.\n *\n * In Node.js (>= 18): uses AsyncLocalStorage for true per-request isolation.\n * In browsers: falls back to a simple variable (safe because browsers have no\n * concurrent request handling within a single page).\n *\n * This is the TypeScript equivalent of Python's `contextvars.ContextVar`.\n */\n\n// ============================================================================\n// Public Interface\n// ============================================================================\n\nexport interface ContextToken<T> {\n /** @internal */\n readonly _previous: T;\n}\n\nexport interface ContextVar<T> {\n /** Get the current value (returns defaultValue if unset in this context). */\n get(): T;\n /** Set a new value. Returns a token that can restore the previous value. */\n set(value: T): ContextToken<T>;\n /** Restore the value that was active before the given token's set() call. */\n reset(token: ContextToken<T>): void;\n}\n\n// ============================================================================\n// Detect AsyncLocalStorage availability\n// ============================================================================\n\nlet _AsyncLocalStorage: any = null;\n\ntry {\n // Use eval to prevent bundlers (webpack, esbuild, vite) from trying to\n // resolve node:async_hooks when building for browser targets.\n // eslint-disable-next-line no-eval\n const mod = eval('require')('node:async_hooks');\n if (mod?.AsyncLocalStorage) {\n _AsyncLocalStorage = mod.AsyncLocalStorage;\n }\n} catch {\n // Not in Node.js or async_hooks unavailable — fall back to simple vars.\n}\n\n// ============================================================================\n// Node.js Implementation (AsyncLocalStorage-backed)\n// ============================================================================\n\nclass NodeContextVar<T> implements ContextVar<T> {\n private readonly _storage: InstanceType<typeof _AsyncLocalStorage>;\n private readonly _defaultValue: T;\n\n constructor(defaultValue: T) {\n this._storage = new _AsyncLocalStorage();\n this._defaultValue = defaultValue;\n }\n\n get(): T {\n const store = this._storage.getStore();\n return store !== undefined ? (store as T) : this._defaultValue;\n }\n\n set(value: T): ContextToken<T> {\n const previous = this.get();\n this._storage.enterWith(value);\n return { _previous: previous };\n }\n\n reset(token: ContextToken<T>): void {\n this._storage.enterWith(token._previous);\n }\n}\n\n// ============================================================================\n// Browser Fallback (simple variable — no concurrency in single-tab context)\n// ============================================================================\n\nclass SimpleContextVar<T> implements ContextVar<T> {\n private _value: T;\n\n constructor(defaultValue: T) {\n this._value = defaultValue;\n }\n\n get(): T {\n return this._value;\n }\n\n set(value: T): ContextToken<T> {\n const previous = this._value;\n this._value = value;\n return { _previous: previous };\n }\n\n reset(token: ContextToken<T>): void {\n this._value = token._previous;\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create an async-safe context variable.\n *\n * Uses AsyncLocalStorage in Node.js for per-request isolation,\n * falls back to a simple variable in browsers.\n *\n * @param defaultValue - Value returned by get() when nothing has been set.\n */\nexport function createContextVar<T>(defaultValue: T): ContextVar<T> {\n if (_AsyncLocalStorage) {\n return new NodeContextVar(defaultValue);\n }\n return new SimpleContextVar(defaultValue);\n}\n","/**\n * Cost calculation utilities for various LLM providers\n */\n\nimport type { CostParams } from './types.js';\n\n// ============================================================================\n// Pricing Data (per 1M tokens) - Updated Jan 2026\n// ============================================================================\n\nconst OPENAI_PRICING: Record<string, { input: number; output: number }> = {\n 'gpt-5.2': { input: 5.0, output: 15.0 },\n 'gpt-5': { input: 4.0, output: 12.0 },\n 'gpt-4.1': { input: 2.0, output: 8.0 },\n 'gpt-4.1-mini': { input: 0.4, output: 1.6 },\n 'gpt-4.1-nano': { input: 0.1, output: 0.4 },\n 'gpt-4o': { input: 2.5, output: 10.0 },\n 'gpt-4o-mini': { input: 0.15, output: 0.6 },\n 'gpt-4-turbo': { input: 10.0, output: 30.0 },\n 'gpt-4': { input: 30.0, output: 60.0 },\n 'gpt-3.5-turbo': { input: 0.5, output: 1.5 },\n 'o4-mini': { input: 1.1, output: 4.4 },\n 'o3': { input: 10.0, output: 40.0 },\n 'o3-mini': { input: 1.1, output: 4.4 },\n 'o3-pro': { input: 20.0, output: 80.0 },\n 'o1': { input: 15.0, output: 60.0 },\n 'o1-preview': { input: 15.0, output: 60.0 },\n 'o1-mini': { input: 3.0, output: 12.0 },\n};\n\nconst ANTHROPIC_PRICING: Record<string, { input: number; output: number }> = {\n 'claude-opus-4': { input: 15.0, output: 75.0 },\n 'claude-sonnet-4': { input: 3.0, output: 15.0 },\n 'claude-4.5-opus': { input: 20.0, output: 100.0 },\n 'claude-4.5-sonnet': { input: 4.0, output: 20.0 },\n 'claude-4-opus': { input: 18.0, output: 90.0 },\n 'claude-4-sonnet': { input: 3.5, output: 17.5 },\n 'claude-3-7-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-5-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-5-haiku': { input: 0.8, output: 4.0 },\n 'claude-3-opus': { input: 15.0, output: 75.0 },\n 'claude-3-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-haiku': { input: 0.25, output: 1.25 },\n};\n\nconst GOOGLE_PRICING: Record<string, { input: number; output: number }> = {\n // Gemini 3 - Preview (Jan 2026)\n 'gemini-3-pro': { input: 2.0, output: 12.0 },\n 'gemini-3-flash': { input: 0.5, output: 3.0 },\n // Gemini 2.5\n 'gemini-2.5-pro': { input: 1.25, output: 5.0 },\n 'gemini-2.5-flash': { input: 0.15, output: 0.6 },\n // Gemini 2.0\n 'gemini-2.0-flash': { input: 0.1, output: 0.4 },\n 'gemini-2.0-flash-lite': { input: 0.075, output: 0.3 },\n // Gemini 1.5\n 'gemini-1.5-pro': { input: 1.25, output: 5.0 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.3 },\n // Gemini 1.0\n 'gemini-1.0-pro': { input: 0.5, output: 1.5 },\n};\n\n// ============================================================================\n// Helper Function\n// ============================================================================\n\nfunction findModelKey(\n model: string,\n pricing: Record<string, { input: number; output: number }>\n): string | null {\n // Sort by length descending so longer (more specific) keys match first.\n // e.g. \"o3-mini\" must match before \"o3\", \"gpt-4-turbo\" before \"gpt-4\".\n const keys = Object.keys(pricing).sort((a, b) => b.length - a.length);\n for (const key of keys) {\n if (model.startsWith(key)) {\n return key;\n }\n }\n return null;\n}\n\nfunction calculateCost(\n inputTokens: number,\n outputTokens: number,\n rates: { input: number; output: number }\n): number {\n const inputCost = (inputTokens / 1_000_000) * rates.input;\n const outputCost = (outputTokens / 1_000_000) * rates.output;\n return inputCost + outputCost;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Calculate cost for OpenAI API calls\n *\n * @param params - Cost calculation parameters\n * @returns Estimated cost in USD\n *\n * @example\n * ```ts\n * const cost = calculateOpenAICost({\n * model: 'gpt-4o',\n * inputTokens: 1000,\n * outputTokens: 500,\n * });\n * console.log(`Cost: $${cost.toFixed(4)}`);\n * ```\n */\nexport function calculateOpenAICost(params: CostParams): number {\n const { model, inputTokens, outputTokens } = params;\n const modelKey = findModelKey(model, OPENAI_PRICING) ?? 'gpt-4';\n return calculateCost(inputTokens, outputTokens, OPENAI_PRICING[modelKey]!);\n}\n\n/**\n * Calculate cost for Anthropic API calls\n *\n * @param params - Cost calculation parameters\n * @returns Estimated cost in USD\n *\n * @example\n * ```ts\n * const cost = calculateAnthropicCost({\n * model: 'claude-3-5-sonnet',\n * inputTokens: 1000,\n * outputTokens: 500,\n * });\n * console.log(`Cost: $${cost.toFixed(4)}`);\n * ```\n */\nexport function calculateAnthropicCost(params: CostParams): number {\n const { model, inputTokens, outputTokens } = params;\n const modelKey = findModelKey(model, ANTHROPIC_PRICING) ?? 'claude-3-sonnet';\n return calculateCost(inputTokens, outputTokens, ANTHROPIC_PRICING[modelKey]!);\n}\n\n/**\n * Calculate cost for Google/Gemini API calls\n *\n * @param params - Cost calculation parameters\n * @returns Estimated cost in USD\n *\n * @example\n * ```ts\n * const cost = calculateGoogleCost({\n * model: 'gemini-2.0-flash',\n * inputTokens: 1000,\n * outputTokens: 500,\n * });\n * console.log(`Cost: $${cost.toFixed(4)}`);\n * ```\n */\nexport function calculateGoogleCost(params: CostParams): number {\n const { model, inputTokens, outputTokens } = params;\n const modelKey = findModelKey(model, GOOGLE_PRICING) ?? 'gemini-2.0-flash';\n return calculateCost(inputTokens, outputTokens, GOOGLE_PRICING[modelKey]!);\n}\n","/**\n * Sentrial LLM Wrappers - Auto-instrument LLM provider SDKs\n *\n * These wrappers automatically track all LLM calls with:\n * - Input messages\n * - Output responses\n * - Token counts\n * - Cost estimation\n * - Latency\n *\n * @example OpenAI\n * ```ts\n * import OpenAI from 'openai';\n * import { wrapOpenAI, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const openai = wrapOpenAI(new OpenAI());\n *\n * // All calls are now automatically tracked!\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n *\n * @example Anthropic\n * ```ts\n * import Anthropic from '@anthropic-ai/sdk';\n * import { wrapAnthropic, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const anthropic = wrapAnthropic(new Anthropic());\n *\n * const response = await anthropic.messages.create({\n * model: 'claude-3-5-sonnet-20241022',\n * max_tokens: 1024,\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n */\n\nimport { SentrialClient } from './client.js';\nimport { calculateOpenAICost, calculateAnthropicCost, calculateGoogleCost } from './cost.js';\nimport { createContextVar, type ContextToken } from './async-context.js';\n\n// ============================================================================\n// Session Context (async-safe via AsyncLocalStorage in Node.js,\n// simple variable fallback in browsers)\n// ============================================================================\n\nconst _currentSessionId = createContextVar<string | null>(null);\nconst _currentClient = createContextVar<SentrialClient | null>(null);\nlet _defaultClient: SentrialClient | null = null;\n\n/** Tokens for restoring previous session context (used internally by decorators). */\nexport interface SessionContextTokens {\n _sessionToken: ContextToken<string | null>;\n _clientToken: ContextToken<SentrialClient | null>;\n}\n\n/**\n * Set the current session context for auto-tracking.\n *\n * Call this before making LLM calls to associate them with a session.\n * This is automatically called when using withSession() or decorators.\n *\n * @param sessionId - The session ID to track LLM calls under\n * @param client - Optional Sentrial client (uses default if not provided)\n */\nexport function setSessionContext(sessionId: string, client?: SentrialClient): void {\n _currentSessionId.set(sessionId);\n if (client) {\n _currentClient.set(client);\n }\n}\n\n/**\n * Clear the current session context.\n */\nexport function clearSessionContext(): void {\n _currentSessionId.set(null);\n _currentClient.set(null);\n}\n\n/**\n * Get the current session ID.\n */\nexport function getSessionContext(): string | null {\n return _currentSessionId.get();\n}\n\n/**\n * Set the default client for wrappers.\n */\nexport function setDefaultClient(client: SentrialClient): void {\n _defaultClient = client;\n}\n\n/**\n * Set session context and return tokens for restoring the previous values.\n * @internal Used by decorators for proper nesting support.\n */\nexport function _setSessionContextWithTokens(\n sessionId: string,\n client?: SentrialClient,\n): SessionContextTokens {\n const _sessionToken = _currentSessionId.set(sessionId);\n const _clientToken = client\n ? _currentClient.set(client)\n : _currentClient.set(_currentClient.get()); // no-op token preserving current\n return { _sessionToken, _clientToken };\n}\n\n/**\n * Restore session context from tokens returned by _setSessionContextWithTokens.\n * @internal Used by decorators for proper nesting support.\n */\nexport function _restoreSessionContext(tokens: SessionContextTokens): void {\n _currentSessionId.reset(tokens._sessionToken);\n _currentClient.reset(tokens._clientToken);\n}\n\nfunction getTrackingClient(): SentrialClient | null {\n return _currentClient.get() ?? _defaultClient;\n}\n\n// ============================================================================\n// OpenAI Wrapper\n// ============================================================================\n\n/**\n * Wrap an OpenAI client to automatically track all LLM calls.\n *\n * @param client - OpenAI client instance\n * @param options - Wrapper options\n * @returns The same client, now with auto-tracking enabled\n *\n * @example\n * ```ts\n * import OpenAI from 'openai';\n * import { wrapOpenAI, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n * const openai = wrapOpenAI(new OpenAI());\n *\n * // Now use client normally - all calls are tracked!\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello' }],\n * });\n * ```\n */\nexport function wrapOpenAI<T extends object>(\n client: T,\n options: { trackWithoutSession?: boolean } = {}\n): T {\n const { trackWithoutSession = false } = options;\n\n // Access chat.completions.create\n const chat = (client as any).chat;\n if (!chat?.completions?.create) {\n console.warn('Sentrial: OpenAI client does not have chat.completions.create');\n return client;\n }\n\n const originalCreate = chat.completions.create.bind(chat.completions);\n\n chat.completions.create = async function (...args: any[]) {\n const startTime = Date.now();\n const params = args[0] ?? {};\n const messages = params.messages ?? [];\n const model = params.model ?? 'unknown';\n const isStreaming = params.stream === true;\n\n // Auto-inject stream_options.include_usage so streaming calls report token counts\n if (isStreaming && !params.stream_options?.include_usage) {\n args[0] = { ...params, stream_options: { ...params.stream_options, include_usage: true } };\n }\n\n try {\n const response = await originalCreate(...args);\n\n if (isStreaming) {\n return wrapOpenAIStream(response, { startTime, messages, model, trackWithoutSession });\n }\n\n const durationMs = Date.now() - startTime;\n\n // Extract token usage\n const promptTokens = response.usage?.prompt_tokens ?? 0;\n const completionTokens = response.usage?.completion_tokens ?? 0;\n const totalTokens = response.usage?.total_tokens ?? 0;\n\n // Extract output\n let outputContent = '';\n if (response.choices?.[0]?.message?.content) {\n outputContent = response.choices[0].message.content;\n }\n\n // Calculate cost\n const cost = calculateOpenAICost({ model, inputTokens: promptTokens, outputTokens: completionTokens });\n\n // Track the call\n trackLLMCall({\n provider: 'openai',\n model,\n messages,\n output: outputContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession,\n });\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n trackLLMError({\n provider: 'openai',\n model,\n messages,\n error: error as Error,\n durationMs,\n trackWithoutSession,\n });\n throw error;\n }\n };\n\n return client;\n}\n\n// ============================================================================\n// Anthropic Wrapper\n// ============================================================================\n\n/**\n * Wrap an Anthropic client to automatically track all LLM calls.\n *\n * @param client - Anthropic client instance\n * @param options - Wrapper options\n * @returns The same client, now with auto-tracking enabled\n *\n * @example\n * ```ts\n * import Anthropic from '@anthropic-ai/sdk';\n * import { wrapAnthropic, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n * const anthropic = wrapAnthropic(new Anthropic());\n *\n * const response = await anthropic.messages.create({\n * model: 'claude-3-5-sonnet-20241022',\n * max_tokens: 1024,\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n */\nexport function wrapAnthropic<T extends object>(\n client: T,\n options: { trackWithoutSession?: boolean } = {}\n): T {\n const { trackWithoutSession = false } = options;\n\n // Access messages.create\n const messages = (client as any).messages;\n if (!messages?.create) {\n console.warn('Sentrial: Anthropic client does not have messages.create');\n return client;\n }\n\n const originalCreate = messages.create.bind(messages);\n\n messages.create = async function (...args: any[]) {\n const startTime = Date.now();\n const params = args[0] ?? {};\n const inputMessages = params.messages ?? [];\n const model = params.model ?? 'unknown';\n const system = params.system ?? '';\n const isStreaming = params.stream === true;\n\n try {\n const response = await originalCreate(...args);\n\n if (isStreaming) {\n return wrapAnthropicStream(response, {\n startTime, messages: inputMessages, model, system, trackWithoutSession,\n });\n }\n\n const durationMs = Date.now() - startTime;\n\n // Anthropic uses input_tokens/output_tokens\n const promptTokens = response.usage?.input_tokens ?? 0;\n const completionTokens = response.usage?.output_tokens ?? 0;\n const totalTokens = promptTokens + completionTokens;\n\n // Extract output\n let outputContent = '';\n if (response.content) {\n for (const block of response.content) {\n if (block.type === 'text') {\n outputContent += block.text;\n }\n }\n }\n\n const cost = calculateAnthropicCost({ model, inputTokens: promptTokens, outputTokens: completionTokens });\n\n // Include system prompt in messages for tracking\n const fullMessages = system\n ? [{ role: 'system', content: system }, ...inputMessages]\n : inputMessages;\n\n trackLLMCall({\n provider: 'anthropic',\n model,\n messages: fullMessages,\n output: outputContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession,\n });\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n trackLLMError({\n provider: 'anthropic',\n model,\n messages: inputMessages,\n error: error as Error,\n durationMs,\n trackWithoutSession,\n });\n throw error;\n }\n };\n\n return client;\n}\n\n// ============================================================================\n// Google/Gemini Wrapper\n// ============================================================================\n\n/**\n * Wrap a Google GenerativeModel to automatically track all LLM calls.\n *\n * @param model - Google GenerativeModel instance\n * @param options - Wrapper options\n * @returns The same model, now with auto-tracking enabled\n *\n * @example\n * ```ts\n * import { GoogleGenerativeAI } from '@google/generative-ai';\n * import { wrapGoogle, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);\n * const model = wrapGoogle(genAI.getGenerativeModel({ model: 'gemini-2.0-flash' }));\n *\n * const response = await model.generateContent('Hello!');\n * ```\n */\nexport function wrapGoogle<T extends object>(\n model: T,\n options: { trackWithoutSession?: boolean } = {}\n): T {\n const { trackWithoutSession = false } = options;\n\n // Access generateContent\n const originalGenerate = (model as any).generateContent;\n if (!originalGenerate) {\n console.warn('Sentrial: Google model does not have generateContent');\n return model;\n }\n\n (model as any).generateContent = async function (...args: any[]) {\n const startTime = Date.now();\n const contents = args[0];\n const modelName = (model as any).model ?? 'gemini-unknown';\n\n // Convert contents to messages format\n const messages = googleContentsToMessages(contents);\n\n try {\n const response = await originalGenerate.apply(model, args);\n const durationMs = Date.now() - startTime;\n\n // Extract usage — generateContent returns { response: GenerateContentResponse }\n // usageMetadata lives on the inner .response, or directly on the result in newer SDK versions\n let promptTokens = 0;\n let completionTokens = 0;\n const usageMeta = response.response?.usageMetadata ?? response.usageMetadata;\n if (usageMeta) {\n promptTokens = usageMeta.promptTokenCount ?? 0;\n completionTokens = usageMeta.candidatesTokenCount ?? 0;\n }\n const totalTokens = promptTokens + completionTokens;\n\n // Extract output — .text() can throw if response was safety-filtered\n let outputContent = '';\n try {\n outputContent = response.response?.text?.() ?? response.text?.() ?? '';\n } catch {\n // text() throws if no text content (e.g. safety filter)\n }\n\n const cost = calculateGoogleCost({ model: modelName, inputTokens: promptTokens, outputTokens: completionTokens });\n\n trackLLMCall({\n provider: 'google',\n model: modelName,\n messages,\n output: outputContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession,\n });\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n trackLLMError({\n provider: 'google',\n model: modelName,\n messages,\n error: error as Error,\n durationMs,\n trackWithoutSession,\n });\n throw error;\n }\n };\n\n return model;\n}\n\nfunction googleContentsToMessages(contents: unknown): Array<{ role: string; content: string }> {\n if (typeof contents === 'string') {\n return [{ role: 'user', content: contents }];\n }\n if (Array.isArray(contents)) {\n return contents.map((item) => {\n if (typeof item === 'string') {\n return { role: 'user', content: item };\n }\n if (item && typeof item === 'object') {\n return { role: (item as any).role ?? 'user', content: String((item as any).content ?? item) };\n }\n return { role: 'user', content: String(item) };\n });\n }\n return [{ role: 'user', content: String(contents) }];\n}\n\n// ============================================================================\n// Auto-detect Wrapper\n// ============================================================================\n\n/**\n * Auto-detect and wrap any supported LLM client.\n *\n * @param client - Any supported LLM client (OpenAI, Anthropic, Google)\n * @param provider - Optional provider hint\n * @returns The wrapped client\n *\n * @example\n * ```ts\n * import OpenAI from 'openai';\n * import { wrapLLM } from '@sentrial/sdk';\n *\n * const client = wrapLLM(new OpenAI()); // Auto-detected as OpenAI\n * ```\n */\nexport function wrapLLM<T extends object>(\n client: T,\n provider?: 'openai' | 'anthropic' | 'google'\n): T {\n // Try to auto-detect based on client shape\n if (provider === 'openai' || (client as any).chat?.completions?.create) {\n return wrapOpenAI(client);\n }\n if (provider === 'anthropic' || (client as any).messages?.create) {\n return wrapAnthropic(client);\n }\n if (provider === 'google' || (client as any).generateContent) {\n return wrapGoogle(client);\n }\n\n console.warn('Sentrial: Unknown LLM client type. No auto-tracking applied.');\n return client;\n}\n\n// ============================================================================\n// Stream Wrappers\n// ============================================================================\n\ninterface StreamContext {\n startTime: number;\n messages: unknown[];\n model: string;\n trackWithoutSession: boolean;\n}\n\ninterface AnthropicStreamContext extends StreamContext {\n system: string;\n}\n\n/**\n * Wrap an OpenAI streaming response to track after completion.\n * Preserves the original stream object shape while intercepting the async iterator.\n */\nfunction wrapOpenAIStream(stream: any, ctx: StreamContext): any {\n let fullContent = '';\n let usage: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number } | null = null;\n let tracked = false;\n\n const originalIterator = stream[Symbol.asyncIterator]?.bind(stream);\n if (!originalIterator) return stream; // Not an async iterable, return as-is\n\n const trackResult = () => {\n if (tracked) return;\n tracked = true;\n const durationMs = Date.now() - ctx.startTime;\n const promptTokens = usage?.prompt_tokens ?? 0;\n const completionTokens = usage?.completion_tokens ?? 0;\n const totalTokens = usage?.total_tokens ?? (promptTokens + completionTokens);\n const cost = calculateOpenAICost({ model: ctx.model, inputTokens: promptTokens, outputTokens: completionTokens });\n\n trackLLMCall({\n provider: 'openai',\n model: ctx.model,\n messages: ctx.messages,\n output: fullContent,\n promptTokens,\n completionTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession: ctx.trackWithoutSession,\n });\n };\n\n // Use a Proxy to transparently delegate all property access to the original\n // stream while only overriding [Symbol.asyncIterator]. This avoids the\n // Object.create+Object.assign approach which can't copy JS private fields\n // (e.g. OpenAI's Stream.#client) — calling .tee() on a shallow copy crashes.\n return new Proxy(stream, {\n get(target, prop, receiver) {\n if (prop === Symbol.asyncIterator) {\n return function () {\n const iter = originalIterator();\n return {\n async next() {\n const result = await iter.next();\n if (!result.done) {\n const chunk = result.value;\n const delta = chunk.choices?.[0]?.delta?.content;\n if (delta) fullContent += delta;\n if (chunk.usage) usage = chunk.usage;\n } else {\n trackResult();\n }\n return result;\n },\n async return(value?: any) {\n trackResult();\n return iter.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(error?: any) {\n return iter.throw?.(error) ?? { done: true, value: undefined };\n },\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\n/**\n * Wrap an Anthropic streaming response to track after completion.\n * Anthropic streams yield events: content_block_delta, message_start, message_delta, etc.\n */\nfunction wrapAnthropicStream(stream: any, ctx: AnthropicStreamContext): any {\n let fullContent = '';\n let inputTokens = 0;\n let outputTokens = 0;\n let tracked = false;\n\n const originalIterator = stream[Symbol.asyncIterator]?.bind(stream);\n if (!originalIterator) return stream;\n\n const trackResult = () => {\n if (tracked) return;\n tracked = true;\n const durationMs = Date.now() - ctx.startTime;\n const totalTokens = inputTokens + outputTokens;\n const cost = calculateAnthropicCost({ model: ctx.model, inputTokens, outputTokens });\n\n const fullMessages = ctx.system\n ? [{ role: 'system', content: ctx.system }, ...ctx.messages]\n : ctx.messages;\n\n trackLLMCall({\n provider: 'anthropic',\n model: ctx.model,\n messages: fullMessages,\n output: fullContent,\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens,\n cost,\n durationMs,\n trackWithoutSession: ctx.trackWithoutSession,\n });\n };\n\n return new Proxy(stream, {\n get(target, prop, receiver) {\n if (prop === Symbol.asyncIterator) {\n return function () {\n const iter = originalIterator();\n return {\n async next() {\n const result = await iter.next();\n if (!result.done) {\n const event = result.value;\n if (event.type === 'content_block_delta' && event.delta?.text) {\n fullContent += event.delta.text;\n }\n if (event.type === 'message_start' && event.message?.usage) {\n inputTokens = event.message.usage.input_tokens ?? 0;\n }\n if (event.type === 'message_delta' && event.usage) {\n outputTokens = event.usage.output_tokens ?? 0;\n }\n } else {\n trackResult();\n }\n return result;\n },\n async return(value?: any) {\n trackResult();\n return iter.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(error?: any) {\n return iter.throw?.(error) ?? { done: true, value: undefined };\n },\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\n// ============================================================================\n// Common Tracking Functions\n// ============================================================================\n\ninterface TrackLLMCallParams {\n provider: string;\n model: string;\n messages: unknown[];\n output: string;\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n cost: number;\n durationMs: number;\n trackWithoutSession: boolean;\n}\n\nfunction trackLLMCall(params: TrackLLMCallParams): void {\n const client = getTrackingClient();\n if (!client) return;\n\n const sessionId = _currentSessionId.get();\n if (!sessionId && !params.trackWithoutSession) {\n return;\n }\n\n if (sessionId) {\n // Track under the existing session\n client.trackToolCall({\n sessionId,\n toolName: `llm:${params.provider}:${params.model}`,\n toolInput: {\n messages: params.messages,\n model: params.model,\n provider: params.provider,\n },\n toolOutput: {\n content: params.output,\n tokens: {\n prompt: params.promptTokens,\n completion: params.completionTokens,\n total: params.totalTokens,\n },\n cost_usd: params.cost,\n },\n reasoning: `LLM call to ${params.provider} ${params.model}`,\n estimatedCost: params.cost,\n tokenCount: params.totalTokens,\n metadata: {\n provider: params.provider,\n model: params.model,\n duration_ms: params.durationMs,\n prompt_tokens: params.promptTokens,\n completion_tokens: params.completionTokens,\n },\n }).catch((err) => {\n console.warn('Sentrial: Failed to track LLM call:', err.message);\n });\n } else if (params.trackWithoutSession) {\n // Create an ad-hoc session, track the call, then auto-complete it\n client.createSession({\n name: `LLM: ${params.provider}/${params.model}`,\n agentName: `${params.provider}-wrapper`,\n userId: 'anonymous',\n }).then((sid) => {\n if (!sid) return;\n return client.trackToolCall({\n sessionId: sid,\n toolName: `llm:${params.provider}:${params.model}`,\n toolInput: {\n messages: params.messages,\n model: params.model,\n provider: params.provider,\n },\n toolOutput: {\n content: params.output,\n tokens: {\n prompt: params.promptTokens,\n completion: params.completionTokens,\n total: params.totalTokens,\n },\n cost_usd: params.cost,\n },\n estimatedCost: params.cost,\n tokenCount: params.totalTokens,\n metadata: {\n provider: params.provider,\n model: params.model,\n duration_ms: params.durationMs,\n },\n }).then(() => {\n // Auto-complete so the session doesn't dangle\n return client.completeSession({\n sessionId: sid,\n success: true,\n estimatedCost: params.cost,\n promptTokens: params.promptTokens,\n completionTokens: params.completionTokens,\n totalTokens: params.totalTokens,\n durationMs: params.durationMs,\n });\n });\n }).catch((err) => {\n console.warn('Sentrial: Failed to track standalone LLM call:', err.message);\n });\n }\n}\n\ninterface TrackLLMErrorParams {\n provider: string;\n model: string;\n messages: unknown[];\n error: Error;\n durationMs: number;\n trackWithoutSession: boolean;\n}\n\nfunction trackLLMError(params: TrackLLMErrorParams): void {\n const client = getTrackingClient();\n if (!client) return;\n\n const sessionId = _currentSessionId.get();\n if (!sessionId && !params.trackWithoutSession) {\n return;\n }\n\n if (!sessionId) return; // trackWithoutSession errors are not worth an ad-hoc session\n\n client.trackError({\n sessionId,\n errorMessage: params.error.message,\n errorType: params.error.name,\n toolName: `llm:${params.provider}:${params.model}`,\n metadata: {\n provider: params.provider,\n model: params.model,\n duration_ms: params.durationMs,\n },\n }).catch((err) => {\n console.warn('Sentrial: Failed to track LLM error:', err.message);\n });\n}\n","/**\n * EventBatcher — fire-and-forget event queue with periodic flushing.\n *\n * Buffers tracking events (tool calls, decisions, errors, generic events)\n * and flushes them in batches to reduce HTTP overhead. Session lifecycle\n * calls (createSession, completeSession) bypass the batcher entirely\n * because they need synchronous responses.\n *\n * Flush triggers:\n * 1. Timer — every `flushIntervalMs` (default 1 000 ms)\n * 2. Queue size — when queue reaches `flushThreshold` (default 10)\n * 3. Manual — `flush()` or `shutdown()`\n * 4. Process exit — `beforeExit` handler\n *\n * Back-pressure: queue is capped at `maxQueueSize` (default 1 000).\n * When full, oldest events are dropped and a warning is logged.\n */\n\nexport interface BatcherConfig {\n /** Whether batching is enabled (default: false — all calls are immediate) */\n enabled?: boolean;\n /** Milliseconds between automatic flushes (default: 1000) */\n flushIntervalMs?: number;\n /** Flush when the queue reaches this many items (default: 10) */\n flushThreshold?: number;\n /** Maximum queue size before dropping oldest events (default: 1000) */\n maxQueueSize?: number;\n}\n\n/** Signature for the function that actually sends an event to the API. */\nexport type SendFn = (\n method: string,\n url: string,\n body: unknown,\n) => Promise<unknown>;\n\nexport class EventBatcher {\n private readonly queue: Array<{ method: string; url: string; body: unknown }> = [];\n private readonly flushIntervalMs: number;\n private readonly flushThreshold: number;\n private readonly maxQueueSize: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n private sendFn: SendFn;\n private flushing = false;\n private shutdownCalled = false;\n private readonly exitHandler: () => void;\n\n constructor(sendFn: SendFn, config: BatcherConfig = {}) {\n this.sendFn = sendFn;\n this.flushIntervalMs = config.flushIntervalMs ?? 1_000;\n this.flushThreshold = config.flushThreshold ?? 10;\n this.maxQueueSize = config.maxQueueSize ?? 1_000;\n\n // Start periodic flush timer\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n\n // Don't hold the process open just for the flush timer\n if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {\n (this.timer as NodeJS.Timeout).unref();\n }\n\n // Register process exit handler for best-effort final flush\n this.exitHandler = () => {\n void this.shutdown();\n };\n\n if (typeof process !== 'undefined' && process.on) {\n process.on('beforeExit', this.exitHandler);\n }\n }\n\n /**\n * Enqueue an event for batched delivery.\n *\n * If the queue hits `flushThreshold`, an automatic flush is triggered.\n * If the queue is full (`maxQueueSize`), the oldest event is dropped.\n */\n enqueue(method: string, url: string, body: unknown): void {\n if (this.shutdownCalled) return;\n\n // Back-pressure: drop oldest when at capacity\n if (this.queue.length >= this.maxQueueSize) {\n this.queue.shift();\n if (typeof console !== 'undefined') {\n console.warn(\n `Sentrial: Event queue full (${this.maxQueueSize}), dropping oldest event`,\n );\n }\n }\n\n this.queue.push({ method, url, body });\n\n // Flush if we hit the threshold\n if (this.queue.length >= this.flushThreshold) {\n void this.flush();\n }\n }\n\n /**\n * Flush all queued events to the API.\n *\n * Drains the queue and fires all requests in parallel. Safe to call\n * concurrently — only one flush runs at a time.\n */\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n\n this.flushing = true;\n\n // Drain the queue atomically\n const batch = this.queue.splice(0, this.queue.length);\n\n try {\n await Promise.all(\n batch.map((event) =>\n this.sendFn(event.method, event.url, event.body).catch((err) => {\n // Individual event failures shouldn't crash the batch\n if (typeof console !== 'undefined') {\n console.warn('Sentrial: Batched event failed:', err);\n }\n }),\n ),\n );\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Stop the batcher: clear the timer, flush remaining events, remove exit handler.\n */\n async shutdown(): Promise<void> {\n if (this.shutdownCalled) return;\n this.shutdownCalled = true;\n\n // Clear periodic timer\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // Remove exit handler\n if (typeof process !== 'undefined' && process.removeListener) {\n process.removeListener('beforeExit', this.exitHandler);\n }\n\n // Final flush\n this.flushing = false; // Reset so flush() will run\n await this.flush();\n }\n\n /** Number of events currently queued. */\n get size(): number {\n return this.queue.length;\n }\n}\n","/**\n * Type definitions for the Sentrial TypeScript SDK\n */\n\nimport type { PiiConfig } from './redact.js';\nimport type { BatcherConfig } from './batcher.js';\n\n// ============================================================================\n// Enums\n// ============================================================================\n\n/**\n * Event types that can be tracked in a session\n */\nexport enum EventType {\n TOOL_CALL = 'tool_call',\n LLM_DECISION = 'llm_decision',\n STATE_CHANGE = 'state_change',\n ERROR = 'error',\n}\n\n/**\n * Session status values\n */\nexport type SessionStatus = 'running' | 'completed' | 'failed' | 'cancelled';\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/**\n * Configuration options for SentrialClient\n */\nexport interface SentrialClientConfig {\n /** API key for authentication (defaults to SENTRIAL_API_KEY env var) */\n apiKey?: string;\n /** URL of the Sentrial API server (defaults to SENTRIAL_API_URL env var or production) */\n apiUrl?: string;\n /**\n * If true (default), SDK errors are logged but won't crash your app.\n * Set to false during development to see full errors.\n */\n failSilently?: boolean;\n /**\n * PII redaction configuration.\n * - Pass a PiiConfig object for full control.\n * - Pass `true` to fetch the org's PII config from the server automatically.\n */\n pii?: PiiConfig | boolean;\n /**\n * Event batching configuration.\n * When enabled, fire-and-forget tracking calls (trackToolCall, trackDecision,\n * trackError, trackEvent) are queued and flushed periodically instead of\n * being sent immediately. Session lifecycle calls (createSession,\n * completeSession) always bypass the batcher.\n */\n batching?: BatcherConfig;\n}\n\n// ============================================================================\n// Session Types\n// ============================================================================\n\n/**\n * Parameters for creating a new session\n */\nexport interface CreateSessionParams {\n /** Name of the session */\n name: string;\n /** Identifier for the agent type (used for grouping) */\n agentName: string;\n /** External user ID (for grouping sessions by end-user) */\n userId: string;\n /** Optional parent session ID for multi-agent hierarchies */\n parentSessionId?: string;\n /** Optional conversation ID to group related sessions into a thread */\n convoId?: string;\n /** Optional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for completing a session\n */\nexport interface CompleteSessionParams {\n /** Session ID */\n sessionId: string;\n /** Whether the session successfully completed its goal (default: true) */\n success?: boolean;\n /** If success=false, why did it fail? */\n failureReason?: string;\n /** Total estimated cost in USD for this session */\n estimatedCost?: number;\n /** Custom metrics (e.g., { customer_satisfaction: 4.5, steps_taken: 3 }) */\n customMetrics?: Record<string, number>;\n /** Duration in milliseconds (auto-calculated if not provided) */\n durationMs?: number;\n /** Number of prompt/input tokens used */\n promptTokens?: number;\n /** Number of completion/output tokens used */\n completionTokens?: number;\n /** Total tokens used (prompt + completion) */\n totalTokens?: number;\n /** The user's original query/input for this session */\n userInput?: string;\n /** The final assistant response for this session */\n assistantOutput?: string;\n /** Alias for assistantOutput - the final output text */\n output?: string;\n}\n\n/**\n * Session data returned from the API\n */\nexport interface Session {\n id: string;\n agentId?: string;\n parentSessionId?: string;\n convoId?: string;\n name: string;\n agentName: string;\n userId: string;\n status: SessionStatus;\n totalEvents: number;\n\n // User interaction tracking\n userInput?: string;\n assistantOutput?: string;\n\n // Performance tracking\n success?: boolean;\n failureReason?: string;\n estimatedCost?: number;\n customMetrics?: Record<string, unknown>;\n score?: number;\n metricScores?: Record<string, unknown>;\n metricReasonings?: Record<string, string>;\n\n // Token tracking\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n\n // Timestamps\n durationMs?: number;\n metadata?: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n completedAt?: Date;\n}\n\n// ============================================================================\n// Event Types\n// ============================================================================\n\n/**\n * Parameters for tracking a tool call event\n */\nexport interface TrackToolCallParams {\n /** Session ID */\n sessionId: string;\n /** Name of the tool */\n toolName: string;\n /** Tool input data */\n toolInput: Record<string, unknown>;\n /** Tool output data */\n toolOutput: Record<string, unknown>;\n /** Optional reasoning for why this tool was called */\n reasoning?: string;\n /** Estimated cost in USD for this tool call */\n estimatedCost?: number;\n /** Error details if the tool failed */\n toolError?: Record<string, unknown>;\n /** Number of tokens used by this tool call (for LLM-based tools) */\n tokenCount?: number;\n /** OpenTelemetry trace ID for distributed tracing */\n traceId?: string;\n /** OpenTelemetry span ID for distributed tracing */\n spanId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for tracking an LLM decision event\n */\nexport interface TrackDecisionParams {\n /** Session ID */\n sessionId: string;\n /** Decision reasoning */\n reasoning: string;\n /** Alternative options considered */\n alternatives?: string[];\n /** Confidence score (0.0 to 1.0) */\n confidence?: number;\n /** Estimated cost in USD for this decision */\n estimatedCost?: number;\n /** Number of tokens used */\n tokenCount?: number;\n /** OpenTelemetry trace ID for distributed tracing */\n traceId?: string;\n /** OpenTelemetry span ID for distributed tracing */\n spanId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for tracking an error event\n */\nexport interface TrackErrorParams {\n /** Session ID */\n sessionId: string;\n /** Error message */\n errorMessage: string;\n /** Type of error (e.g., \"ValueError\", \"APIError\") */\n errorType?: string;\n /** Name of the tool that caused the error (if applicable) */\n toolName?: string;\n /** Stack trace for debugging */\n stackTrace?: string;\n /** OpenTelemetry trace ID for distributed tracing */\n traceId?: string;\n /** OpenTelemetry span ID for distributed tracing */\n spanId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Event data returned from the API\n */\nexport interface Event {\n id: string;\n sessionId: string;\n eventType: EventType;\n timestamp: Date;\n sequenceNumber: number;\n\n // Tool call data\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: Record<string, unknown>;\n toolError?: Record<string, unknown>;\n\n // LLM decision data\n reasoning?: string;\n alternativesConsidered?: string[];\n confidence?: number;\n\n // State tracking\n stateBefore?: Record<string, unknown>;\n stateAfter?: Record<string, unknown>;\n stateDiff?: Record<string, unknown>;\n\n // Performance tracking\n estimatedCost?: number;\n tokenCount?: number;\n\n // Metadata\n metadata?: Record<string, unknown>;\n traceId?: string;\n spanId?: string;\n}\n\n// ============================================================================\n// Interaction Types (Simple API)\n// ============================================================================\n\n/**\n * Parameters for beginning an interaction\n */\nexport interface BeginParams {\n /** External user ID for grouping sessions */\n userId: string;\n /** Event type/name (e.g., \"chat_message\", \"search_query\") */\n event: string;\n /** Optional input data for the interaction */\n input?: string;\n /** Optional custom event ID (auto-generated UUID if not provided) */\n eventId?: string;\n /** Optional conversation ID to group related interactions */\n convoId?: string;\n /** Optional additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for finishing an interaction\n */\nexport interface FinishParams {\n /** Output/response from the interaction */\n output?: string;\n /** Whether the interaction succeeded (default: true) */\n success?: boolean;\n /** Reason for failure if success=false */\n failureReason?: string;\n /** Total estimated cost in USD */\n estimatedCost?: number;\n /** Custom metrics */\n customMetrics?: Record<string, number>;\n /** Number of prompt/input tokens used */\n promptTokens?: number;\n /** Number of completion/output tokens used */\n completionTokens?: number;\n /** Total tokens used */\n totalTokens?: number;\n /** Duration in milliseconds (auto-calculated from begin/finish if not provided) */\n durationMs?: number;\n}\n\n// ============================================================================\n// Cost Calculation Types\n// ============================================================================\n\n/**\n * Parameters for cost calculation\n */\nexport interface CostParams {\n /** Model name (e.g., \"gpt-4\", \"claude-3-sonnet\") */\n model: string;\n /** Number of input tokens */\n inputTokens: number;\n /** Number of output tokens */\n outputTokens: number;\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\n/**\n * Standard API response wrapper\n */\nexport interface ApiResponse<T> {\n data?: T;\n error?: {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n };\n}\n","/**\n * Sentrial Client - Main SDK interface\n *\n * Captures agent sessions, tool calls, and metrics. This data powers:\n * - Signal detection (auto-detect patterns/anomalies)\n * - Root cause analysis (understand WHY agents fail)\n * - Code fixer (AI-suggested fixes with GitHub PRs)\n */\n\nimport { ApiError, NetworkError } from './errors.js';\nimport { redactPayload } from './redact.js';\nimport type { PiiConfig } from './redact.js';\nimport { createContextVar, type ContextVar } from './async-context.js';\nimport {\n _setSessionContextWithTokens,\n _restoreSessionContext,\n type SessionContextTokens,\n} from './wrappers.js';\nimport { EventBatcher } from './batcher.js';\nimport type {\n SentrialClientConfig,\n CreateSessionParams,\n CompleteSessionParams,\n TrackToolCallParams,\n TrackDecisionParams,\n TrackErrorParams,\n BeginParams,\n FinishParams,\n Session,\n Event,\n EventType,\n} from './types.js';\nimport { calculateOpenAICost, calculateAnthropicCost, calculateGoogleCost } from './cost.js';\n\n// Re-export EventType enum for convenience\nexport { EventType } from './types.js';\n\nconst DEFAULT_API_URL = 'https://api.sentrial.com';\n\n// Retry configuration (mirrors Python SDK)\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 500;\nconst MAX_BACKOFF_MS = 8000;\nconst BACKOFF_MULTIPLIER = 2;\nconst RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);\nconst REQUEST_TIMEOUT_MS = 10_000;\n\n/**\n * Sentrial Client for agent observability.\n *\n * Captures the data needed for automated root cause analysis and fix suggestions.\n *\n * @example\n * ```ts\n * const client = new SentrialClient({\n * apiKey: 'sentrial_live_xxx',\n * apiUrl: 'https://api.sentrial.com',\n * });\n *\n * // Create a session for an agent run\n * const sessionId = await client.createSession({\n * name: 'Support Request #123',\n * agentName: 'support-agent',\n * userId: 'user_123',\n * });\n *\n * // Track events\n * await client.trackToolCall({\n * sessionId,\n * toolName: 'search_kb',\n * toolInput: { query: 'password reset' },\n * toolOutput: { articles: ['KB-001'] },\n * });\n *\n * // Complete session with metrics\n * await client.completeSession({\n * sessionId,\n * success: true,\n * customMetrics: { customer_satisfaction: 90 },\n * });\n * ```\n *\n * @remarks\n * By default, the SDK fails silently (failSilently=true) to ensure\n * monitoring never crashes your application. Set failSilently=false\n * during development to see errors.\n */\nexport class SentrialClient {\n private readonly apiUrl: string;\n private readonly apiKey?: string;\n private readonly failSilently: boolean;\n private piiConfig?: PiiConfig;\n private piiConfigNeedsHydration: boolean = false;\n private piiHydrationPromise?: Promise<void>;\n private readonly _stateVar: ContextVar<Record<string, unknown>> =\n createContextVar<Record<string, unknown>>({});\n private readonly batcher?: EventBatcher;\n\n /** Per-session cost/token accumulator — populated by trackToolCall/trackDecision */\n private sessionAccumulators = new Map<string, {\n cost: number;\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n }>();\n\n private get currentState(): Record<string, unknown> {\n return this._stateVar.get();\n }\n\n private set currentState(value: Record<string, unknown>) {\n this._stateVar.set(value);\n }\n\n constructor(config: SentrialClientConfig = {}) {\n this.apiUrl = (\n config.apiUrl ??\n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_URL : undefined) ??\n DEFAULT_API_URL\n ).replace(/\\/$/, '');\n\n this.apiKey =\n config.apiKey ??\n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_KEY : undefined);\n\n this.failSilently = config.failSilently ?? true;\n\n if (config.pii === true) {\n // Minimal default until we fetch the real config from the server\n this.piiConfig = { enabled: true };\n this.piiConfigNeedsHydration = true;\n } else if (config.pii && typeof config.pii === 'object') {\n this.piiConfig = config.pii;\n this.piiConfigNeedsHydration = false;\n }\n\n // Initialize event batcher if enabled\n if (config.batching?.enabled) {\n this.batcher = new EventBatcher(\n (method, url, body) => this.safeRequest(method, url, body),\n config.batching,\n );\n }\n }\n\n /**\n * Fetch the organization's PII config from the server.\n *\n * Called lazily on the first request when `pii: true` was passed to the constructor.\n * Uses a single shared promise so concurrent requests don't trigger duplicate fetches.\n */\n private async hydratePiiConfig(): Promise<void> {\n if (!this.piiConfigNeedsHydration) return;\n\n // Deduplicate: if a fetch is already in flight, reuse its promise\n if (this.piiHydrationPromise) {\n await this.piiHydrationPromise;\n return;\n }\n\n this.piiHydrationPromise = (async () => {\n try {\n const headers: Record<string, string> = {};\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(`${this.apiUrl}/api/sdk/pii-config`, {\n method: 'GET',\n headers,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (response.ok) {\n const data = (await response.json()) as {\n config: {\n enabled: boolean;\n mode: string;\n fields: string[];\n builtinPatterns: Record<string, boolean>;\n customPatterns: Array<{ pattern: string; label: string }>;\n enhancedDetection: boolean;\n } | null;\n };\n if (data.config) {\n this.piiConfig = {\n enabled: data.config.enabled,\n mode: data.config.mode as PiiConfig['mode'],\n fields: data.config.fields as PiiConfig['fields'],\n builtinPatterns: data.config.builtinPatterns as PiiConfig['builtinPatterns'],\n customPatterns: (data.config.customPatterns || []).map(\n (cp: { pattern: string; label: string }) => ({\n pattern: new RegExp(cp.pattern, 'g'),\n label: cp.label,\n })\n ),\n enhancedDetection: data.config.enhancedDetection,\n };\n }\n }\n } catch {\n // Fail silently — keep the minimal default config (enabled: true)\n }\n this.piiConfigNeedsHydration = false;\n })();\n\n await this.piiHydrationPromise;\n }\n\n /**\n * Make an HTTP request with retry logic and exponential backoff.\n *\n * Retries on transient failures (network errors, timeouts, 429/5xx).\n * Up to MAX_RETRIES attempts with exponential backoff.\n */\n private async safeRequest<T>(\n method: string,\n url: string,\n body?: unknown\n ): Promise<T | null> {\n // Hydrate server-driven PII config before first request\n if (this.piiConfigNeedsHydration) {\n await this.hydratePiiConfig();\n }\n\n let lastError: Error | undefined;\n let backoff = INITIAL_BACKOFF_MS;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n // Apply PII redaction before serialization\n const finalBody =\n this.piiConfig && body && typeof body === 'object'\n ? redactPayload(body as Record<string, unknown>, this.piiConfig)\n : body;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: finalBody ? JSON.stringify(finalBody) : undefined,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n // Retryable HTTP status — back off and retry\n if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < MAX_RETRIES) {\n await this.sleep(backoff);\n backoff = Math.min(backoff * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n continue;\n }\n\n if (!response.ok) {\n const errorBody = await response.text();\n let errorData: { error?: { message?: string; code?: string } } = {};\n try {\n errorData = JSON.parse(errorBody);\n } catch {\n // Ignore JSON parse errors\n }\n\n const error = new ApiError(\n errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n errorData.error?.code\n );\n\n if (this.failSilently) {\n console.warn(`Sentrial: Request failed (${method} ${url}):`, error.message);\n return null;\n }\n throw error;\n }\n\n return (await response.json()) as T;\n } catch (error) {\n // Non-retryable Sentrial errors — throw immediately\n if (error instanceof ApiError) {\n throw error;\n }\n\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Network / timeout errors are retryable\n if (attempt < MAX_RETRIES) {\n await this.sleep(backoff);\n backoff = Math.min(backoff * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n continue;\n }\n }\n }\n\n // All retries exhausted\n const networkError = new NetworkError(\n lastError?.message ?? 'Unknown network error',\n lastError\n );\n\n if (this.failSilently) {\n console.warn(`Sentrial: Request failed after ${MAX_RETRIES + 1} attempts (${method} ${url}):`, networkError.message);\n return null;\n }\n throw networkError;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private accumulate(sessionId: string, cost?: number, tokenCount?: number, toolOutput?: Record<string, unknown>): void {\n let acc = this.sessionAccumulators.get(sessionId);\n if (!acc) {\n acc = { cost: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n this.sessionAccumulators.set(sessionId, acc);\n }\n if (cost != null) acc.cost += cost;\n if (tokenCount != null) acc.totalTokens += tokenCount;\n // LLM wrappers store prompt/completion breakdown in toolOutput.tokens\n const rawTokens = toolOutput?.tokens;\n if (rawTokens && typeof rawTokens === 'object' && !Array.isArray(rawTokens)) {\n const tokens = rawTokens as Record<string, unknown>;\n if (typeof tokens.prompt === 'number') acc.promptTokens += tokens.prompt;\n if (typeof tokens.completion === 'number') acc.completionTokens += tokens.completion;\n }\n }\n\n /**\n * Create a new session\n *\n * @param params - Session creation parameters\n * @returns Session ID, or null if the request failed and failSilently is true\n */\n async createSession(params: CreateSessionParams): Promise<string | null> {\n // Clear state from previous sessions to prevent memory leaks\n this.currentState = {};\n\n const payload: Record<string, unknown> = {\n name: params.name,\n agentName: params.agentName,\n userId: params.userId,\n metadata: params.metadata,\n };\n\n if (params.parentSessionId) {\n payload.parentSessionId = params.parentSessionId;\n }\n\n if (params.convoId) {\n payload.convoId = params.convoId;\n }\n\n const response = await this.safeRequest<{ id: string }>(\n 'POST',\n `${this.apiUrl}/api/sdk/sessions`,\n payload\n );\n\n return response?.id ?? null;\n }\n\n /**\n * Track a tool call event\n *\n * @param params - Tool call parameters\n * @returns Event data\n */\n async trackToolCall(params: TrackToolCallParams): Promise<Event | null> {\n this.accumulate(params.sessionId, params.estimatedCost, params.tokenCount, params.toolOutput);\n const stateBefore = { ...this.currentState };\n\n // Update current state\n this.currentState[`${params.toolName}_result`] = params.toolOutput;\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: 'tool_call' as EventType,\n toolName: params.toolName,\n toolInput: params.toolInput,\n toolOutput: params.toolOutput,\n reasoning: params.reasoning,\n stateBefore,\n stateAfter: { ...this.currentState },\n estimatedCost: params.estimatedCost ?? 0,\n };\n\n if (params.toolError !== undefined) payload.toolError = params.toolError;\n if (params.tokenCount !== undefined) payload.tokenCount = params.tokenCount;\n if (params.traceId !== undefined) payload.traceId = params.traceId;\n if (params.spanId !== undefined) payload.spanId = params.spanId;\n if (params.metadata !== undefined) payload.metadata = params.metadata;\n\n if (this.batcher) {\n this.batcher.enqueue('POST', `${this.apiUrl}/api/sdk/events`, payload);\n return null;\n }\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Track an LLM decision event\n *\n * @param params - Decision parameters\n * @returns Event data\n */\n async trackDecision(params: TrackDecisionParams): Promise<Event | null> {\n this.accumulate(params.sessionId, params.estimatedCost, params.tokenCount);\n const stateBefore = { ...this.currentState };\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: 'llm_decision' as EventType,\n reasoning: params.reasoning,\n alternativesConsidered: params.alternatives,\n confidence: params.confidence,\n stateBefore,\n stateAfter: { ...this.currentState },\n estimatedCost: params.estimatedCost ?? 0,\n };\n\n if (params.tokenCount !== undefined) payload.tokenCount = params.tokenCount;\n if (params.traceId !== undefined) payload.traceId = params.traceId;\n if (params.spanId !== undefined) payload.spanId = params.spanId;\n if (params.metadata !== undefined) payload.metadata = params.metadata;\n\n if (this.batcher) {\n this.batcher.enqueue('POST', `${this.apiUrl}/api/sdk/events`, payload);\n return null;\n }\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Track an error event\n *\n * @param params - Error parameters\n * @returns Event data\n */\n async trackError(params: TrackErrorParams): Promise<Event | null> {\n const stateBefore = { ...this.currentState };\n\n const toolError: Record<string, unknown> = {\n message: params.errorMessage,\n };\n if (params.errorType) toolError.type = params.errorType;\n if (params.stackTrace) toolError.stack_trace = params.stackTrace;\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: 'error' as EventType,\n toolError,\n stateBefore,\n stateAfter: { ...this.currentState },\n };\n\n if (params.toolName !== undefined) payload.toolName = params.toolName;\n if (params.traceId !== undefined) payload.traceId = params.traceId;\n if (params.spanId !== undefined) payload.spanId = params.spanId;\n if (params.metadata !== undefined) payload.metadata = params.metadata;\n\n if (this.batcher) {\n this.batcher.enqueue('POST', `${this.apiUrl}/api/sdk/events`, payload);\n return null;\n }\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Update the current state\n *\n * @param key - State key\n * @param value - State value\n */\n updateState(key: string, value: unknown): void {\n this.currentState[key] = value;\n }\n\n /**\n * Set the user input for a session\n *\n * @param sessionId - Session ID\n * @param input - User input text\n * @returns Updated session or null on error\n */\n async setInput(sessionId: string, input: string): Promise<Session | null> {\n return this.safeRequest<Session>(\n 'PATCH',\n `${this.apiUrl}/api/sdk/sessions/${sessionId}`,\n { userInput: input }\n );\n }\n\n /**\n * Track a generic event\n *\n * @param params - Event parameters\n * @returns Event data or null on error\n */\n async trackEvent(params: {\n sessionId: string;\n eventType: string;\n eventData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n }): Promise<Event | null> {\n const stateBefore = { ...this.currentState };\n\n const payload: Record<string, unknown> = {\n sessionId: params.sessionId,\n eventType: params.eventType,\n stateBefore,\n stateAfter: { ...this.currentState },\n };\n\n if (params.eventData) {\n Object.assign(payload, params.eventData);\n }\n if (params.metadata) {\n payload.metadata = params.metadata;\n }\n\n if (this.batcher) {\n this.batcher.enqueue('POST', `${this.apiUrl}/api/sdk/events`, payload);\n return null;\n }\n return this.safeRequest<Event>('POST', `${this.apiUrl}/api/sdk/events`, payload);\n }\n\n /**\n * Complete a session with performance metrics\n *\n * This is the recommended way to close sessions for performance monitoring.\n *\n * @param params - Session completion parameters\n * @returns Updated session data\n *\n * @example\n * ```ts\n * await client.completeSession({\n * sessionId,\n * success: true,\n * estimatedCost: 0.023,\n * promptTokens: 1500,\n * completionTokens: 500,\n * totalTokens: 2000,\n * userInput: \"What's the weather in San Francisco?\",\n * assistantOutput: \"The weather in San Francisco is 65°F and sunny.\",\n * customMetrics: {\n * customer_satisfaction: 4.5,\n * order_value: 129.99,\n * },\n * });\n * ```\n */\n async completeSession(params: CompleteSessionParams): Promise<Session | null> {\n // Flush queued events before completing the session so they arrive first\n if (this.batcher) {\n await this.batcher.flush();\n }\n\n // Auto-fill cost/tokens from accumulated event data if not explicitly provided\n const acc = this.sessionAccumulators.get(params.sessionId);\n if (acc) {\n if (params.estimatedCost === undefined && acc.cost > 0) params = { ...params, estimatedCost: acc.cost };\n if (params.promptTokens === undefined && acc.promptTokens > 0) params = { ...params, promptTokens: acc.promptTokens };\n if (params.completionTokens === undefined && acc.completionTokens > 0) params = { ...params, completionTokens: acc.completionTokens };\n if (params.totalTokens === undefined && acc.totalTokens > 0) params = { ...params, totalTokens: acc.totalTokens };\n this.sessionAccumulators.delete(params.sessionId);\n }\n\n const payload: Record<string, unknown> = {\n status: params.success !== false ? 'completed' : 'failed',\n success: params.success ?? true,\n };\n\n if (params.failureReason !== undefined) payload.failureReason = params.failureReason;\n if (params.estimatedCost !== undefined) payload.estimatedCost = params.estimatedCost;\n if (params.customMetrics !== undefined) payload.customMetrics = params.customMetrics;\n if (params.durationMs !== undefined) payload.durationMs = params.durationMs;\n if (params.promptTokens !== undefined) payload.promptTokens = params.promptTokens;\n if (params.completionTokens !== undefined) payload.completionTokens = params.completionTokens;\n if (params.totalTokens !== undefined) payload.totalTokens = params.totalTokens;\n if (params.userInput !== undefined) payload.userInput = params.userInput;\n // Support both 'assistantOutput' and 'output' (alias)\n const output = params.assistantOutput ?? params.output;\n if (output !== undefined) payload.assistantOutput = output;\n\n return this.safeRequest<Session>(\n 'PATCH',\n `${this.apiUrl}/api/sdk/sessions/${params.sessionId}`,\n payload\n );\n }\n\n /**\n * Flush any queued events immediately.\n *\n * No-op if batching is not enabled.\n */\n async flush(): Promise<void> {\n if (this.batcher) {\n await this.batcher.flush();\n }\n }\n\n /**\n * Shut down the event batcher, flushing remaining events.\n *\n * Call this before your process exits for a clean shutdown.\n * No-op if batching is not enabled.\n */\n async shutdown(): Promise<void> {\n if (this.batcher) {\n await this.batcher.shutdown();\n }\n }\n\n /**\n * Begin tracking an interaction (simplified API)\n *\n * @param params - Interaction parameters\n * @returns Interaction object with finish() method\n *\n * @example\n * ```ts\n * const interaction = await client.begin({\n * userId: 'user_123',\n * event: 'chat_message',\n * input: message,\n * convoId: 'convo_456',\n * });\n *\n * // ... do your agent work ...\n *\n * await interaction.finish({ output: responseText });\n * ```\n */\n async begin(params: BeginParams): Promise<Interaction> {\n const eventId = params.eventId ?? crypto.randomUUID();\n\n // Build metadata with optional fields\n const fullMetadata: Record<string, unknown> = params.metadata ? { ...params.metadata } : {};\n if (params.input) fullMetadata.input = params.input;\n if (params.convoId) fullMetadata.convo_id = params.convoId;\n fullMetadata.event_id = eventId;\n\n // Create session first (clears old state)\n const sessionId = await this.createSession({\n name: `${params.event}:${eventId.slice(0, 8)}`,\n agentName: params.event,\n userId: params.userId,\n convoId: params.convoId,\n metadata: fullMetadata,\n });\n\n // Store input in current_state AFTER createSession clears old state\n if (params.input) {\n this.currentState.input = params.input;\n }\n\n // Auto-wire session context so wrapped LLM calls flow into this session\n let sessionTokens: SessionContextTokens | undefined;\n if (sessionId) {\n sessionTokens = _setSessionContextWithTokens(sessionId, this);\n }\n\n return new Interaction({\n client: this,\n sessionId,\n eventId,\n userId: params.userId,\n event: params.event,\n userInput: params.input,\n sessionTokens,\n });\n }\n\n // Cost calculation static methods for convenience\n static calculateOpenAICost = calculateOpenAICost;\n static calculateAnthropicCost = calculateAnthropicCost;\n static calculateGoogleCost = calculateGoogleCost;\n}\n\n// ============================================================================\n// Interaction Class\n// ============================================================================\n\ninterface InteractionConfig {\n client: SentrialClient;\n sessionId: string | null;\n eventId: string;\n userId: string;\n event: string;\n userInput?: string;\n sessionTokens?: SessionContextTokens;\n}\n\n/**\n * Represents an in-progress interaction that can be finished.\n *\n * Created by SentrialClient.begin() - provides a clean begin/finish API pattern.\n *\n * @remarks\n * If session creation failed (sessionId is null), all tracking methods\n * become no-ops to ensure your application continues running.\n */\nexport class Interaction {\n private readonly client: SentrialClient;\n private readonly sessionId: string | null;\n /** Event ID for this interaction */\n public readonly eventId: string;\n /** User ID for this interaction */\n public readonly userId: string;\n /** Event name for this interaction */\n public readonly event: string;\n private readonly startTime = Date.now();\n private finished = false;\n private success = true;\n private failureReason?: string;\n private output?: string;\n private readonly userInput?: string;\n private readonly degraded: boolean;\n\n /** Context tokens for restoring previous session context on finish() */\n private sessionTokens?: SessionContextTokens;\n\n constructor(config: InteractionConfig) {\n this.client = config.client;\n this.sessionId = config.sessionId;\n this.eventId = config.eventId;\n this.userId = config.userId;\n this.event = config.event;\n this.userInput = config.userInput;\n this.degraded = config.sessionId === null;\n this.sessionTokens = config.sessionTokens;\n }\n\n /**\n * Set the output for this interaction\n *\n * This will be used when finish() is called.\n * Useful when you want to set the output but call finish() later.\n *\n * @param output - The output/response text\n */\n setOutput(output: string): void {\n this.output = output;\n }\n\n /**\n * Finish the interaction and record metrics\n *\n * @param params - Finish parameters\n * @returns Updated session data, or null if degraded/already finished\n *\n * @example\n * ```ts\n * await interaction.finish({\n * output: \"Here's the answer to your question...\",\n * success: true,\n * customMetrics: { satisfaction: 4.5 },\n * });\n * ```\n */\n async finish(params: FinishParams = {}): Promise<Session | null> {\n if (this.finished) return null;\n if (this.degraded) {\n this.finished = true;\n return null;\n }\n\n this.finished = true;\n\n // Use stored output if not provided\n const finalOutput = params.output ?? this.output;\n\n const result = await this.client.completeSession({\n sessionId: this.sessionId!,\n success: params.success ?? this.success,\n failureReason: params.failureReason ?? this.failureReason,\n estimatedCost: params.estimatedCost,\n customMetrics: params.customMetrics,\n durationMs: params.durationMs ?? (Date.now() - this.startTime),\n promptTokens: params.promptTokens,\n completionTokens: params.completionTokens,\n totalTokens: params.totalTokens,\n userInput: this.userInput,\n assistantOutput: finalOutput,\n });\n\n // Restore previous session context after completing (supports nesting)\n if (this.sessionTokens) {\n _restoreSessionContext(this.sessionTokens);\n this.sessionTokens = undefined;\n }\n\n return result;\n }\n\n /**\n * Track a tool call within this interaction\n */\n async trackToolCall(\n params: Omit<TrackToolCallParams, 'sessionId'>\n ): Promise<Event | null> {\n if (this.degraded) return null;\n\n return this.client.trackToolCall({\n ...params,\n sessionId: this.sessionId!,\n });\n }\n\n /**\n * Track an LLM decision within this interaction\n */\n async trackDecision(\n params: Omit<TrackDecisionParams, 'sessionId'>\n ): Promise<Event | null> {\n if (this.degraded) return null;\n\n return this.client.trackDecision({\n ...params,\n sessionId: this.sessionId!,\n });\n }\n\n /**\n * Track an error within this interaction\n */\n async trackError(\n params: Omit<TrackErrorParams, 'sessionId'>\n ): Promise<Event | null> {\n // Store failure info\n this.success = false;\n this.failureReason = params.errorMessage;\n\n if (this.degraded) return null;\n\n return this.client.trackError({\n ...params,\n sessionId: this.sessionId!,\n });\n }\n\n /**\n * Get the session ID (null if session creation failed)\n */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Check if the interaction is in a degraded state (session creation failed)\n */\n isDegraded(): boolean {\n return this.degraded;\n }\n}\n\n// ============================================================================\n// Module-level Simple API\n// ============================================================================\n\nlet defaultClient: SentrialClient | null = null;\n\nfunction getClient(): SentrialClient {\n if (!defaultClient) {\n defaultClient = new SentrialClient();\n }\n return defaultClient;\n}\n\n/**\n * Configure the default Sentrial client\n *\n * @param config - Client configuration\n *\n * @example\n * ```ts\n * import { sentrial } from '@sentrial/sdk';\n *\n * sentrial.configure({\n * apiKey: 'sentrial_live_xxx',\n * apiUrl: 'https://api.sentrial.com',\n * });\n * ```\n */\nexport function configure(config: SentrialClientConfig): void {\n defaultClient = new SentrialClient(config);\n}\n\n/**\n * Begin tracking an interaction (module-level convenience function)\n *\n * This is a shorthand for new SentrialClient().begin(...).\n * Configure the client first with sentrial.configure() or set SENTRIAL_API_KEY env var.\n *\n * @param params - Interaction parameters\n * @returns Interaction object with finish() method\n *\n * @example\n * ```ts\n * import { sentrial } from '@sentrial/sdk';\n *\n * sentrial.configure({ apiKey: 'sentrial_live_xxx' });\n *\n * const interaction = await sentrial.begin({\n * userId: 'user_123',\n * event: 'chat_message',\n * input: message,\n * });\n *\n * // ... do your agent work ...\n *\n * await interaction.finish({ output: responseText });\n * ```\n */\nexport function begin(params: BeginParams): Promise<Interaction> {\n return getClient().begin(params);\n}\n\n/**\n * Simple API namespace for module-level usage\n */\nasync function flush(): Promise<void> {\n if (defaultClient) await defaultClient.flush();\n}\n\nasync function shutdown(): Promise<void> {\n if (defaultClient) await defaultClient.shutdown();\n}\n\nexport const sentrial = {\n configure,\n begin,\n flush,\n shutdown,\n};\n","/**\n * Vercel AI SDK Integration for Sentrial\n *\n * Automatically traces AI SDK calls with full input/output logging, metrics, and tool execution.\n * Supports Vercel AI SDK v3, v4, v5, and v6.\n *\n * @example Simple Usage\n * ```ts\n * import { configureVercel, wrapAISDK } from '@sentrial/sdk';\n * import * as ai from 'ai';\n * import { openai } from '@ai-sdk/openai';\n *\n * configureVercel({\n * apiKey: process.env.SENTRIAL_API_KEY,\n * defaultAgent: 'my-ai-agent',\n * });\n *\n * const { generateText } = wrapAISDK(ai);\n *\n * // This automatically logs to Sentrial\n * const { text } = await generateText({\n * model: openai('gpt-4'),\n * prompt: 'What is the capital of France?',\n * });\n * ```\n *\n * @example Multi-Turn Conversations\n * ```ts\n * const { generateText } = wrapAISDK(ai, { convoId: 'convo_123' });\n *\n * // All calls are linked to the same conversation thread\n * const { text } = await generateText({\n * model: openai('gpt-4o'),\n * messages: conversationHistory,\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { SentrialClient } from './client.js';\nimport { calculateOpenAICost, calculateAnthropicCost, calculateGoogleCost } from './cost.js';\n\n// ============================================================================\n// Types for AI SDK (we don't import to keep it as peer dep)\n// ============================================================================\n\ntype AIModule = {\n generateText?: Function;\n streamText?: Function;\n generateObject?: Function;\n streamObject?: Function;\n experimental_generateText?: Function;\n experimental_streamText?: Function;\n};\n\ntype GenerateTextParams = {\n model: { modelId?: string; provider?: string } & Record<string, unknown>;\n prompt?: string;\n messages?: Array<{ role: string; content: string | unknown }>;\n system?: string;\n tools?: Record<string, { execute?: Function } & Record<string, unknown>>;\n maxTokens?: number;\n maxSteps?: number;\n temperature?: number;\n [key: string]: unknown;\n};\n\ntype UsageInfo = {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n};\n\ntype StepResult = {\n text?: string;\n toolCalls?: Array<{ toolName: string; args: unknown }>;\n toolResults?: Array<{ toolName: string; result: unknown }>;\n usage?: UsageInfo;\n finishReason?: string;\n};\n\ntype GenerateTextResult = {\n text: string;\n usage?: UsageInfo;\n finishReason?: string;\n toolCalls?: Array<{ toolName: string; args: unknown }>;\n toolResults?: Array<{ toolName: string; result: unknown }>;\n response?: { modelId?: string; id?: string };\n steps?: StepResult[];\n reasoning?: unknown;\n reasoningText?: string;\n sources?: unknown[];\n [key: string]: unknown;\n};\n\ntype StreamTextResult = {\n textStream: AsyncIterable<string>;\n fullStream?: AsyncIterable<unknown>;\n text?: string | Promise<string>;\n usage?: Promise<UsageInfo>;\n finishReason?: Promise<string>;\n toolCalls?: Promise<Array<{ toolName: string; args: unknown }>>;\n toolResults?: Promise<Array<{ toolName: string; result: unknown }>>;\n steps?: Promise<StepResult[]>;\n response?: Promise<{ modelId?: string; id?: string }>;\n [key: string]: unknown;\n};\n\n/** Per-instance config passed through closures (not global) */\ntype WrapperConfig = {\n defaultAgent?: string;\n userId?: string;\n convoId?: string;\n};\n\n// ============================================================================\n// Global Configuration (used only by configureVercel)\n// ============================================================================\n\nlet _defaultClient: SentrialClient | null = null;\nlet _globalConfig: WrapperConfig = {};\n\n/**\n * Configure the Sentrial SDK for Vercel AI SDK integration.\n *\n * @example\n * ```ts\n * import { configureVercel } from '@sentrial/sdk';\n *\n * configureVercel({\n * apiKey: process.env.SENTRIAL_API_KEY,\n * defaultAgent: 'my-chatbot',\n * userId: 'user_123',\n * });\n * ```\n */\nexport function configureVercel(config: {\n apiKey?: string;\n apiUrl?: string;\n defaultAgent?: string;\n userId?: string;\n convoId?: string;\n failSilently?: boolean;\n}): void {\n _defaultClient = new SentrialClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n failSilently: config.failSilently ?? true,\n });\n _globalConfig = {\n defaultAgent: config.defaultAgent,\n userId: config.userId,\n convoId: config.convoId,\n };\n}\n\nfunction getClient(): SentrialClient {\n if (!_defaultClient) {\n _defaultClient = new SentrialClient();\n }\n return _defaultClient;\n}\n\n// ============================================================================\n// Model & Provider Utilities\n// ============================================================================\n\nfunction extractModelInfo(model: GenerateTextParams['model']): {\n modelId: string;\n provider: string;\n} {\n const modelId =\n model.modelId || (model as Record<string, unknown>).id as string || 'unknown';\n const provider = model.provider || guessProvider(modelId);\n return { modelId, provider };\n}\n\nfunction guessProvider(modelId: string): string {\n const id = modelId.toLowerCase();\n if (id.includes('gpt') || id.startsWith('o1') || id.startsWith('o3') || id.startsWith('o4')\n || id.startsWith('chatgpt')) return 'openai';\n if (id.includes('claude')) return 'anthropic';\n if (id.includes('gemini')) return 'google';\n if (id.includes('mistral') || id.includes('mixtral') || id.includes('codestral')\n || id.includes('pixtral')) return 'mistral';\n if (id.includes('llama')) return 'meta';\n if (id.includes('deepseek')) return 'deepseek';\n if (id.includes('command')) return 'cohere';\n if (id.includes('qwen')) return 'alibaba';\n return 'unknown';\n}\n\nfunction calculateCostForCall(\n provider: string,\n modelId: string,\n promptTokens: number,\n completionTokens: number\n): number {\n const params = { model: modelId, inputTokens: promptTokens, outputTokens: completionTokens };\n switch (provider.toLowerCase()) {\n case 'openai':\n return calculateOpenAICost(params);\n case 'anthropic':\n return calculateAnthropicCost(params);\n case 'google':\n return calculateGoogleCost(params);\n case 'deepseek':\n return (promptTokens / 1_000_000) * 0.27 + (completionTokens / 1_000_000) * 1.10;\n case 'cohere':\n return (promptTokens / 1_000_000) * 0.50 + (completionTokens / 1_000_000) * 1.50;\n case 'mistral':\n return (promptTokens / 1_000_000) * 2.00 + (completionTokens / 1_000_000) * 6.00;\n default:\n return 0;\n }\n}\n\nfunction extractInput(params: GenerateTextParams): string {\n if (params.prompt) return params.prompt;\n if (params.messages && params.messages.length > 0) {\n const lastUserMessage = [...params.messages].reverse().find(m => m.role === 'user');\n if (lastUserMessage) {\n return typeof lastUserMessage.content === 'string'\n ? lastUserMessage.content\n : JSON.stringify(lastUserMessage.content);\n }\n return JSON.stringify(params.messages);\n }\n return '';\n}\n\n// ============================================================================\n// Tool Wrapping\n// ============================================================================\n\nfunction wrapTools(\n tools: Record<string, { execute?: Function } & Record<string, unknown>> | undefined,\n sessionId: string,\n client: SentrialClient\n): Record<string, { execute?: Function } & Record<string, unknown>> | undefined {\n if (!tools) return undefined;\n\n const wrappedTools: Record<string, { execute?: Function } & Record<string, unknown>> = {};\n\n for (const [toolName, tool] of Object.entries(tools)) {\n if (typeof tool.execute === 'function') {\n const originalExecute = tool.execute;\n wrappedTools[toolName] = {\n ...tool,\n execute: async (...args: unknown[]) => {\n const startTime = Date.now();\n try {\n const result = await originalExecute(...args);\n const durationMs = Date.now() - startTime;\n\n // Fire-and-forget: don't let tracking delay tool result\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: result as Record<string, unknown>,\n reasoning: `Tool executed in ${durationMs}ms`,\n }).catch(() => {});\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: {},\n toolError: { message: error instanceof Error ? error.message : 'Unknown error' },\n reasoning: `Tool failed after ${durationMs}ms`,\n }).catch(() => {});\n\n throw error;\n }\n },\n };\n } else {\n wrappedTools[toolName] = tool;\n }\n }\n\n return wrappedTools;\n}\n\nfunction wrapToolsAsync(\n tools: Record<string, { execute?: Function } & Record<string, unknown>>,\n sessionPromise: Promise<string | null>,\n client: SentrialClient\n): Record<string, { execute?: Function } & Record<string, unknown>> {\n const wrappedTools: Record<string, { execute?: Function } & Record<string, unknown>> = {};\n\n for (const [toolName, tool] of Object.entries(tools)) {\n if (typeof tool.execute === 'function') {\n const originalExecute = tool.execute;\n wrappedTools[toolName] = {\n ...tool,\n execute: async (...args: unknown[]) => {\n const startTime = Date.now();\n const sid = await sessionPromise;\n\n try {\n const result = await originalExecute(...args);\n const durationMs = Date.now() - startTime;\n\n if (sid) {\n client.trackToolCall({\n sessionId: sid,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: result as Record<string, unknown>,\n reasoning: `Tool executed in ${durationMs}ms`,\n }).catch(() => {});\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (sid) {\n client.trackToolCall({\n sessionId: sid,\n toolName,\n toolInput: args[0] as Record<string, unknown>,\n toolOutput: {},\n toolError: { message: error instanceof Error ? error.message : 'Unknown error' },\n reasoning: `Tool failed after ${durationMs}ms`,\n }).catch(() => {});\n }\n\n throw error;\n }\n },\n };\n } else {\n wrappedTools[toolName] = tool;\n }\n }\n\n return wrappedTools;\n}\n\n// ============================================================================\n// generateText Wrapper\n// ============================================================================\n\nfunction wrapGenerateText(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => Promise<GenerateTextResult> {\n return async (params: GenerateTextParams): Promise<GenerateTextResult> => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n const sessionId = await client.createSession({\n name: `generateText: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'generateText',\n ...(params.maxSteps ? { maxSteps: params.maxSteps } : {}),\n },\n });\n\n if (!sessionId) {\n return originalFn(params);\n }\n\n await client.setInput(sessionId, input);\n\n const wrappedParams = {\n ...params,\n tools: wrapTools(params.tools, sessionId, client),\n };\n\n try {\n const result: GenerateTextResult = await originalFn(wrappedParams);\n const durationMs = Date.now() - startTime;\n\n const resolvedModelId = result.response?.modelId || modelId;\n\n const promptTokens = result.usage?.promptTokens ?? 0;\n const completionTokens = result.usage?.completionTokens ?? 0;\n const totalTokens = result.usage?.totalTokens ?? (promptTokens + completionTokens);\n const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);\n\n // Track multi-step results (v4+ with maxSteps) — fire-and-forget to avoid\n // adding N serial HTTP requests of latency to the user's call\n const steps = result.steps;\n if (steps && steps.length >= 1) {\n const stepPromises = steps.map((step, i) =>\n client.trackEvent({\n sessionId,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n step: i + 1,\n total_steps: steps.length,\n prompt_tokens: step.usage?.promptTokens ?? 0,\n completion_tokens: step.usage?.completionTokens ?? 0,\n total_tokens: step.usage?.totalTokens ?? 0,\n finish_reason: step.finishReason,\n tool_calls: step.toolCalls?.map(tc => tc.toolName),\n },\n }).catch(() => {}),\n );\n await Promise.all(stepPromises);\n } else {\n await client.trackEvent({\n sessionId,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: totalTokens,\n finish_reason: result.finishReason,\n tool_calls: result.toolCalls?.map(tc => tc.toolName),\n },\n });\n }\n\n await client.completeSession({\n sessionId,\n success: true,\n output: result.text,\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n });\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n await client.trackError({\n sessionId,\n errorType: error instanceof Error ? error.name : 'Error',\n errorMessage: error instanceof Error ? error.message : 'Unknown error',\n });\n\n await client.completeSession({\n sessionId,\n success: false,\n failureReason: error instanceof Error ? error.message : 'Unknown error',\n durationMs,\n });\n\n throw error;\n }\n };\n}\n\n// ============================================================================\n// streamText Wrapper\n// ============================================================================\n\nfunction wrapStreamText(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => StreamTextResult {\n return (params: GenerateTextParams): StreamTextResult => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n let sessionId: string | null = null;\n const sessionPromise = (async () => {\n try {\n const id = await client.createSession({\n name: `streamText: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'streamText',\n },\n });\n sessionId = id;\n if (id) {\n client.setInput(id, input).catch(() => {});\n }\n return id;\n } catch {\n return null;\n }\n })();\n\n const wrappedParams = {\n ...params,\n tools: params.tools ? wrapToolsAsync(params.tools, sessionPromise, client) : undefined,\n };\n\n const result = originalFn(wrappedParams) as StreamTextResult;\n\n // Always wrap textStream — this is the only reliable approach because:\n // 1. toUIMessageStreamResponse() / toDataStreamResponse() consume textStream\n // 2. result.text is a read-only getter in Vercel AI SDK v5 (assigning crashes)\n // 3. textStream wrapping fires inline as the response streams, so it works\n // in serverless environments where orphaned promises are GC'd\n const originalTextStream = result.textStream;\n let fullText = '';\n let tracked = false;\n\n async function trackCompletion(text: string, error?: Error) {\n if (tracked) return;\n tracked = true;\n\n const durationMs = Date.now() - startTime;\n const sid = sessionId || await sessionPromise;\n if (!sid) return;\n\n if (error) {\n await client.trackError({\n sessionId: sid,\n errorType: error.name || 'Error',\n errorMessage: error.message || 'Unknown error',\n }).catch(() => {});\n\n await client.completeSession({\n sessionId: sid,\n success: false,\n failureReason: error.message || 'Unknown error',\n durationMs,\n }).catch(() => {});\n return;\n }\n\n // Resolve actual model from response (handles aliasing/routing)\n let resolvedModelId = modelId;\n try {\n const resp = result.response ? await result.response : undefined;\n if (resp?.modelId) resolvedModelId = resp.modelId;\n } catch {\n // Response promise may reject\n }\n\n let usage: UsageInfo | undefined;\n try {\n usage = result.usage ? await result.usage : undefined;\n } catch {\n // Usage promise may reject if stream errors\n }\n\n // Track per-step events if available (multi-step with maxSteps)\n let steps: StepResult[] | undefined;\n try {\n steps = result.steps ? await result.steps : undefined;\n } catch {\n // Steps promise may reject\n }\n\n if (steps && steps.length >= 1) {\n // Aggregate usage from steps\n let totalPrompt = 0, totalCompletion = 0;\n const stepPromises = steps.map((step, i) => {\n const sp = step.usage?.promptTokens ?? 0;\n const sc = step.usage?.completionTokens ?? 0;\n totalPrompt += sp;\n totalCompletion += sc;\n return client.trackEvent({\n sessionId: sid,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n step: i + 1,\n total_steps: steps!.length,\n prompt_tokens: sp,\n completion_tokens: sc,\n total_tokens: step.usage?.totalTokens ?? 0,\n finish_reason: step.finishReason,\n tool_calls: step.toolCalls?.map(tc => tc.toolName),\n },\n }).catch(() => {});\n });\n await Promise.all(stepPromises);\n\n // Use step-aggregated usage (more reliable than top-level for multi-step)\n const promptTokens = usage?.promptTokens ?? totalPrompt;\n const completionTokens = usage?.completionTokens ?? totalCompletion;\n const totalTokens = usage?.totalTokens ?? (promptTokens + completionTokens);\n const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);\n\n await client.completeSession({\n sessionId: sid,\n success: true,\n output: text,\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n }).catch(() => {});\n } else {\n const promptTokens = usage?.promptTokens ?? 0;\n const completionTokens = usage?.completionTokens ?? 0;\n const totalTokens = usage?.totalTokens ?? (promptTokens + completionTokens);\n const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);\n\n await client.trackEvent({\n sessionId: sid,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: totalTokens,\n },\n }).catch(() => {});\n\n await client.completeSession({\n sessionId: sid,\n success: true,\n output: text,\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n }).catch(() => {});\n }\n }\n\n result.textStream = (async function* () {\n try {\n for await (const chunk of originalTextStream) {\n fullText += chunk;\n yield chunk;\n }\n await trackCompletion(fullText);\n } catch (error) {\n await trackCompletion(\n fullText,\n error instanceof Error ? error : new Error(String(error))\n );\n throw error;\n }\n })();\n\n return result;\n };\n}\n\n// ============================================================================\n// generateObject Wrapper\n// ============================================================================\n\nfunction wrapGenerateObject(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => Promise<unknown> {\n return async (params: GenerateTextParams): Promise<unknown> => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n const sessionId = await client.createSession({\n name: `generateObject: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'generateObject',\n },\n });\n\n if (!sessionId) {\n return originalFn(params);\n }\n\n await client.setInput(sessionId, input);\n\n try {\n const result = await originalFn(params) as {\n object?: unknown;\n usage?: UsageInfo;\n response?: { modelId?: string };\n };\n const durationMs = Date.now() - startTime;\n\n const resolvedModelId = result.response?.modelId || modelId;\n const promptTokens = result.usage?.promptTokens ?? 0;\n const completionTokens = result.usage?.completionTokens ?? 0;\n const totalTokens = result.usage?.totalTokens ?? (promptTokens + completionTokens);\n const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);\n\n await client.trackEvent({\n sessionId,\n eventType: 'llm_call',\n eventData: {\n model: resolvedModelId,\n provider,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: totalTokens,\n },\n }).catch(() => {});\n\n await client.completeSession({\n sessionId,\n success: true,\n output: JSON.stringify(result.object),\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n });\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n await client.trackError({\n sessionId,\n errorType: error instanceof Error ? error.name : 'Error',\n errorMessage: error instanceof Error ? error.message : 'Unknown error',\n });\n\n await client.completeSession({\n sessionId,\n success: false,\n failureReason: error instanceof Error ? error.message : 'Unknown error',\n durationMs,\n });\n\n throw error;\n }\n };\n}\n\n// ============================================================================\n// streamObject Wrapper\n// ============================================================================\n\nfunction wrapStreamObject(\n originalFn: Function,\n client: SentrialClient,\n config: WrapperConfig\n): (params: GenerateTextParams) => unknown {\n return (params: GenerateTextParams): unknown => {\n const startTime = Date.now();\n const { modelId, provider } = extractModelInfo(params.model);\n const input = extractInput(params);\n\n const sessionPromise = (async () => {\n try {\n const id = await client.createSession({\n name: `streamObject: ${input.slice(0, 50)}${input.length > 50 ? '...' : ''}`,\n agentName: config.defaultAgent ?? 'vercel-ai-sdk',\n userId: config.userId ?? 'anonymous',\n convoId: config.convoId,\n metadata: {\n model: modelId,\n provider,\n function: 'streamObject',\n },\n });\n if (id) {\n client.setInput(id, input).catch(() => {});\n }\n return id;\n } catch {\n return null;\n }\n })();\n\n const result = originalFn(params) as {\n object?: Promise<unknown>;\n usage?: Promise<UsageInfo>;\n };\n\n async function completeStreamObject(obj: unknown | undefined, error?: Error) {\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n if (!sid) return;\n\n if (error) {\n await client.trackError({\n sessionId: sid,\n errorType: error.name || 'Error',\n errorMessage: error.message || 'Unknown error',\n }).catch(() => {});\n\n await client.completeSession({\n sessionId: sid,\n success: false,\n failureReason: error.message || 'Unknown error',\n durationMs,\n }).catch(() => {});\n return;\n }\n\n let usage: UsageInfo | undefined;\n try {\n usage = result.usage ? await result.usage : undefined;\n } catch {\n // Ignore\n }\n\n const promptTokens = usage?.promptTokens ?? 0;\n const completionTokens = usage?.completionTokens ?? 0;\n const totalTokens = usage?.totalTokens ?? (promptTokens + completionTokens);\n const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);\n\n await client.trackEvent({\n sessionId: sid,\n eventType: 'llm_call',\n eventData: {\n model: modelId,\n provider,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: totalTokens,\n },\n }).catch(() => {});\n\n await client.completeSession({\n sessionId: sid,\n success: true,\n output: JSON.stringify(obj),\n durationMs,\n estimatedCost: cost,\n promptTokens,\n completionTokens,\n totalTokens,\n }).catch(() => {});\n }\n\n if (result.object) {\n const originalObjectPromise = result.object;\n result.object = originalObjectPromise\n .then(async (obj) => {\n await completeStreamObject(obj);\n return obj;\n })\n .catch(async (error) => {\n await completeStreamObject(undefined, error instanceof Error ? error : new Error(String(error)));\n throw error;\n });\n } else if (result.usage) {\n // Fallback: if result.object is missing but usage exists, use it to detect completion\n result.usage.then(async () => {\n await completeStreamObject(undefined);\n }).catch(async (error) => {\n await completeStreamObject(undefined, error instanceof Error ? error : new Error(String(error)));\n });\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Wrap the Vercel AI SDK to automatically trace all AI calls.\n *\n * @param ai - The imported `ai` module from the Vercel AI SDK\n * @param options - Optional configuration (client, defaultAgent, userId, convoId)\n * @returns Wrapped versions of the AI SDK functions\n *\n * @example Using with configureVercel\n * ```ts\n * configureVercel({ apiKey: process.env.SENTRIAL_API_KEY, defaultAgent: 'my-agent' });\n * const { generateText, streamText } = wrapAISDK(ai);\n * ```\n *\n * @example Multi-turn conversations\n * ```ts\n * const { generateText } = wrapAISDK(ai, { convoId: 'convo_123' });\n * ```\n *\n * @example Using with existing SentrialClient\n * ```ts\n * const client = new SentrialClient({ apiKey: process.env.SENTRIAL_API_KEY });\n * const { generateText } = wrapAISDK(ai, { client, defaultAgent: 'my-agent' });\n * ```\n */\nexport function wrapAISDK(\n ai: AIModule,\n options?: {\n client?: SentrialClient;\n defaultAgent?: string;\n userId?: string;\n convoId?: string;\n }\n): {\n generateText: ReturnType<typeof wrapGenerateText>;\n streamText: ReturnType<typeof wrapStreamText>;\n generateObject: ReturnType<typeof wrapGenerateObject>;\n streamObject: ReturnType<typeof wrapStreamObject>;\n} {\n const client = options?.client ?? getClient();\n\n // Create per-instance config — never mutate global state\n const config: WrapperConfig = {\n defaultAgent: options?.defaultAgent ?? _globalConfig.defaultAgent,\n userId: options?.userId ?? _globalConfig.userId,\n convoId: options?.convoId ?? _globalConfig.convoId,\n };\n\n return {\n generateText: ai.generateText\n ? wrapGenerateText(ai.generateText, client, config)\n : wrapGenerateText(\n () => Promise.reject(new Error('generateText not available')),\n client,\n config\n ),\n streamText: ai.streamText\n ? wrapStreamText(ai.streamText, client, config)\n : wrapStreamText(() => ({ textStream: (async function* () {})() }), client, config),\n generateObject: ai.generateObject\n ? wrapGenerateObject(ai.generateObject, client, config)\n : wrapGenerateObject(\n () => Promise.reject(new Error('generateObject not available')),\n client,\n config\n ),\n streamObject: ai.streamObject\n ? wrapStreamObject(ai.streamObject, client, config)\n : wrapStreamObject(() => ({}), client, config),\n };\n}\n\n// Export types for consumers\nexport type { GenerateTextParams, GenerateTextResult, StreamTextResult };\n","/**\n * Sentrial wrapper for the Claude Agent SDK (Claude Code).\n *\n * Wraps the `query()` async generator to automatically track sessions,\n * tool calls, tokens, and costs to Sentrial.\n *\n * @example\n * ```ts\n * import { query } from '@anthropic-ai/claude-agent-sdk';\n * import { SentrialClient, wrapClaudeAgent } from '@sentrial/sdk';\n *\n * const client = new SentrialClient({ apiKey: process.env.SENTRIAL_API_KEY });\n *\n * const trackedQuery = wrapClaudeAgent(query, {\n * client,\n * defaultAgent: 'my-agent',\n * userId: 'user-123',\n * });\n *\n * for await (const message of trackedQuery({ prompt: 'Fix the tests' })) {\n * // Messages pass through unchanged\n * console.log(message.type);\n * }\n * ```\n */\n\nimport type { SentrialClient } from './client.js';\n\n// ============================================================================\n// Types (local — Claude Agent SDK is a peer dep, not imported at runtime)\n// ============================================================================\n\ntype HookCallback = (\n input: any,\n toolUseID: string | undefined,\n options: { signal: AbortSignal },\n) => Promise<any>;\n\ninterface HookCallbackMatcher {\n matcher?: string;\n hooks: HookCallback[];\n}\n\ninterface QueryOptions {\n hooks?: Record<string, HookCallbackMatcher[]>;\n [key: string]: unknown;\n}\n\ninterface QueryParams {\n prompt: string | AsyncIterable<any>;\n options?: QueryOptions;\n}\n\ntype QueryFunction = (params: QueryParams) => AsyncGenerator<any, void>;\n\n// ============================================================================\n// Wrapper Options\n// ============================================================================\n\nexport interface WrapClaudeAgentOptions {\n /** Sentrial client instance for tracking */\n client: SentrialClient;\n /** Default agent name (used in session creation). Defaults to 'claude-agent'. */\n defaultAgent?: string;\n /** User ID to associate with sessions. Defaults to 'anonymous'. */\n userId?: string;\n /** Conversation ID to group related sessions into a thread. */\n convoId?: string;\n /** Extra metadata to include in every session. */\n extraMetadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Wrap the Claude Agent SDK's `query()` function to automatically track\n * sessions, tool calls, tokens, and costs to Sentrial.\n *\n * @param queryFn - The original `query()` function from `@anthropic-ai/claude-agent-sdk`\n * @param wrapOptions - Configuration for the wrapper\n * @returns A new function with the same signature as `query()`\n */\nexport function wrapClaudeAgent(\n queryFn: QueryFunction,\n wrapOptions: WrapClaudeAgentOptions,\n): QueryFunction {\n const {\n client,\n defaultAgent = 'claude-agent',\n userId = 'anonymous',\n convoId,\n extraMetadata,\n } = wrapOptions;\n\n return function wrappedQuery(params: QueryParams): AsyncGenerator<any, void> {\n const { prompt, options = {} } = params;\n const startTime = Date.now();\n let sessionId: string | null = null;\n\n // Session-ready gate: hooks await this before checking sessionId.\n // This prevents hooks that fire during the createSession HTTP request\n // from silently skipping because sessionId is still null.\n let resolveSessionReady: () => void;\n const sessionReady = new Promise<void>((resolve) => {\n resolveSessionReady = resolve;\n });\n\n // Build the session name from the prompt\n const sessionName =\n typeof prompt === 'string'\n ? `${defaultAgent}: ${prompt.slice(0, 100)}`\n : `${defaultAgent} session`;\n\n // Collect pending tool call promises so we can flush before completing\n const pendingToolCalls: Promise<unknown>[] = [];\n\n // --- PostToolUse hook for tracking successful tool calls ---\n const sentrialToolHook: HookCallbackMatcher = {\n hooks: [\n async (input: any, toolUseID: string | undefined, _opts: { signal: AbortSignal }) => {\n await sessionReady;\n if (!sessionId) return;\n const toolOutput =\n input?.tool_response && typeof input.tool_response === 'object'\n ? (input.tool_response as Record<string, unknown>)\n : { response: input?.tool_response ?? null };\n\n const p = client\n .trackToolCall({\n sessionId,\n toolName: input?.tool_name ?? 'unknown',\n toolInput: input?.tool_input ?? {},\n toolOutput,\n metadata: { tool_use_id: toolUseID },\n })\n .catch(() => {});\n pendingToolCalls.push(p);\n },\n ],\n };\n\n // --- PostToolUseFailure hook for tracking failed tool calls ---\n const sentrialToolFailureHook: HookCallbackMatcher = {\n hooks: [\n async (input: any, toolUseID: string | undefined, _opts: { signal: AbortSignal }) => {\n await sessionReady;\n if (!sessionId) return;\n const p = client\n .trackToolCall({\n sessionId,\n toolName: input?.tool_name ?? 'unknown',\n toolInput: input?.tool_input ?? {},\n toolOutput: {},\n toolError: { message: input?.error ?? 'unknown error' },\n metadata: { tool_use_id: toolUseID },\n })\n .catch(() => {});\n pendingToolCalls.push(p);\n },\n ],\n };\n\n // Merge hooks: preserve existing user hooks, append ours\n const mergedHooks: Record<string, HookCallbackMatcher[]> = {\n ...(options.hooks ?? {}),\n };\n\n const existingPostToolUse = mergedHooks.PostToolUse ?? [];\n mergedHooks.PostToolUse = [...existingPostToolUse, sentrialToolHook];\n\n const existingPostToolUseFailure = mergedHooks.PostToolUseFailure ?? [];\n mergedHooks.PostToolUseFailure = [...existingPostToolUseFailure, sentrialToolFailureHook];\n\n const mergedOptions: QueryOptions = {\n ...options,\n hooks: mergedHooks,\n };\n\n // Call the original query function with merged options\n const generator = queryFn({ prompt, options: mergedOptions });\n\n // Wrap the generator to observe messages\n return (async function* () {\n try {\n for await (const message of generator) {\n // --- Observe init message: create session ---\n if (message.type === 'system' && message.subtype === 'init') {\n const metadata: Record<string, unknown> = {\n model: message.model,\n tools: message.tools,\n cwd: message.cwd,\n mcp_servers: message.mcp_servers,\n sdk_session_id: message.session_id,\n ...(extraMetadata ?? {}),\n };\n\n // Await session creation so sessionId is available for tool call hooks\n try {\n sessionId = await client.createSession({\n name: sessionName,\n agentName: defaultAgent,\n userId,\n convoId,\n metadata,\n });\n } catch {\n // Degrade to passthrough if session creation fails\n sessionId = null;\n }\n // Unblock hooks waiting for session creation (success or failure)\n resolveSessionReady!();\n }\n\n // --- Observe result message: complete session ---\n // Await completeSession here because result is the terminal message —\n // no more yields follow, and short-lived processes may exit immediately.\n if (message.type === 'result' && sessionId) {\n const isError = !!message.is_error;\n const inputTokens = message.usage?.input_tokens ?? 0;\n const outputTokens = message.usage?.output_tokens ?? 0;\n\n let failureReason: string | undefined;\n if (isError) {\n if (message.errors && message.errors.length > 0) {\n failureReason = message.errors.join('; ');\n } else {\n failureReason = message.subtype;\n }\n }\n\n // Flush pending tool call tracking before completing the session\n await Promise.allSettled(pendingToolCalls);\n\n try {\n await client.completeSession({\n sessionId,\n success: !isError,\n failureReason,\n estimatedCost: message.total_cost_usd,\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens: inputTokens + outputTokens,\n durationMs: message.duration_ms ?? (Date.now() - startTime),\n userInput: typeof prompt === 'string' ? prompt : undefined,\n output: message.result,\n customMetrics: {\n num_turns: message.num_turns ?? 0,\n duration_api_ms: message.duration_api_ms ?? 0,\n },\n });\n } catch {\n // Swallow — never break the agent due to tracking failures\n }\n }\n\n yield message;\n }\n } catch (error) {\n // If the generator throws, complete the session as failed\n if (sessionId) {\n await Promise.allSettled(pendingToolCalls);\n try {\n await client.completeSession({\n sessionId,\n success: false,\n failureReason:\n error instanceof Error ? error.message : String(error),\n durationMs: Date.now() - startTime,\n });\n } catch {\n // Never break the agent due to tracking failures\n }\n }\n throw error;\n }\n })();\n };\n}\n","/**\n * Sentrial Decorators - Easy instrumentation for AI agents\n *\n * Provides decorators and higher-order functions for automatic tracking:\n * - withTool: Track tool/function calls\n * - withSession: Create session boundaries around agent runs\n *\n * @example Using higher-order functions (works everywhere)\n * ```ts\n * import { withTool, withSession, configure } from '@sentrial/sdk';\n *\n * configure({ apiKey: 'sentrial_live_xxx' });\n *\n * // Wrap a tool function\n * const searchWeb = withTool('search', async (query: string) => {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * });\n *\n * // Wrap an agent session\n * const handleRequest = withSession('support-agent', async (userId: string, message: string) => {\n * const results = await searchWeb(message);\n * return processResults(results);\n * });\n * ```\n *\n * @example Using TypeScript decorators (requires experimentalDecorators)\n * ```ts\n * import { Tool, Session } from '@sentrial/sdk';\n *\n * class MyAgent {\n * @Tool('search')\n * async searchWeb(query: string) {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * }\n *\n * @Session('support-agent')\n * async handleRequest(userId: string, message: string) {\n * const results = await this.searchWeb(message);\n * return processResults(results);\n * }\n * }\n * ```\n */\n\nimport { SentrialClient, Interaction } from './client.js';\nimport {\n setSessionContext,\n clearSessionContext,\n getSessionContext,\n setDefaultClient,\n _setSessionContextWithTokens,\n _restoreSessionContext,\n type SessionContextTokens,\n} from './wrappers.js';\nimport { createContextVar, type ContextToken } from './async-context.js';\n\n// Re-export context functions\nexport { setSessionContext, clearSessionContext, getSessionContext };\n\n// ============================================================================\n// Context Management\n// ============================================================================\n\nlet _defaultClient: SentrialClient | null = null;\nconst _currentInteraction = createContextVar<Interaction | null>(null);\n\n/**\n * Get the default client, creating one if needed\n */\nfunction getClient(): SentrialClient | null {\n if (!_defaultClient) {\n try {\n _defaultClient = new SentrialClient();\n setDefaultClient(_defaultClient);\n } catch {\n return null;\n }\n }\n return _defaultClient;\n}\n\n/**\n * Set the default client for decorators\n */\nexport function setClient(client: SentrialClient): void {\n _defaultClient = client;\n setDefaultClient(client);\n}\n\n/**\n * Get the current session ID (if inside a session context)\n */\nexport function getCurrentSessionId(): string | null {\n return getSessionContext();\n}\n\n/**\n * Get the current interaction (if inside a session context)\n */\nexport function getCurrentInteraction(): Interaction | null {\n return _currentInteraction.get();\n}\n\n// ============================================================================\n// withTool - Higher-order function for tool tracking\n// ============================================================================\n\ntype AsyncFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => Promise<TReturn>;\ntype SyncFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn;\n\n/**\n * Wrap a function to automatically track it as a tool call.\n *\n * When called within a session context, automatically tracks:\n * - tool_input: Function arguments\n * - tool_output: Return value\n * - duration: Execution time\n * - errors: Any exceptions raised\n *\n * @param name - Name of the tool\n * @param fn - The function to wrap\n * @returns Wrapped function with automatic tracking\n *\n * @example\n * ```ts\n * const searchWeb = withTool('search', async (query: string) => {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * });\n *\n * // Use within a session - automatically tracked\n * const results = await searchWeb('AI agents');\n * ```\n */\nexport function withTool<TArgs extends unknown[], TReturn>(\n name: string,\n fn: AsyncFunction<TArgs, TReturn>\n): AsyncFunction<TArgs, TReturn>;\nexport function withTool<TArgs extends unknown[], TReturn>(\n name: string,\n fn: SyncFunction<TArgs, TReturn>\n): SyncFunction<TArgs, TReturn>;\nexport function withTool<TArgs extends unknown[], TReturn>(\n name: string,\n fn: (...args: TArgs) => TReturn | Promise<TReturn>\n): (...args: TArgs) => TReturn | Promise<TReturn> {\n // Check if function is async\n const isAsync = fn.constructor.name === 'AsyncFunction';\n\n if (isAsync) {\n return async function (...args: TArgs): Promise<TReturn> {\n return trackToolAsync(name, fn as AsyncFunction<TArgs, TReturn>, args);\n };\n } else {\n return function (...args: TArgs): TReturn {\n return trackToolSync(name, fn as SyncFunction<TArgs, TReturn>, args);\n };\n }\n}\n\nasync function trackToolAsync<TArgs extends unknown[], TReturn>(\n toolName: string,\n fn: AsyncFunction<TArgs, TReturn>,\n args: TArgs\n): Promise<TReturn> {\n const startTime = Date.now();\n const toolInput = buildToolInput(args);\n\n const client = getClient();\n const sessionId = getSessionContext();\n\n try {\n const result = await fn(...args);\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n const toolOutput = serializeOutput(result);\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput,\n toolOutput,\n metadata: { duration_ms: durationMs },\n }).catch((err) => {\n console.warn(`Sentrial: Failed to track tool ${toolName}:`, err.message);\n });\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n client.trackError({\n sessionId,\n errorMessage: (error as Error).message,\n errorType: (error as Error).name,\n toolName,\n stackTrace: (error as Error).stack,\n metadata: { duration_ms: durationMs },\n }).catch(() => {});\n }\n\n throw error;\n }\n}\n\nfunction trackToolSync<TArgs extends unknown[], TReturn>(\n toolName: string,\n fn: SyncFunction<TArgs, TReturn>,\n args: TArgs\n): TReturn {\n const startTime = Date.now();\n const toolInput = buildToolInput(args);\n\n const client = getClient();\n const sessionId = getSessionContext();\n\n try {\n const result = fn(...args);\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n const toolOutput = serializeOutput(result);\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput,\n toolOutput,\n metadata: { duration_ms: durationMs },\n }).catch((err) => {\n console.warn(`Sentrial: Failed to track tool ${toolName}:`, err.message);\n });\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n client.trackError({\n sessionId,\n errorMessage: (error as Error).message,\n errorType: (error as Error).name,\n toolName,\n stackTrace: (error as Error).stack,\n metadata: { duration_ms: durationMs },\n }).catch(() => {});\n }\n\n throw error;\n }\n}\n\n// ============================================================================\n// withSession - Higher-order function for session management\n// ============================================================================\n\ninterface SessionOptions {\n /** Parameter index or name for user ID (default: looks for 'userId' or first param) */\n userIdParam?: string | number;\n /** Parameter index or name for input (default: looks for 'input', 'message', 'query') */\n inputParam?: string | number;\n}\n\n/**\n * Wrap a function to automatically manage a session around it.\n *\n * Automatically:\n * - Creates a session when the function is called\n * - Sets up context for withTool and wrapped LLM calls\n * - Captures output from return value\n * - Completes the session when the function returns\n * - Marks as failed if an exception is raised\n *\n * @param agentName - Name of the agent\n * @param fn - The function to wrap\n * @param options - Session options\n * @returns Wrapped function with automatic session management\n *\n * @example\n * ```ts\n * const handleRequest = withSession('support-agent', async (userId: string, message: string) => {\n * // Session automatically created\n * // All withTool calls and wrapped LLM calls are tracked\n * const response = await processRequest(message);\n * return response; // Captured as output\n * });\n *\n * await handleRequest('user_123', 'Hello!');\n * ```\n */\nexport function withSession<TArgs extends unknown[], TReturn>(\n agentName: string,\n fn: AsyncFunction<TArgs, TReturn>,\n options?: SessionOptions\n): AsyncFunction<TArgs, TReturn>;\nexport function withSession<TArgs extends unknown[], TReturn>(\n agentName: string,\n fn: SyncFunction<TArgs, TReturn>,\n options?: SessionOptions\n): AsyncFunction<TArgs, TReturn>; // Session always returns a Promise\nexport function withSession<TArgs extends unknown[], TReturn>(\n agentName: string,\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n options: SessionOptions = {}\n): (...args: TArgs) => Promise<TReturn> {\n return async function (...args: TArgs): Promise<TReturn> {\n const client = getClient();\n if (!client) {\n // No client, just run the function\n return fn(...args) as Promise<TReturn>;\n }\n\n // Extract userId and input from args\n const { userId, userInput } = extractParams(args, options);\n\n // Create interaction\n const interaction = await client.begin({\n userId,\n event: agentName,\n input: userInput,\n });\n\n // Save previous context and set new one (supports nesting)\n const sessionId = interaction.getSessionId();\n let sessionTokens: SessionContextTokens | undefined;\n if (sessionId) {\n sessionTokens = _setSessionContextWithTokens(sessionId, client);\n }\n const interactionToken = _currentInteraction.set(interaction);\n\n try {\n const result = await fn(...args);\n\n // Capture output\n let output: string | undefined;\n if (typeof result === 'string') {\n output = result;\n } else if (result && typeof result === 'object') {\n if ('response' in result) {\n output = String((result as any).response);\n } else if ('output' in result) {\n output = String((result as any).output);\n }\n }\n if (output === undefined && result !== null && result !== undefined) {\n output = String(result).slice(0, 1000);\n }\n\n await interaction.finish({ output, success: true });\n return result;\n } catch (error) {\n await interaction.finish({\n success: false,\n failureReason: `${(error as Error).name}: ${(error as Error).message}`,\n });\n throw error;\n } finally {\n // Restore previous context (supports nesting)\n if (sessionTokens) {\n _restoreSessionContext(sessionTokens);\n }\n _currentInteraction.reset(interactionToken);\n }\n };\n}\n\nfunction extractParams(\n args: unknown[],\n options: SessionOptions\n): { userId: string; userInput: string | undefined } {\n let userId = 'anonymous';\n let userInput: string | undefined;\n\n // Extract userId\n if (typeof options.userIdParam === 'number') {\n userId = String(args[options.userIdParam] ?? 'anonymous');\n } else if (typeof options.userIdParam === 'string') {\n // Can't access by name in JS, use index 0\n userId = String(args[0] ?? 'anonymous');\n } else {\n // Default: first arg is userId\n userId = String(args[0] ?? 'anonymous');\n }\n\n // Extract input\n if (typeof options.inputParam === 'number') {\n userInput = String(args[options.inputParam] ?? '');\n } else {\n // Default: second arg is input\n const input = args[1];\n if (typeof input === 'string') {\n userInput = input;\n } else if (Array.isArray(input)) {\n userInput = JSON.stringify(input).slice(0, 500);\n }\n }\n\n return { userId, userInput };\n}\n\n// ============================================================================\n// TypeScript Decorators (requires experimentalDecorators)\n// ============================================================================\n\n/**\n * Method decorator to track a method as a tool call.\n *\n * @param name - Name of the tool (optional, defaults to method name)\n *\n * @example\n * ```ts\n * class MyAgent {\n * @Tool('search')\n * async searchWeb(query: string) {\n * return await fetch(`/search?q=${query}`).then(r => r.json());\n * }\n * }\n * ```\n */\nexport function Tool(name?: string): MethodDecorator {\n return function (\n _target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const originalMethod = descriptor.value;\n const toolName = name ?? String(propertyKey);\n\n descriptor.value = async function (...args: unknown[]) {\n const startTime = Date.now();\n const toolInput = buildToolInput(args);\n\n const client = getClient();\n const sessionId = getSessionContext();\n\n try {\n const result = await originalMethod.apply(this, args);\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n const toolOutput = serializeOutput(result);\n client.trackToolCall({\n sessionId,\n toolName,\n toolInput,\n toolOutput,\n metadata: { duration_ms: durationMs },\n }).catch((err) => {\n console.warn(`Sentrial: Failed to track tool ${toolName}:`, err.message);\n });\n }\n\n return result;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n\n if (client && sessionId) {\n client.trackError({\n sessionId,\n errorMessage: (error as Error).message,\n errorType: (error as Error).name,\n toolName,\n stackTrace: (error as Error).stack,\n metadata: { duration_ms: durationMs },\n }).catch(() => {});\n }\n\n throw error;\n }\n };\n\n return descriptor;\n };\n}\n\n/**\n * Method decorator to wrap a method in a session.\n *\n * @param agentName - Name of the agent (optional, defaults to class name)\n * @param options - Session options\n *\n * @example\n * ```ts\n * class MyAgent {\n * @TrackSession('support-agent')\n * async handleRequest(userId: string, message: string) {\n * // Session automatically created\n * const results = await this.searchWeb(message);\n * return processResults(results);\n * }\n * }\n * ```\n */\nexport function TrackSession(agentName?: string, options?: SessionOptions): MethodDecorator {\n return function (\n target: object,\n _propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const originalMethod = descriptor.value;\n const agent = agentName ?? target.constructor.name;\n\n descriptor.value = async function (...args: unknown[]) {\n const client = getClient();\n if (!client) {\n return originalMethod.apply(this, args);\n }\n\n const { userId, userInput } = extractParams(args, options ?? {});\n\n const interaction = await client.begin({\n userId,\n event: agent,\n input: userInput,\n });\n\n // Save previous context and set new one (supports nesting)\n const sessionId = interaction.getSessionId();\n let sessionTokens: SessionContextTokens | undefined;\n if (sessionId) {\n sessionTokens = _setSessionContextWithTokens(sessionId, client);\n }\n const interactionToken = _currentInteraction.set(interaction);\n\n try {\n const result = await originalMethod.apply(this, args);\n\n let output: string | undefined;\n if (typeof result === 'string') {\n output = result;\n } else if (result && typeof result === 'object') {\n if ('response' in result) {\n output = String((result as any).response);\n } else if ('output' in result) {\n output = String((result as any).output);\n }\n }\n if (output === undefined && result !== null && result !== undefined) {\n output = String(result).slice(0, 1000);\n }\n\n await interaction.finish({ output, success: true });\n return result;\n } catch (error) {\n await interaction.finish({\n success: false,\n failureReason: `${(error as Error).name}: ${(error as Error).message}`,\n });\n throw error;\n } finally {\n // Restore previous context (supports nesting)\n if (sessionTokens) {\n _restoreSessionContext(sessionTokens);\n }\n _currentInteraction.reset(interactionToken);\n }\n };\n\n return descriptor;\n };\n}\n\n// ============================================================================\n// SessionContext - Manual session control\n// ============================================================================\n\n/**\n * Manual session context for when you need more control.\n *\n * @example\n * ```ts\n * const ctx = new SessionContext({\n * userId: 'user_123',\n * agent: 'my-agent',\n * input: 'Hello!',\n * });\n *\n * await ctx.start();\n * try {\n * // All tool calls are tracked\n * const result = await myTool(...);\n * ctx.setOutput(result);\n * await ctx.finish();\n * } catch (error) {\n * await ctx.finish({ success: false, error: error.message });\n * }\n * ```\n */\nexport class SessionContext {\n private readonly userId: string;\n private readonly agent: string;\n private readonly input?: string;\n private readonly client: SentrialClient | null;\n private interaction: Interaction | null = null;\n private output?: string;\n private sessionTokens?: SessionContextTokens;\n private interactionToken?: ContextToken<Interaction | null>;\n\n constructor(options: { userId: string; agent: string; input?: string; client?: SentrialClient }) {\n this.userId = options.userId;\n this.agent = options.agent;\n this.input = options.input;\n this.client = options.client ?? getClient();\n }\n\n /**\n * Start the session\n */\n async start(): Promise<this> {\n if (!this.client) return this;\n\n this.interaction = await this.client.begin({\n userId: this.userId,\n event: this.agent,\n input: this.input,\n });\n\n const sessionId = this.interaction.getSessionId();\n if (sessionId) {\n this.sessionTokens = _setSessionContextWithTokens(sessionId, this.client);\n }\n this.interactionToken = _currentInteraction.set(this.interaction);\n\n return this;\n }\n\n /**\n * Set the output for this session\n */\n setOutput(output: string): void {\n this.output = output;\n }\n\n /**\n * Finish the session\n */\n async finish(options?: { success?: boolean; error?: string }): Promise<void> {\n if (this.interaction) {\n await this.interaction.finish({\n output: this.output,\n success: options?.success ?? true,\n failureReason: options?.error,\n });\n }\n\n // Restore previous context (supports nesting)\n if (this.sessionTokens) {\n _restoreSessionContext(this.sessionTokens);\n }\n if (this.interactionToken) {\n _currentInteraction.reset(this.interactionToken);\n }\n }\n\n /**\n * Get the session ID\n */\n get sessionId(): string | null {\n return this.interaction?.getSessionId() ?? null;\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction buildToolInput(args: unknown[]): Record<string, unknown> {\n if (args.length === 0) {\n return {};\n }\n if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) {\n return serializeValue(args[0]) as Record<string, unknown>;\n }\n return {\n args: args.map(serializeValue),\n };\n}\n\nfunction serializeValue(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(serializeValue);\n }\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = serializeValue(v);\n }\n return result;\n }\n try {\n return String(value).slice(0, 1000);\n } catch {\n return `<${typeof value}>`;\n }\n}\n\nfunction serializeOutput(value: unknown): Record<string, unknown> {\n if (value === null || value === undefined) {\n return { result: null };\n }\n if (typeof value === 'object' && !Array.isArray(value)) {\n return serializeValue(value) as Record<string, unknown>;\n }\n return { result: serializeValue(value) };\n}\n","/**\n * Sentrial Experiment Context\n *\n * Provides context-aware configuration for experiments. When running experiments\n * via the CLI, the system prompt and other experiment config is automatically\n * available to your agent code via simple function calls.\n *\n * @example\n * ```ts\n * import { getSystemPrompt } from '@sentrial/sdk';\n *\n * async function myAgent(query: string): Promise<string> {\n * // When running normally: returns your default\n * // When running experiment: returns the variant's system prompt\n * const systemPrompt = getSystemPrompt('You are a helpful assistant.');\n *\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [\n * { role: 'system', content: systemPrompt },\n * { role: 'user', content: query },\n * ],\n * });\n *\n * return response.choices[0].message.content!;\n * }\n * ```\n *\n * That's it! No other changes needed. The experiment runner handles setting the context.\n */\n\n// ============================================================================\n// Experiment Context Types\n// ============================================================================\n\n/**\n * Context for the current experiment run.\n */\nexport interface ExperimentContext {\n /** The system prompt for this variant */\n systemPrompt: string;\n /** Name of the variant being tested */\n variantName: string;\n /** The experiment ID */\n experimentId: string;\n /** The test case session ID (if applicable) */\n testCaseId?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Context Storage (async-safe via AsyncLocalStorage in Node.js)\n// ============================================================================\n\nimport { createContextVar } from './async-context.js';\n\nconst _experimentContext = createContextVar<ExperimentContext | null>(null);\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Get the current system prompt.\n *\n * When running an experiment, this returns the variant's system prompt.\n * Otherwise, returns your default.\n *\n * @param defaultPrompt - The default system prompt to use when not in experiment mode.\n * This should be your normal production prompt.\n * @returns The system prompt to use.\n *\n * @example\n * ```ts\n * import { getSystemPrompt } from '@sentrial/sdk';\n *\n * async function myAgent(query: string) {\n * // Just wrap your existing prompt with getSystemPrompt()\n * const prompt = getSystemPrompt('You are a helpful weather assistant.');\n *\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [\n * { role: 'system', content: prompt },\n * { role: 'user', content: query },\n * ],\n * });\n *\n * return response.choices[0].message.content;\n * }\n * ```\n */\nexport function getSystemPrompt(defaultPrompt?: string): string {\n const ctx = _experimentContext.get();\n if (ctx?.systemPrompt) {\n return ctx.systemPrompt;\n }\n return defaultPrompt ?? '';\n}\n\n/**\n * Get the full experiment context if running in experiment mode.\n *\n * Returns null if not running an experiment.\n *\n * @returns ExperimentContext with variant info, or null.\n *\n * @example\n * ```ts\n * import { getExperimentContext } from '@sentrial/sdk';\n *\n * const ctx = getExperimentContext();\n * if (ctx) {\n * console.log(`Running variant: ${ctx.variantName}`);\n * }\n * ```\n */\nexport function getExperimentContext(): ExperimentContext | null {\n return _experimentContext.get();\n}\n\n/**\n * Check if currently running in experiment mode.\n *\n * @returns True if running via experiment runner, false otherwise.\n */\nexport function isExperimentMode(): boolean {\n return _experimentContext.get() !== null;\n}\n\n/**\n * Get the current variant name if in experiment mode.\n *\n * @returns The variant name (e.g., \"Control\", \"Variant A\"), or null.\n */\nexport function getVariantName(): string | null {\n return _experimentContext.get()?.variantName ?? null;\n}\n\n/**\n * Get the current experiment ID if in experiment mode.\n *\n * @returns The experiment ID, or null.\n */\nexport function getExperimentId(): string | null {\n return _experimentContext.get()?.experimentId ?? null;\n}\n\n// ============================================================================\n// Internal Functions (used by experiment runner)\n// ============================================================================\n\n/**\n * Set the experiment context. Called by the experiment runner before running agent.\n *\n * @internal This is an internal function - users don't need to call this directly.\n */\nexport function setExperimentContext(context: ExperimentContext): void {\n _experimentContext.set(context);\n}\n\n/**\n * Clear the experiment context. Called by the experiment runner after agent completes.\n *\n * @internal This is an internal function - users don't need to call this directly.\n */\nexport function clearExperimentContext(): void {\n _experimentContext.set(null);\n}\n","/**\n * Sentrial Experiments - Run prompt A/B tests with results synced to Sentrial\n *\n * @example\n * ```ts\n * import { Experiment } from '@sentrial/sdk';\n *\n * const experiment = new Experiment('exp_abc123');\n *\n * // Get config\n * await experiment.load();\n * console.log(`Testing ${experiment.variants.length} variants on ${experiment.testCases.length} inputs`);\n *\n * // Run with your agent function\n * await experiment.run(async (testCase, variant, tracker) => {\n * // Create a session for this run\n * const interaction = await client.begin({\n * userId: 'experiment',\n * event: 'my-agent',\n * input: testCase.userInput,\n * });\n * tracker.setResultSessionId(interaction.getSessionId()!);\n *\n * // Run your agent with the variant's system prompt\n * const response = await runAgent(testCase.userInput, variant.systemPrompt);\n *\n * await interaction.finish({ output: response, success: true });\n * });\n * ```\n *\n * @example Manual run tracking\n * ```ts\n * const experiment = new Experiment('exp_abc123');\n * await experiment.load();\n *\n * for (const variant of experiment.variants) {\n * for (const testCase of experiment.testCases) {\n * const tracker = await experiment.trackRun(variant.name, testCase.sessionId);\n * try {\n * const sessionId = await runAgent(testCase.userInput, variant.systemPrompt);\n * tracker.setResultSessionId(sessionId);\n * await tracker.complete();\n * } catch (error) {\n * await tracker.fail(error.message);\n * }\n * }\n * }\n * ```\n */\n\nimport { SentrialClient } from './client.js';\nimport { setExperimentContext, clearExperimentContext } from './context.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A prompt variant to test.\n */\nexport interface ExperimentVariant {\n /** Name of the variant */\n name: string;\n /** The system prompt for this variant */\n systemPrompt: string;\n /** Optional description */\n description?: string;\n}\n\n/**\n * A test case (input) to run against each variant.\n */\nexport interface ExperimentTestCase {\n /** Session ID of the original session */\n sessionId: string;\n /** The user input to test */\n userInput: string;\n}\n\n/**\n * Result of a single experiment run.\n */\nexport interface ExperimentRunResult {\n /** Name of the variant */\n variantName: string;\n /** Session ID of the original test case */\n testCaseSessionId: string;\n /** Session ID of the result session (if created) */\n resultSessionId?: string;\n /** Whether the run succeeded */\n success: boolean;\n /** Error message if failed */\n errorMessage?: string;\n}\n\n/**\n * Aggregated experiment results.\n */\nexport interface ExperimentResults {\n experimentId: string;\n status: string;\n variants: Array<{\n name: string;\n successRate: number;\n avgScore?: number;\n avgCost?: number;\n avgDuration?: number;\n runCount: number;\n }>;\n winner?: string;\n}\n\n// ============================================================================\n// Run Tracker\n// ============================================================================\n\n/**\n * Tracks a single experiment run.\n *\n * Created by Experiment.trackRun() - use this to manually track runs.\n */\nexport class ExperimentRunTracker {\n private readonly experiment: Experiment;\n private readonly variantName: string;\n private readonly baseSessionId: string;\n private runId?: string;\n private resultSessionId?: string;\n private _success = true;\n private _errorMessage?: string;\n\n /** @internal */\n constructor(experiment: Experiment, variantName: string, baseSessionId: string) {\n this.experiment = experiment;\n this.variantName = variantName;\n this.baseSessionId = baseSessionId;\n }\n\n /**\n * Start the run - creates a run record via API.\n */\n async start(): Promise<this> {\n try {\n const response = await this.experiment.request<{ run?: { id: string } }>(\n 'POST',\n `/api/experiments/${this.experiment.experimentId}/runs`,\n {\n variantName: this.variantName,\n baseSessionId: this.baseSessionId,\n }\n );\n this.runId = response?.run?.id;\n } catch {\n // Ignore errors starting the run\n }\n return this;\n }\n\n /**\n * Set the session ID of the result session.\n */\n setResultSessionId(sessionId: string): void {\n this.resultSessionId = sessionId;\n }\n\n /**\n * Mark the run as complete.\n */\n async complete(): Promise<void> {\n if (!this.runId) return;\n\n try {\n await this.experiment.request(\n 'PATCH',\n `/api/experiments/${this.experiment.experimentId}/runs/${this.runId}`,\n {\n status: 'completed',\n resultSessionId: this.resultSessionId,\n }\n );\n } catch {\n // Ignore errors completing the run\n }\n }\n\n /**\n * Mark the run as failed.\n */\n async fail(errorMessage: string): Promise<void> {\n this._success = false;\n this._errorMessage = errorMessage;\n\n if (!this.runId) return;\n\n try {\n await this.experiment.request(\n 'PATCH',\n `/api/experiments/${this.experiment.experimentId}/runs/${this.runId}`,\n {\n status: 'failed',\n resultSessionId: this.resultSessionId,\n errorMessage,\n }\n );\n } catch {\n // Ignore errors failing the run\n }\n }\n\n /**\n * Get the result of this run.\n */\n getResult(): ExperimentRunResult {\n return {\n variantName: this.variantName,\n testCaseSessionId: this.baseSessionId,\n resultSessionId: this.resultSessionId,\n success: this._success,\n errorMessage: this._errorMessage,\n };\n }\n}\n\n// ============================================================================\n// Experiment Class\n// ============================================================================\n\ntype AgentFunction = (\n testCase: ExperimentTestCase,\n variant: ExperimentVariant,\n tracker: ExperimentRunTracker\n) => Promise<void>;\n\n/**\n * Run experiments with results synced to Sentrial.\n *\n * @example\n * ```ts\n * const experiment = new Experiment('exp_abc123');\n *\n * await experiment.load();\n * console.log(`Testing ${experiment.variants.length} variants`);\n *\n * await experiment.run(async (testCase, variant, tracker) => {\n * const sessionId = await runMyAgent(testCase.userInput, variant.systemPrompt);\n * tracker.setResultSessionId(sessionId);\n * });\n * ```\n */\nexport class Experiment {\n /** The experiment ID */\n readonly experimentId: string;\n\n /** @internal */\n readonly client: SentrialClient;\n /** @internal */\n readonly apiUrl: string;\n /** @internal */\n readonly apiKey?: string;\n \n private config?: Record<string, unknown>;\n private _variants?: ExperimentVariant[];\n private _testCases?: ExperimentTestCase[];\n\n /**\n * Create an experiment instance.\n *\n * @param experimentId - The experiment ID from Sentrial dashboard\n * @param options - Configuration options\n */\n constructor(\n experimentId: string,\n options: {\n apiKey?: string;\n apiUrl?: string;\n } = {}\n ) {\n this.experimentId = experimentId;\n this.apiUrl = (\n options.apiUrl ??\n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_URL : undefined) ??\n 'https://api.sentrial.com'\n ).replace(/\\/$/, '');\n this.apiKey = options.apiKey ?? \n (typeof process !== 'undefined' ? process.env?.SENTRIAL_API_KEY : undefined);\n this.client = new SentrialClient({\n apiKey: this.apiKey,\n apiUrl: this.apiUrl,\n failSilently: false, // We want errors for experiments\n });\n }\n\n /**\n * Make an HTTP request to the API\n * @internal\n */\n async request<T>(method: string, path: string, body?: unknown): Promise<T | null> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n const response = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n return null;\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Load the experiment configuration from the API.\n */\n async load(): Promise<this> {\n interface ExperimentConfig {\n experiment?: {\n name?: string;\n status?: string;\n variants?: Array<{\n name: string;\n systemPrompt?: string;\n description?: string;\n }>;\n testCases?: Array<{\n sessionId: string;\n userInput?: string;\n }>;\n };\n }\n\n const response = await this.request<ExperimentConfig>(\n 'GET',\n `/api/experiments/${this.experimentId}/runs`\n );\n\n if (!response?.experiment) {\n throw new Error(`Failed to load experiment config for ${this.experimentId}`);\n }\n\n this.config = response.experiment;\n\n // Parse variants\n const variants = this.config.variants as Array<{ name: string; systemPrompt?: string; description?: string }> | undefined;\n this._variants = variants?.map((v) => ({\n name: v.name ?? '',\n systemPrompt: v.systemPrompt ?? '',\n description: v.description,\n })) ?? [];\n\n // Parse test cases (only include those with input)\n const testCases = this.config.testCases as Array<{ sessionId: string; userInput?: string }> | undefined;\n this._testCases = testCases\n ?.filter((tc) => tc.userInput)\n ?.map((tc) => ({\n sessionId: tc.sessionId ?? '',\n userInput: tc.userInput ?? '',\n })) ?? [];\n\n return this;\n }\n\n /**\n * Get experiment name.\n */\n get name(): string {\n return (this.config?.name as string) ?? '';\n }\n\n /**\n * Get experiment status.\n */\n get status(): string {\n return (this.config?.status as string) ?? '';\n }\n\n /**\n * Get list of variants to test.\n */\n get variants(): ExperimentVariant[] {\n if (!this._variants) {\n throw new Error('Experiment not loaded. Call load() first.');\n }\n return this._variants;\n }\n\n /**\n * Get list of test cases.\n */\n get testCases(): ExperimentTestCase[] {\n if (!this._testCases) {\n throw new Error('Experiment not loaded. Call load() first.');\n }\n return this._testCases;\n }\n\n /**\n * Get a specific variant by name.\n */\n getVariant(name: string): ExperimentVariant | undefined {\n return this.variants.find((v) => v.name === name);\n }\n\n /**\n * Create a run tracker for manual experiment runs.\n */\n async trackRun(variantName: string, baseSessionId: string): Promise<ExperimentRunTracker> {\n const tracker = new ExperimentRunTracker(this, variantName, baseSessionId);\n await tracker.start();\n return tracker;\n }\n\n /**\n * Run the experiment with all variants and test cases.\n *\n * @param agentFn - Function that runs your agent with a test case and variant\n * @param options - Run options\n * @returns List of run results\n */\n async run(\n agentFn: AgentFunction,\n options: {\n /** Run test cases in parallel */\n parallel?: boolean;\n /** Max parallel workers */\n maxWorkers?: number;\n } = {}\n ): Promise<ExperimentRunResult[]> {\n const { parallel = false, maxWorkers = 4 } = options;\n\n if (!this._variants || !this._testCases) {\n await this.load();\n }\n\n const results: ExperimentRunResult[] = [];\n const totalRuns = this.variants.length * this.testCases.length;\n let completed = 0;\n\n console.log(`Running experiment: ${this.name || this.experimentId}`);\n console.log(` Variants: ${this.variants.length}`);\n console.log(` Test cases: ${this.testCases.length}`);\n console.log(` Total runs: ${totalRuns}`);\n console.log();\n\n const runSingle = async (\n variant: ExperimentVariant,\n testCase: ExperimentTestCase\n ): Promise<ExperimentRunResult> => {\n const tracker = await this.trackRun(variant.name, testCase.sessionId);\n\n // Set experiment context so getSystemPrompt() returns the variant prompt\n setExperimentContext({\n systemPrompt: variant.systemPrompt,\n variantName: variant.name,\n experimentId: this.experimentId,\n testCaseId: testCase.sessionId,\n });\n\n try {\n await agentFn(testCase, variant, tracker);\n await tracker.complete();\n return tracker.getResult();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n await tracker.fail(errorMessage);\n return tracker.getResult();\n } finally {\n clearExperimentContext();\n }\n };\n\n if (parallel) {\n // Run in parallel with limited concurrency\n const queue: Array<() => Promise<void>> = [];\n\n for (const variant of this.variants) {\n for (const testCase of this.testCases) {\n queue.push(async () => {\n const result = await runSingle(variant, testCase);\n results.push(result);\n completed++;\n const status = result.success ? '✓' : '✗';\n console.log(\n ` [${completed}/${totalRuns}] ${status} ${variant.name} x ${testCase.sessionId.slice(0, 8)}...`\n );\n });\n }\n }\n\n // Process queue with limited concurrency\n const executing: Promise<void>[] = [];\n for (const task of queue) {\n const promise = task().then(() => {\n executing.splice(executing.indexOf(promise), 1);\n });\n executing.push(promise);\n\n if (executing.length >= maxWorkers) {\n await Promise.race(executing);\n }\n }\n await Promise.all(executing);\n } else {\n // Run sequentially\n for (const variant of this.variants) {\n for (const testCase of this.testCases) {\n const result = await runSingle(variant, testCase);\n results.push(result);\n completed++;\n const status = result.success ? '✓' : '✗';\n console.log(\n ` [${completed}/${totalRuns}] ${status} ${variant.name} x ${testCase.sessionId.slice(0, 8)}...`\n );\n }\n }\n }\n\n console.log();\n console.log('Experiment complete!');\n console.log(` Successful: ${results.filter((r) => r.success).length}/${totalRuns}`);\n console.log(` Failed: ${results.filter((r) => !r.success).length}/${totalRuns}`);\n\n return results;\n }\n\n /**\n * Reset all runs for this experiment (for re-running).\n */\n async reset(): Promise<boolean> {\n try {\n const response = await this.request(\n 'DELETE',\n `/api/experiments/${this.experimentId}/runs`\n );\n\n if (response !== null) {\n // Clear cached config\n this.config = undefined;\n this._variants = undefined;\n this._testCases = undefined;\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Fetch aggregated results from the API.\n */\n async getResults(): Promise<ExperimentResults | null> {\n try {\n const response = await this.request<ExperimentResults>(\n 'GET',\n `/api/experiments/${this.experimentId}/results`\n );\n return response;\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"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","names":["EventType","_defaultClient","getClient","_defaultClient","getClient"]}
|