@sentrial/sdk 0.3.1 → 0.3.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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../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 * 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 // Observe the text result without mutating — v5+ makes .text a read-only getter\n const textProp = result.text;\n if (typeof textProp === 'string') {\n trackCompletion(textProp).catch(() => {});\n } else if (textProp != null && typeof (textProp as Promise<string>).then === 'function') {\n (textProp as Promise<string>)\n .then((text) => { trackCompletion(text).catch(() => {}); })\n .catch((err) => {\n trackCompletion('', err instanceof Error ? err : new Error(String(err))).catch(() => {});\n });\n } else {\n // Legacy fallback (v3): wrap textStream to capture full text.\n // Check writability first — v5+ makes textStream a read-only getter.\n const desc = Object.getOwnPropertyDescriptor(result, 'textStream')\n ?? Object.getOwnPropertyDescriptor(Object.getPrototypeOf(result), 'textStream');\n const isWritable = !desc || desc.writable || desc.set;\n\n if (isWritable) {\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\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 // Observe without mutating — v5+ makes .object a read-only getter\n const objectProp = result.object;\n if (objectProp && typeof (objectProp as Promise<unknown>).then === 'function') {\n (objectProp as Promise<unknown>)\n .then(async (obj) => {\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n if (!sid) return;\n\n let usage: UsageInfo | undefined;\n try {\n usage = result.usage ? await result.usage : undefined;\n } catch { /* ignore */ }\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 .catch(async (error) => {\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n if (!sid) return;\n\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\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/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 * 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.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 // Use Proxy to intercept textStream — tracking runs inline with consumption\n // so it completes before serverless runtimes shut down. No read-only getter issues.\n const originalTextStream = result.textStream;\n let wrappedTextStream: AsyncIterable<string> | null = null;\n\n const proxied = new Proxy(result, {\n get(target, prop, receiver) {\n if (prop === 'textStream') {\n if (!wrappedTextStream) {\n let fullText = '';\n wrappedTextStream = (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 wrappedTextStream;\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n\n // Fallback: observe result.text Promise for consumers that don't iterate textStream\n // (e.g., toDataStreamResponse). Fire-and-forget — may not complete in serverless,\n // but covers long-running servers and cases where textStream isn't consumed directly.\n const textProp = result.text;\n if (typeof textProp === 'string') {\n trackCompletion(textProp).catch(() => {});\n } else if (textProp != null && typeof (textProp as Promise<string>).then === 'function') {\n (textProp as Promise<string>)\n .then((text) => { trackCompletion(text).catch(() => {}); })\n .catch((err) => {\n trackCompletion('', err instanceof Error ? err : new Error(String(err))).catch(() => {});\n });\n }\n\n return proxied as StreamTextResult;\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 // Use Proxy to intercept .object — tracking runs inline with the consumer's await,\n // so it completes before serverless runtimes shut down. No read-only getter issues.\n const originalObjectPromise = result.object;\n if (originalObjectPromise && typeof (originalObjectPromise as Promise<unknown>).then === 'function') {\n let tracked = false;\n let wrappedObjectPromise: Promise<unknown> | null = null;\n\n return new Proxy(result, {\n get(target, prop, receiver) {\n if (prop === 'object') {\n if (!wrappedObjectPromise) {\n wrappedObjectPromise = (originalObjectPromise as Promise<unknown>)\n .then(async (obj) => {\n if (tracked) return obj;\n tracked = true;\n\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n if (!sid) return obj;\n\n let usage: UsageInfo | undefined;\n try {\n usage = result.usage ? await result.usage : undefined;\n } catch { /* ignore */ }\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 return obj;\n })\n .catch(async (error) => {\n if (tracked) throw error;\n tracked = true;\n\n const durationMs = Date.now() - startTime;\n const sid = await sessionPromise;\n if (!sid) throw error;\n\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 throw error;\n });\n }\n return wrappedObjectPromise;\n }\n return Reflect.get(target, prop, receiver);\n },\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"]}