@tangle-network/sdk-telemetry 0.1.8

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["DEFAULT_CONFIG","GEN_AI_REQUEST_MODEL","GEN_AI_RESPONSE_MODEL","GEN_AI_USAGE_INPUT_TOKENS","GEN_AI_USAGE_OUTPUT_TOKENS","GEN_AI_OPERATION_NAME","GEN_AI_CONVERSATION_ID","coreParseSSEData","DEFAULT_CONFIG"],"sources":["../src/credits.ts","../src/internal/best-effort.ts","../src/enrichment/types.ts","../src/enrichment/adapter-registry.ts","../src/enrichment/json-adapter.ts","../src/enrichment/index.ts","../src/trace-types.ts","../src/langfuse-sink.ts","../src/otel-provider.ts","../src/otel-sink.ts","../src/sinks.ts","../src/sse-parser.ts","../src/trace-collector.ts","../src/tracker.ts","../src/usage.ts"],"sourcesContent":["/**\n * Credit Tracker\n *\n * Real-time credit balance monitoring with threshold alerts,\n * reservation system for pre-authorization, and transaction history.\n */\n\nimport type { Storage } from \"@tangle-network/agent-core\";\nimport type {\n CreditBalance,\n CreditProvider,\n CreditTrackerConfig,\n CreditTransaction,\n} from \"./types.js\";\n\nconst DEFAULT_CONFIG = {\n refreshIntervalMs: 60000,\n warningThreshold: 0.2,\n criticalThreshold: 0.05,\n};\n\nexport class CreditTracker {\n private readonly config: Required<\n Omit<\n CreditTrackerConfig,\n | \"endpoint\"\n | \"userId\"\n | \"onLowBalance\"\n | \"onCriticalBalance\"\n | \"onBalanceUpdate\"\n >\n > &\n CreditTrackerConfig;\n private readonly provider: CreditProvider;\n private readonly storage?: Storage;\n private cachedBalance?: CreditBalance;\n private lastRefresh?: Date;\n private refreshTimer?: ReturnType<typeof setInterval>;\n private reservations = new Map<\n string,\n { amount: number; createdAt: Date; expiresAt?: Date }\n >();\n\n constructor(\n config: CreditTrackerConfig,\n provider: CreditProvider,\n storage?: Storage,\n ) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.provider = provider;\n this.storage = storage;\n\n if (this.config.refreshIntervalMs > 0) {\n this.startRefreshTimer();\n }\n }\n\n async getBalance(forceRefresh = false): Promise<CreditBalance> {\n if (forceRefresh || !this.cachedBalance || this.isStale()) {\n this.cachedBalance = await this.provider.getBalance();\n this.lastRefresh = new Date();\n this.checkThresholds(this.cachedBalance);\n\n if (this.storage) {\n await this.storage.set(\n `credits:${this.config.projectRef}:balance`,\n this.cachedBalance,\n );\n }\n\n this.config.onBalanceUpdate?.(this.cachedBalance);\n }\n\n return this.cachedBalance;\n }\n\n async getTransactions(options?: {\n limit?: number;\n offset?: number;\n since?: Date;\n }): Promise<CreditTransaction[]> {\n return this.provider.getTransactions(options);\n }\n\n async hasCredits(amount: number): Promise<boolean> {\n const balance = await this.getBalance();\n const reservedTotal = this.getReservedTotal();\n return balance.available - reservedTotal >= amount;\n }\n\n async reserve(\n amount: number,\n options?: { ttlMs?: number },\n ): Promise<string | null> {\n if (!(await this.hasCredits(amount))) {\n return null;\n }\n\n const reservationId = await this.provider.reserveCredit(amount);\n if (!reservationId) return null;\n\n const now = new Date();\n this.reservations.set(reservationId, {\n amount,\n createdAt: now,\n expiresAt: options?.ttlMs\n ? new Date(now.getTime() + options.ttlMs)\n : undefined,\n });\n\n return reservationId;\n }\n\n async release(reservationId: string): Promise<void> {\n await this.provider.releaseReservation(reservationId);\n this.reservations.delete(reservationId);\n }\n\n async charge(\n reservationId: string,\n actualAmount?: number,\n ): Promise<CreditTransaction> {\n const transaction = await this.provider.chargeReservation(\n reservationId,\n actualAmount,\n );\n this.reservations.delete(reservationId);\n\n await this.getBalance(true);\n\n return transaction;\n }\n\n async chargeImmediate(\n amount: number,\n _description: string,\n ): Promise<CreditTransaction | null> {\n const reservationId = await this.reserve(amount);\n if (!reservationId) return null;\n\n return this.charge(reservationId, amount);\n }\n\n getReservedTotal(): number {\n this.cleanupExpiredReservations();\n let total = 0;\n for (const reservation of this.reservations.values()) {\n total += reservation.amount;\n }\n return total;\n }\n\n getEffectiveBalance(): number {\n const balance = this.cachedBalance;\n if (!balance) return 0;\n return balance.available - this.getReservedTotal();\n }\n\n getUsagePercent(): number {\n const balance = this.cachedBalance;\n if (!balance || balance.limit === 0) return 0;\n return (balance.used / balance.limit) * 100;\n }\n\n getRemainingPercent(): number {\n const balance = this.cachedBalance;\n if (!balance || balance.limit === 0) return 100;\n return (balance.available / balance.limit) * 100;\n }\n\n isLow(): boolean {\n return this.getRemainingPercent() / 100 <= this.config.warningThreshold;\n }\n\n isCritical(): boolean {\n return this.getRemainingPercent() / 100 <= this.config.criticalThreshold;\n }\n\n async close(): Promise<void> {\n this.stopRefreshTimer();\n\n for (const [id] of this.reservations) {\n try {\n await this.release(id);\n } catch {\n // Best effort cleanup\n }\n }\n }\n\n private isStale(): boolean {\n if (!this.lastRefresh) return true;\n return (\n Date.now() - this.lastRefresh.getTime() > this.config.refreshIntervalMs\n );\n }\n\n private checkThresholds(balance: CreditBalance): void {\n if (!balance.limit || balance.limit <= 0) return;\n const remainingPercent = balance.available / balance.limit;\n\n if (remainingPercent <= this.config.criticalThreshold) {\n this.config.onCriticalBalance?.(balance);\n } else if (remainingPercent <= this.config.warningThreshold) {\n this.config.onLowBalance?.(balance);\n }\n }\n\n private cleanupExpiredReservations(): void {\n const now = Date.now();\n for (const [id, reservation] of this.reservations) {\n if (reservation.expiresAt && reservation.expiresAt.getTime() < now) {\n this.reservations.delete(id);\n }\n }\n }\n\n private startRefreshTimer(): void {\n if (this.refreshTimer) return;\n this.refreshTimer = setInterval(\n () => this.getBalance(true),\n this.config.refreshIntervalMs,\n );\n }\n\n private stopRefreshTimer(): void {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n }\n}\n\n// In-memory credit provider for testing/development\nexport class MemoryCreditProvider implements CreditProvider {\n private balance: CreditBalance;\n private transactions: CreditTransaction[] = [];\n private reservations = new Map<string, number>();\n\n constructor(initialBalance: Partial<CreditBalance> = {}) {\n this.balance = {\n available: initialBalance.available ?? 1000,\n used: initialBalance.used ?? 0,\n limit: initialBalance.limit ?? 1000,\n currency: initialBalance.currency ?? \"credits\",\n };\n }\n\n async getBalance(): Promise<CreditBalance> {\n return { ...this.balance };\n }\n\n async getTransactions(options?: {\n limit?: number;\n offset?: number;\n since?: Date;\n }): Promise<CreditTransaction[]> {\n let result = [...this.transactions];\n\n if (options?.since) {\n const since = options.since;\n result = result.filter((t) => t.timestamp >= since);\n }\n\n result = result.slice(\n options?.offset ?? 0,\n (options?.offset ?? 0) + (options?.limit ?? 100),\n );\n\n return result;\n }\n\n async checkCredit(amount: number): Promise<boolean> {\n const reservedTotal = Array.from(this.reservations.values()).reduce(\n (sum, amt) => sum + amt,\n 0,\n );\n return this.balance.available - reservedTotal >= amount;\n }\n\n async reserveCredit(amount: number): Promise<string | null> {\n if (!(await this.checkCredit(amount))) return null;\n\n const id = `res-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n this.reservations.set(id, amount);\n return id;\n }\n\n async releaseReservation(reservationId: string): Promise<void> {\n this.reservations.delete(reservationId);\n }\n\n async chargeReservation(\n reservationId: string,\n actualAmount?: number,\n ): Promise<CreditTransaction> {\n const reserved = this.reservations.get(reservationId);\n if (reserved === undefined) {\n throw new Error(`Reservation not found: ${reservationId}`);\n }\n\n const chargeAmount = actualAmount ?? reserved;\n this.reservations.delete(reservationId);\n\n this.balance.available -= chargeAmount;\n this.balance.used += chargeAmount;\n\n const transaction: CreditTransaction = {\n id: `txn-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n type: \"charge\",\n amount: chargeAmount,\n balance: this.balance.available,\n description: `Charged from reservation ${reservationId}`,\n timestamp: new Date(),\n };\n\n this.transactions.unshift(transaction);\n return transaction;\n }\n\n addCredits(amount: number, description: string): CreditTransaction {\n this.balance.available += amount;\n this.balance.limit += amount;\n\n const transaction: CreditTransaction = {\n id: `txn-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n type: \"grant\",\n amount,\n balance: this.balance.available,\n description,\n timestamp: new Date(),\n };\n\n this.transactions.unshift(transaction);\n return transaction;\n }\n}\n\nexport function createCreditTracker(\n config: CreditTrackerConfig,\n provider: CreditProvider,\n storage?: Storage,\n): CreditTracker {\n return new CreditTracker(config, provider, storage);\n}\n","/**\n * Local best-effort helpers. Mirrors `@repo/shared/best-effort` but kept\n * inline so this published SDK package has no private workspace deps.\n */\n\nexport function bestEffort<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n","/**\n * Enrichment Types\n *\n * Types for client-forwarded metadata blobs and parsed enrichment context.\n * Partners can send arbitrary metadata that gets parsed by registered adapters\n * and attached to traces for analytics and tenant isolation.\n */\n\n/** Supported blob formats for metadata transmission */\nexport type MetadataBlobFormat = \"json\" | \"msgpack\" | \"protobuf\";\n\n/** Client-forwarded metadata blob */\nexport interface MetadataBlob {\n /** Serialization format of the data */\n format: MetadataBlobFormat;\n /** Serialized data (base64 for binary formats) */\n data: string | Buffer;\n /** Optional schema identifier for validation */\n schema?: string;\n}\n\n/** Product context for trace classification */\nexport type ProductType = \"vibe_coding\" | \"simulation\" | \"background_agent\";\n\n/** Environment context */\nexport type EnvironmentType = \"development\" | \"staging\" | \"production\";\n\n/** Subscription tier for rate limiting and feature gating */\nexport type SubscriptionTier = \"free\" | \"pro\" | \"enterprise\";\n\n/**\n * Parsed enrichment context attached to traces.\n * Built from partner metadata blobs via adapters.\n */\nexport interface EnrichmentContext {\n /** Partner identifier for tenant isolation */\n partnerId?: string;\n /** Subscription tier for rate limiting */\n subscriptionTier?: SubscriptionTier;\n /** End user identifier within partner's system */\n endUserId?: string;\n /** Product context */\n product?: ProductType;\n /** Deployment environment */\n environment?: EnvironmentType;\n /** Custom key-value labels for filtering */\n customLabels: Record<string, string>;\n /** Original blob for audit/replay */\n rawBlob?: MetadataBlob;\n}\n\n/**\n * Adapter interface for parsing metadata blobs.\n * Register adapters to handle different blob formats or schemas.\n */\nexport interface MetadataBlobAdapter {\n /** Unique adapter name */\n name: string;\n /** Check if this adapter can parse the given blob */\n canParse(blob: MetadataBlob): boolean;\n /** Parse blob into enrichment context fields */\n parse(blob: MetadataBlob): Partial<EnrichmentContext>;\n}\n\n/** Create an empty enrichment context with defaults */\nexport function createEmptyContext(): EnrichmentContext {\n return {\n customLabels: {},\n };\n}\n","/**\n * Adapter Registry\n *\n * Registry for metadata blob adapters. Adapters are tried in order of registration\n * until one can parse the blob. Default JSON adapter is always available.\n */\n\nimport { bestEffort } from \"../internal/best-effort.js\";\nimport {\n createEmptyContext,\n type EnrichmentContext,\n type MetadataBlob,\n type MetadataBlobAdapter,\n} from \"./types.js\";\n\n/** Registry for metadata blob adapters */\nexport class AdapterRegistry {\n private adapters: MetadataBlobAdapter[] = [];\n\n /** Register an adapter (added to end of chain) */\n register(adapter: MetadataBlobAdapter): void {\n const existing = this.adapters.find((a) => a.name === adapter.name);\n if (existing) {\n throw new Error(`Adapter \"${adapter.name}\" already registered`);\n }\n this.adapters.push(adapter);\n }\n\n /** Unregister an adapter by name */\n unregister(name: string): boolean {\n const idx = this.adapters.findIndex((a) => a.name === name);\n if (idx >= 0) {\n this.adapters.splice(idx, 1);\n return true;\n }\n return false;\n }\n\n /** Get all registered adapter names */\n getAdapterNames(): string[] {\n return this.adapters.map((a) => a.name);\n }\n\n /**\n * Parse a metadata blob using registered adapters.\n * Returns enrichment context with parsed fields.\n * Stores original blob in rawBlob for audit.\n */\n parse(blob: MetadataBlob): EnrichmentContext {\n const context = createEmptyContext();\n context.rawBlob = blob;\n\n for (const adapter of this.adapters) {\n if (adapter.canParse(blob)) {\n const parsed = bestEffort(() => adapter.parse(blob));\n if (parsed) {\n return {\n ...context,\n ...parsed,\n customLabels: {\n ...context.customLabels,\n ...(parsed.customLabels || {}),\n },\n rawBlob: blob,\n };\n }\n }\n }\n\n // No adapter could parse - return context with just the raw blob\n return context;\n }\n\n /** Check if any adapter can parse the given blob */\n canParse(blob: MetadataBlob): boolean {\n return this.adapters.some((a) => a.canParse(blob));\n }\n}\n\n/** Default global registry instance */\nlet defaultRegistry: AdapterRegistry | null = null;\n\n/** Get or create the default adapter registry */\nexport function getDefaultRegistry(): AdapterRegistry {\n if (!defaultRegistry) {\n defaultRegistry = new AdapterRegistry();\n }\n return defaultRegistry;\n}\n\n/** Reset the default registry (for testing) */\nexport function resetDefaultRegistry(): void {\n defaultRegistry = null;\n}\n","/**\n * JSON Metadata Adapter\n *\n * Default adapter for parsing JSON-formatted metadata blobs.\n * Supports common field mappings for partner metadata.\n */\n\nimport type {\n EnrichmentContext,\n EnvironmentType,\n MetadataBlob,\n MetadataBlobAdapter,\n ProductType,\n SubscriptionTier,\n} from \"./types.js\";\n\n/** Expected JSON structure for partner metadata */\ninterface JsonMetadataPayload {\n partner_id?: string;\n partnerId?: string;\n subscription_tier?: string;\n subscriptionTier?: string;\n user_id?: string;\n userId?: string;\n end_user_id?: string;\n endUserId?: string;\n product?: string;\n environment?: string;\n env?: string;\n custom?: Record<string, string>;\n labels?: Record<string, string>;\n [key: string]: unknown;\n}\n\nconst VALID_PRODUCTS: ProductType[] = [\n \"vibe_coding\",\n \"simulation\",\n \"background_agent\",\n];\nconst VALID_ENVIRONMENTS: EnvironmentType[] = [\n \"development\",\n \"staging\",\n \"production\",\n];\nconst VALID_TIERS: SubscriptionTier[] = [\"free\", \"pro\", \"enterprise\"];\n\nfunction normalizeProduct(value: unknown): ProductType | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.toLowerCase().replace(/-/g, \"_\");\n return VALID_PRODUCTS.includes(normalized as ProductType)\n ? (normalized as ProductType)\n : undefined;\n}\n\nfunction normalizeEnvironment(value: unknown): EnvironmentType | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.toLowerCase();\n // Map common aliases\n if (normalized === \"dev\") return \"development\";\n if (normalized === \"prod\") return \"production\";\n return VALID_ENVIRONMENTS.includes(normalized as EnvironmentType)\n ? (normalized as EnvironmentType)\n : undefined;\n}\n\nfunction normalizeTier(value: unknown): SubscriptionTier | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.toLowerCase();\n return VALID_TIERS.includes(normalized as SubscriptionTier)\n ? (normalized as SubscriptionTier)\n : undefined;\n}\n\n/** JSON adapter for parsing metadata blobs */\nexport const jsonAdapter: MetadataBlobAdapter = {\n name: \"json\",\n\n canParse(blob: MetadataBlob): boolean {\n return blob.format === \"json\";\n },\n\n parse(blob: MetadataBlob): Partial<EnrichmentContext> {\n const dataStr =\n typeof blob.data === \"string\" ? blob.data : blob.data.toString(\"utf-8\");\n\n const payload = JSON.parse(dataStr) as JsonMetadataPayload;\n const context: Partial<EnrichmentContext> = {\n customLabels: {},\n };\n\n // Partner ID (snake_case or camelCase)\n const partnerId = payload.partner_id || payload.partnerId;\n if (typeof partnerId === \"string\" && partnerId.length > 0) {\n context.partnerId = partnerId;\n }\n\n // Subscription tier\n const tier = payload.subscription_tier || payload.subscriptionTier;\n context.subscriptionTier = normalizeTier(tier);\n\n // End user ID (multiple possible field names)\n const userId =\n payload.end_user_id ||\n payload.endUserId ||\n payload.user_id ||\n payload.userId;\n if (typeof userId === \"string\" && userId.length > 0) {\n context.endUserId = userId;\n }\n\n // Product type\n context.product = normalizeProduct(payload.product);\n\n // Environment\n context.environment = normalizeEnvironment(\n payload.environment || payload.env,\n );\n\n // Custom labels (merge custom and labels fields)\n const customLabels: Record<string, string> = {};\n const addLabels = (obj: unknown) => {\n if (obj && typeof obj === \"object\" && !Array.isArray(obj)) {\n for (const [key, value] of Object.entries(obj)) {\n if (typeof value === \"string\") {\n customLabels[key] = value;\n } else if (typeof value === \"number\" || typeof value === \"boolean\") {\n customLabels[key] = String(value);\n }\n }\n }\n };\n addLabels(payload.custom);\n addLabels(payload.labels);\n\n if (Object.keys(customLabels).length > 0) {\n context.customLabels = customLabels;\n }\n\n return context;\n },\n};\n\n/** Create a JSON adapter instance */\nexport function createJsonAdapter(): MetadataBlobAdapter {\n return jsonAdapter;\n}\n","/**\n * Enrichment Module\n *\n * Metadata blob parsing and enrichment context for traces.\n * Partners forward metadata blobs that are parsed by adapters\n * and attached to execution traces for analytics and tenant isolation.\n */\n\nexport {\n AdapterRegistry,\n getDefaultRegistry,\n resetDefaultRegistry,\n} from \"./adapter-registry.js\";\nexport { createJsonAdapter, jsonAdapter } from \"./json-adapter.js\";\nexport type {\n EnrichmentContext,\n EnvironmentType,\n MetadataBlob,\n MetadataBlobAdapter,\n MetadataBlobFormat,\n ProductType,\n SubscriptionTier,\n} from \"./types.js\";\nexport { createEmptyContext } from \"./types.js\";\n\nimport { getDefaultRegistry } from \"./adapter-registry.js\";\nimport { jsonAdapter } from \"./json-adapter.js\";\nimport type { EnrichmentContext, MetadataBlob } from \"./types.js\";\n\n/**\n * Initialize the default registry with built-in adapters.\n * Call this at startup to ensure JSON adapter is available.\n */\nexport function initializeDefaultAdapters(): void {\n const registry = getDefaultRegistry();\n if (!registry.getAdapterNames().includes(\"json\")) {\n registry.register(jsonAdapter);\n }\n}\n\n/**\n * Parse a metadata blob using the default registry.\n * Convenience function for common use case.\n */\nexport function parseMetadataBlob(blob: MetadataBlob): EnrichmentContext {\n initializeDefaultAdapters();\n return getDefaultRegistry().parse(blob);\n}\n","/**\n * Trace Event Types\n *\n * Strongly typed discriminated union for execution trace events.\n * Used to capture detailed agent execution data for analysis and training.\n *\n * SSE events from OpenCode (MessagePartUpdatedEvent, StatusEvent, etc.) are\n * imported from sdk-core and passed through without transformation.\n * Trace-specific events (DiagnosticEvent, BuildResultEvent, etc.) are\n * collected by other means and defined here.\n */\n\n// Re-export canonical SSE event types from sdk-core (which re-exports from agent-interface)\nexport type {\n AgentEvent,\n ErrorEvent as AgentErrorEvent,\n FilePart,\n MessagePartUpdatedEvent,\n Part,\n PartBase,\n ReasoningPart,\n ResultEvent,\n StatusEvent,\n SubtaskPart,\n TextPart,\n ToolPart,\n ToolState,\n ToolStateCompleted,\n ToolStateError,\n ToolStatePending,\n ToolStateRunning,\n} from \"@tangle-network/agent-core\";\n\nexport {\n isFilePart,\n isReasoningPart,\n isSubtaskPart,\n isTextPart,\n isToolPart,\n} from \"@tangle-network/agent-core\";\n\nimport type { MessagePartUpdatedEvent } from \"@tangle-network/agent-core\";\n\n/**\n * MessagePartUpdatedEvent with timestamp for trace collection.\n * This is the canonical event type from OpenCode - pass through without transformation.\n */\nexport interface MessagePartUpdatedEventWithTimestamp\n extends MessagePartUpdatedEvent {\n /** Timestamp when event was recorded */\n timestamp: Date;\n}\n\n/** Type guard for MessagePartUpdatedEvent */\nexport function isMessagePartUpdatedEvent(\n event: TraceEvent,\n): event is MessagePartUpdatedEventWithTimestamp {\n return event.type === \"message.part.updated\";\n}\n\n/** Execution scope for trace association */\nexport interface TraceScope {\n projectRef?: string;\n sessionId?: string;\n /** Links sub-agent sessions to their parent session */\n parentSessionId?: string;\n userId?: string;\n /**\n * Tangle Intelligence resource attributes for this scope — the `tangle.*`\n * dimensions a partner filters on (partner / project / domain / pseudonymous\n * end-user). Built host-side and threaded per session; stamped onto every\n * exported span by the OTEL sink. Kept as an open bag so new dimensions flow\n * through without a schema change.\n */\n resourceAttributes?: Record<string, string>;\n}\n\n/** Success/failure signals captured during execution */\nexport type Signal =\n | \"build_passed\"\n | \"build_failed\"\n | \"tests_passed\"\n | \"tests_failed\"\n | \"lint_passed\"\n | \"lint_failed\"\n | \"user_approved\"\n | \"user_rejected\"\n | \"task_completed\"\n | \"task_abandoned\"\n | \"runtime_error\"\n | \"timeout\";\n\nexport interface SignalEvent {\n signal: Signal;\n timestamp: Date;\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// Trace Events - Strongly Typed Discriminated Union\n// =============================================================================\n\n/** Base fields shared by all trace events */\ninterface TraceEventBase {\n timestamp: Date;\n}\n\n/** Message updated event (final model text and result metadata) */\nexport interface MessageUpdatedEvent extends TraceEventBase {\n type: \"message.updated\";\n /** Final text content */\n text?: string;\n /** Legacy alias for text */\n finalText?: string;\n /** Token usage details */\n tokenUsage?: Record<string, unknown>;\n /** Timing details */\n timing?: Record<string, unknown>;\n /** Tool invocations captured during execution */\n toolInvocations?: unknown[];\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/** Error event */\nexport interface ErrorEvent extends TraceEventBase {\n type: \"error\";\n /** Error category */\n category: \"runtime\" | \"syntax\" | \"type\" | \"network\" | \"timeout\" | \"unknown\";\n /** Error message */\n message: string;\n /** Stack trace if available */\n stack?: string;\n /** Error code if available */\n code?: string;\n /** Source of error (tool name, file, etc.) */\n source?: string;\n}\n\n/** Diagnostic severity levels */\nexport type DiagnosticSeverity = \"error\" | \"warning\" | \"info\" | \"hint\";\n\n/** Individual diagnostic message */\nexport interface Diagnostic {\n /** Severity level */\n severity: DiagnosticSeverity;\n /** Diagnostic message */\n message: string;\n /** Diagnostic code (e.g., \"TS2304\", \"E0001\") */\n code?: string | number;\n /** Source (e.g., \"typescript\", \"eslint\") */\n source?: string;\n /** Start line (0-indexed) */\n startLine: number;\n /** Start column (0-indexed) */\n startColumn: number;\n /** End line (0-indexed) */\n endLine: number;\n /** End column (0-indexed) */\n endColumn: number;\n}\n\n/** Summary of diagnostics for a file */\nexport interface DiagnosticSummary {\n errors: number;\n warnings: number;\n infos: number;\n hints: number;\n}\n\n/** Diagnostic change event - captured after file edits */\nexport interface DiagnosticEvent extends TraceEventBase {\n type: \"diagnostic\";\n /** File path */\n file: string;\n /** Language server ID */\n serverId?: string;\n /** Language (e.g., \"typescript\", \"python\") */\n language?: string;\n /** Diagnostics before the change */\n before?: Diagnostic[];\n /** Diagnostics after the change */\n after: Diagnostic[];\n /** Summary counts before */\n beforeSummary?: DiagnosticSummary;\n /** Summary counts after */\n afterSummary: DiagnosticSummary;\n /** Net change in error count (negative = improvement) */\n errorDelta: number;\n /** Diagnostics that were resolved */\n resolved?: Diagnostic[];\n /** Diagnostics that were introduced */\n introduced?: Diagnostic[];\n}\n\n/** File change event */\nexport interface FileChangeEvent extends TraceEventBase {\n type: \"file_change\";\n /** File path */\n file: string;\n /** Change type */\n changeType: \"created\" | \"modified\" | \"deleted\";\n /** Lines added */\n linesAdded?: number;\n /** Lines removed */\n linesRemoved?: number;\n}\n\n/** Build result event */\nexport interface BuildResultEvent extends TraceEventBase {\n type: \"build_result\";\n /** Build command */\n command?: string;\n /** Whether build succeeded */\n success: boolean;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Error count */\n errorCount?: number;\n /** Warning count */\n warningCount?: number;\n}\n\n/** Test result event */\nexport interface TestResultEvent extends TraceEventBase {\n type: \"test_result\";\n /** Test command */\n command?: string;\n /** Whether all tests passed */\n success: boolean;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Number of tests passed */\n passed?: number;\n /** Number of tests failed */\n failed?: number;\n /** Number of tests skipped */\n skipped?: number;\n}\n\n/** Environment snapshot at trace start */\nexport interface EnvironmentEvent extends TraceEventBase {\n type: \"environment\";\n /** Runtime versions */\n versions?: Record<string, string>;\n /** Working directory */\n workingDir?: string;\n /** Platform info */\n platform?: string;\n}\n\n/** Generic event for extensibility (use sparingly) */\nexport interface CustomEvent extends TraceEventBase {\n type: \"custom\";\n /** Custom event name */\n name: string;\n /** Custom event data */\n data: Record<string, unknown>;\n}\n\n// =============================================================================\n// Batch Execution Events\n// =============================================================================\n\n/** Batch execution started event */\nexport interface BatchStartedEvent extends TraceEventBase {\n type: \"batch_started\";\n /** Unique batch execution ID */\n batchId: string;\n /** Number of tasks in the batch */\n taskCount: number;\n /** Number of backends being tested */\n backendCount: number;\n /** Total executions (tasks × backends) */\n totalExecutions: number;\n /** Scaling mode used */\n scalingMode?: \"fastest\" | \"balanced\" | \"cheapest\";\n}\n\n/** Batch execution completed event */\nexport interface BatchCompletedEvent extends TraceEventBase {\n type: \"batch_completed\";\n /** Unique batch execution ID */\n batchId: string;\n /** Number of successful task executions */\n successCount: number;\n /** Number of failed task executions */\n failureCount: number;\n /** Success rate as percentage (0-100) */\n successRate: number;\n /** Total duration in milliseconds */\n durationMs: number;\n /** Total tokens used across all executions */\n totalTokensUsed?: number;\n}\n\n/** Individual task within a batch completed */\nexport interface BatchTaskCompletedEvent extends TraceEventBase {\n type: \"batch_task_completed\";\n /** Parent batch ID */\n batchId: string;\n /** Task ID within the batch */\n taskId: string;\n /** Backend used for this execution */\n backend: string;\n /** Whether the task succeeded */\n success: boolean;\n /** Duration in milliseconds */\n durationMs: number;\n /** Attempt number (1 = first try) */\n attempt: number;\n /** Number of retries before success/failure */\n retries: number;\n /** Tokens used for this task */\n tokensUsed?: number;\n /** Error message if failed */\n error?: string;\n}\n\n/** Union of all trace event types */\nexport type TraceEvent =\n // Canonical SSE event from OpenCode (preferred)\n | MessagePartUpdatedEventWithTimestamp\n | MessageUpdatedEvent\n | ErrorEvent\n // Trace-specific events (not from SSE)\n | DiagnosticEvent\n | FileChangeEvent\n | BuildResultEvent\n | TestResultEvent\n | EnvironmentEvent\n | CustomEvent\n | BatchStartedEvent\n | BatchCompletedEvent\n | BatchTaskCompletedEvent;\n\n/** Event type discriminator */\nexport type TraceEventType = TraceEvent[\"type\"];\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\nexport function isMessageUpdatedEvent(\n event: TraceEvent,\n): event is MessageUpdatedEvent {\n return event.type === \"message.updated\";\n}\n\nexport function isErrorEvent(event: TraceEvent): event is ErrorEvent {\n return event.type === \"error\";\n}\n\nexport function isDiagnosticEvent(event: TraceEvent): event is DiagnosticEvent {\n return event.type === \"diagnostic\";\n}\n\nexport function isFileChangeEvent(event: TraceEvent): event is FileChangeEvent {\n return event.type === \"file_change\";\n}\n\nexport function isBuildResultEvent(\n event: TraceEvent,\n): event is BuildResultEvent {\n return event.type === \"build_result\";\n}\n\nexport function isTestResultEvent(event: TraceEvent): event is TestResultEvent {\n return event.type === \"test_result\";\n}\n\nexport function isEnvironmentEvent(\n event: TraceEvent,\n): event is EnvironmentEvent {\n return event.type === \"environment\";\n}\n\nexport function isCustomEvent(event: TraceEvent): event is CustomEvent {\n return event.type === \"custom\";\n}\n\nexport function isBatchStartedEvent(\n event: TraceEvent,\n): event is BatchStartedEvent {\n return event.type === \"batch_started\";\n}\n\nexport function isBatchCompletedEvent(\n event: TraceEvent,\n): event is BatchCompletedEvent {\n return event.type === \"batch_completed\";\n}\n\nexport function isBatchTaskCompletedEvent(\n event: TraceEvent,\n): event is BatchTaskCompletedEvent {\n return event.type === \"batch_task_completed\";\n}\n\n// =============================================================================\n// Execution Trace\n// =============================================================================\n\n/** Execution outcome derived from signals */\nexport type Outcome = \"success\" | \"partial\" | \"failure\" | \"unknown\";\n\n/** Execution trace - complete record of an agent task */\nexport interface ExecutionTrace {\n id: string;\n scope: TraceScope;\n task: string;\n events: TraceEvent[];\n signals: SignalEvent[];\n outcome: Outcome;\n startedAt: Date;\n completedAt?: Date;\n metadata?: Record<string, unknown>;\n}\n","/**\n * Langfuse Sink\n *\n * Native Langfuse SDK integration for trace export.\n * Uses the langfuse-node SDK directly instead of going through OTEL,\n * which provides better compatibility and richer trace visualization.\n */\n\nimport type {\n ExecutionTrace,\n MessagePartUpdatedEventWithTimestamp,\n ToolPart,\n TraceEvent,\n} from \"./trace-types.js\";\nimport {\n isFilePart,\n isReasoningPart,\n isTextPart,\n isToolPart,\n} from \"./trace-types.js\";\n\n/** Langfuse sink configuration */\nexport interface LangfuseSinkConfig {\n /** Langfuse public key */\n publicKey: string;\n /** Langfuse secret key */\n secretKey: string;\n /** Langfuse host (default: https://cloud.langfuse.com) */\n baseUrl?: string;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// Lazy-loaded Langfuse client\nlet langfuseClient: import(\"langfuse-node\").Langfuse | null = null;\nlet langfuseConfig: LangfuseSinkConfig | null = null;\n\n/**\n * Get or create the Langfuse client\n */\nasync function getLangfuseClient(\n config: LangfuseSinkConfig,\n): Promise<import(\"langfuse-node\").Langfuse | null> {\n if (langfuseClient && langfuseConfig === config) {\n return langfuseClient;\n }\n\n try {\n const { Langfuse } = await import(\"langfuse-node\");\n langfuseClient = new Langfuse({\n publicKey: config.publicKey,\n secretKey: config.secretKey,\n baseUrl: config.baseUrl || \"https://cloud.langfuse.com\",\n });\n langfuseConfig = config;\n return langfuseClient;\n } catch (err) {\n console.warn(\"[langfuse-sink] Failed to initialize Langfuse:\", err);\n return null;\n }\n}\n\n/**\n * Langfuse trace exporter using the native SDK.\n *\n * Creates proper Langfuse traces with spans and generations\n * for rich observability in the Langfuse dashboard.\n *\n * Note: This is a trace-level exporter (ExecutionTrace), not a\n * TelemetrySink (TelemetryEvent). Use for exporting complete traces.\n */\nexport class LangfuseSink {\n readonly name = \"langfuse\";\n private readonly config: LangfuseSinkConfig;\n\n constructor(config: LangfuseSinkConfig) {\n this.config = config;\n }\n\n /**\n * Export an execution trace to Langfuse.\n */\n async exportTrace(trace: ExecutionTrace): Promise<void> {\n const client = await getLangfuseClient(this.config);\n if (!client) return;\n\n try {\n // Create the main trace\n const langfuseTrace = client.trace({\n id: trace.id,\n name: trace.task,\n sessionId: trace.scope.sessionId,\n userId: trace.scope.userId,\n metadata: {\n outcome: trace.outcome,\n projectRef: trace.scope.projectRef,\n ...trace.metadata,\n },\n tags: trace.outcome === \"failure\" ? [\"failed\"] : undefined,\n });\n\n // Export events as spans/generations\n for (const event of trace.events) {\n if (!event) continue;\n this.exportTraceEvent(langfuseTrace, event);\n }\n\n // Add signals as trace events\n for (const signal of trace.signals) {\n langfuseTrace.event({\n name: `signal.${signal.signal}`,\n startTime: signal.timestamp,\n metadata: signal.metadata,\n });\n }\n\n // Update trace with completion info\n langfuseTrace.update({\n output: trace.outcome,\n });\n\n // Flush to ensure delivery\n await client.flushAsync();\n } catch (err) {\n console.warn(\"[langfuse-sink] Failed to export trace:\", err);\n }\n }\n\n /**\n * Export a trace event as a Langfuse span or generation.\n */\n private exportTraceEvent(\n trace: ReturnType<import(\"langfuse-node\").Langfuse[\"trace\"]>,\n event: TraceEvent,\n ): void {\n switch (event.type) {\n // Canonical SSE event from OpenCode - interpret based on part type\n case \"message.part.updated\": {\n const partEvent = event as MessagePartUpdatedEventWithTimestamp;\n const part = partEvent.part;\n\n if (isToolPart(part)) {\n this.exportToolPart(trace, partEvent, part);\n } else if (isTextPart(part) || isReasoningPart(part)) {\n // Text/reasoning parts become generations\n trace.generation({\n name: part.type === \"reasoning\" ? \"reasoning\" : \"text\",\n startTime: partEvent.timestamp,\n output: part.text,\n metadata: {\n partId: part.id,\n sessionID: part.sessionID,\n messageID: part.messageID,\n delta: partEvent.delta,\n },\n });\n } else if (isFilePart(part)) {\n trace.event({\n name: `file.${part.mediaType || \"unknown\"}`,\n startTime: partEvent.timestamp,\n metadata: {\n partId: part.id,\n filename: part.filename,\n mediaType: part.mediaType,\n url: part.url,\n },\n });\n }\n break;\n }\n\n case \"message.updated\":\n trace.event({\n name: \"message.updated\",\n startTime: event.timestamp,\n metadata: {\n text: event.text ?? event.finalText,\n finalText: event.finalText,\n tokenUsage: event.tokenUsage,\n timing: event.timing,\n toolInvocations: event.toolInvocations,\n metadata: event.metadata,\n },\n });\n break;\n\n case \"error\":\n trace.event({\n name: `error.${event.category}`,\n startTime: event.timestamp,\n level: \"ERROR\",\n metadata: {\n message: event.message,\n code: event.code,\n source: event.source,\n },\n });\n break;\n\n case \"file_change\":\n trace.event({\n name: `file.${event.changeType}`,\n startTime: event.timestamp,\n metadata: {\n file: event.file,\n linesAdded: event.linesAdded,\n linesRemoved: event.linesRemoved,\n },\n });\n break;\n\n case \"build_result\":\n trace.span({\n name: `build.${event.success ? \"success\" : \"failure\"}`,\n startTime: event.timestamp,\n endTime: event.durationMs\n ? new Date(event.timestamp.getTime() + event.durationMs)\n : undefined,\n output: event.success ? \"success\" : \"failed\",\n metadata: {\n command: event.command,\n errorCount: event.errorCount,\n warningCount: event.warningCount,\n },\n level: event.success ? undefined : \"ERROR\",\n });\n break;\n\n case \"test_result\":\n trace.span({\n name: `test.${event.success ? \"success\" : \"failure\"}`,\n startTime: event.timestamp,\n endTime: event.durationMs\n ? new Date(event.timestamp.getTime() + event.durationMs)\n : undefined,\n output: event.success ? \"success\" : \"failed\",\n metadata: {\n command: event.command,\n passed: event.passed,\n failed: event.failed,\n skipped: event.skipped,\n },\n level: event.success ? undefined : \"ERROR\",\n });\n break;\n\n case \"diagnostic\":\n trace.event({\n name: `diagnostic.${event.language || \"unknown\"}`,\n startTime: event.timestamp,\n metadata: {\n file: event.file,\n errorDelta: event.errorDelta,\n errorsAfter: event.afterSummary.errors,\n warningsAfter: event.afterSummary.warnings,\n },\n });\n break;\n\n case \"environment\":\n trace.event({\n name: \"environment.snapshot\",\n startTime: event.timestamp,\n metadata: {\n workingDir: event.workingDir,\n platform: event.platform,\n },\n });\n break;\n\n case \"custom\":\n if (event.name === \"startup.phase\") {\n const durationMs =\n typeof event.data.durationMs === \"number\"\n ? event.data.durationMs\n : 0;\n const phase =\n typeof event.data.phase === \"string\" ? event.data.phase : \"unknown\";\n trace.span({\n name: `startup.${phase}`,\n startTime: new Date(event.timestamp.getTime() - durationMs),\n endTime: event.timestamp,\n metadata: event.data,\n level: event.data.status === \"error\" ? \"ERROR\" : undefined,\n statusMessage:\n typeof event.data.error === \"string\"\n ? event.data.error\n : undefined,\n });\n } else {\n trace.event({\n name: `custom.${event.name}`,\n startTime: event.timestamp,\n metadata: event.data,\n });\n }\n break;\n }\n }\n\n /**\n * Export a tool part from message.part.updated event.\n * Tool parts have state progression: pending → running → completed/error\n */\n private exportToolPart(\n trace: ReturnType<import(\"langfuse-node\").Langfuse[\"trace\"]>,\n event: MessagePartUpdatedEventWithTimestamp,\n part: ToolPart,\n ): void {\n const status = part.state.status;\n const toolName = part.tool;\n\n if (status === \"running\") {\n // Tool started - create span with input\n trace.span({\n name: `tool.${toolName}`,\n startTime: event.timestamp,\n input: part.state.input ?? { callID: part.callID },\n metadata: {\n partId: part.id,\n sessionID: part.sessionID,\n messageID: part.messageID,\n status,\n },\n });\n } else if (status === \"completed\") {\n // Tool finished - create span with output\n const endTime = part.state.time?.end\n ? new Date(part.state.time.end)\n : event.timestamp;\n const startTime = part.state.time?.start\n ? new Date(part.state.time.start)\n : event.timestamp;\n\n trace.span({\n name: `tool.${toolName}.result`,\n startTime,\n endTime,\n input: part.state.input,\n output: part.state.output ?? \"success\",\n metadata: {\n partId: part.id,\n sessionID: part.sessionID,\n messageID: part.messageID,\n callID: part.callID,\n success: true,\n },\n });\n } else if (status === \"failed\" || status === \"error\") {\n const endTime = part.state.time?.end\n ? new Date(part.state.time.end)\n : event.timestamp;\n const startTime = part.state.time?.start\n ? new Date(part.state.time.start)\n : event.timestamp;\n\n trace.span({\n name: `tool.${toolName}.result`,\n startTime,\n endTime,\n input: part.state.input,\n output: part.state.output ?? part.state.error,\n metadata: {\n partId: part.id,\n sessionID: part.sessionID,\n messageID: part.messageID,\n callID: part.callID,\n success: false,\n },\n level: \"ERROR\",\n statusMessage: part.state.error,\n });\n }\n }\n\n async flush(): Promise<void> {\n const client = await getLangfuseClient(this.config);\n if (client) {\n await client.flushAsync();\n }\n }\n\n async close(): Promise<void> {\n const client = await getLangfuseClient(this.config);\n if (client) {\n await client.shutdownAsync();\n langfuseClient = null;\n }\n }\n}\n\n/**\n * Create a Langfuse sink from environment variables.\n */\nexport function createLangfuseSinkFromEnv(): LangfuseSink | null {\n const publicKey = process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = process.env.LANGFUSE_SECRET_KEY;\n\n if (!publicKey || !secretKey) {\n return null;\n }\n\n return new LangfuseSink({\n publicKey,\n secretKey,\n baseUrl: process.env.LANGFUSE_HOST || \"https://cloud.langfuse.com\",\n debug: process.env.LANGFUSE_DEBUG === \"true\",\n });\n}\n","/**\n * Global OpenTelemetry tracer provider for the in-container agent loop.\n *\n * Registers a Node TracerProvider + batching OTLP/HTTP exporter so the GLOBAL\n * tracer (`@opentelemetry/api` `trace.getTracer`, used by the OTel sink) ships\n * spans to the configured collector (Tangle Intelligence) instead of being a\n * no-op. Endpoint + headers come from the standard OTEL env the host threads in\n * (`OTEL_EXPORTER_OTLP_ENDPOINT` / `OTEL_EXPORTER_OTLP_HEADERS`); per-session\n * partner/project/user resource attributes are stamped on spans by the sink,\n * not here.\n *\n * Opt-in + best-effort:\n * - a no-op unless `TELEMETRY_ENABLED=true` AND an endpoint is set;\n * - any SDK import / construction failure degrades to disabled, never crashes\n * the sidecar (telemetry must never take down the agent);\n * - idempotent — a second `initOtelProvider` returns the already-active result.\n */\n\nexport interface InitOtelProviderResult {\n active: boolean;\n reason?: string;\n}\n\ninterface Shutdownable {\n forceFlush(): Promise<void>;\n shutdown(): Promise<void>;\n}\n\nlet provider: Shutdownable | null = null;\n\n/** Parse `OTEL_EXPORTER_OTLP_HEADERS` (\"k=v,k2=v2\") into a header map. */\nfunction parseHeaders(raw: string | undefined): Record<string, string> {\n const out: Record<string, string> = {};\n if (!raw) return out;\n for (const pair of raw.split(\",\")) {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = pair.slice(0, eq).trim();\n const value = pair.slice(eq + 1).trim();\n if (key) out[key] = value;\n }\n return out;\n}\n\nexport async function initOtelProvider(\n serviceName: string,\n): Promise<InitOtelProviderResult> {\n if (provider) return { active: true };\n\n if (process.env.TELEMETRY_ENABLED !== \"true\") {\n return { active: false, reason: \"TELEMETRY_ENABLED!=true\" };\n }\n const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n if (!endpoint) {\n return { active: false, reason: \"OTEL_EXPORTER_OTLP_ENDPOINT unset\" };\n }\n\n try {\n const [\n { NodeTracerProvider, BatchSpanProcessor },\n { OTLPTraceExporter },\n { resourceFromAttributes },\n { ATTR_SERVICE_NAME },\n ] = await Promise.all([\n import(\"@opentelemetry/sdk-trace-node\"),\n import(\"@opentelemetry/exporter-trace-otlp-http\"),\n import(\"@opentelemetry/resources\"),\n import(\"@opentelemetry/semantic-conventions\"),\n ]);\n\n const exporter = new OTLPTraceExporter({\n url: endpoint,\n headers: parseHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS),\n });\n\n const tracerProvider = new NodeTracerProvider({\n resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: serviceName }),\n spanProcessors: [new BatchSpanProcessor(exporter)],\n });\n // Registers as the global provider so `api.trace.getTracer(...)` (the sink)\n // returns real tracers instead of the default no-op.\n tracerProvider.register();\n provider = tracerProvider;\n\n return { active: true };\n } catch (error) {\n return {\n active: false,\n reason: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport async function shutdownOtelProvider(): Promise<void> {\n if (!provider) return;\n const current = provider;\n provider = null;\n try {\n await current.forceFlush();\n } catch {\n // best-effort flush\n }\n await current.shutdown();\n}\n","/**\n * OpenTelemetry Sink\n *\n * Exports telemetry data to OpenTelemetry-compatible backends including\n * Langfuse, Jaeger, Honeycomb, and other OTLP collectors.\n *\n * IMPORTANT: This sink uses the GLOBAL tracer from @opentelemetry/api.\n * The OTEL SDK must be initialized separately (e.g., via NodeSDK with\n * LangfuseSpanProcessor or OTLPTraceExporter) before using this sink.\n *\n * This sink converts ExecutionTrace data into OTEL spans using the\n * global tracer, which ensures spans flow through whatever processor\n * was configured at SDK initialization.\n */\n\nimport {\n GEN_AI_CONVERSATION_ID,\n GEN_AI_OPERATION_NAME,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n} from \"@tangle-network/agent-core\";\nimport {\n type ExecutionTrace,\n isFilePart,\n isReasoningPart,\n isTextPart,\n isToolPart,\n type TraceEvent,\n} from \"./trace-types.js\";\nimport type { TelemetryEvent, TelemetrySink } from \"./types.js\";\n\n/** OTEL span attribute value. */\ntype SpanAttrValue = string | number | boolean;\n\n/**\n * GenAI semantic-convention keys promoted from a `message.updated` event's\n * metadata bag to DISCRETE span attributes. The intelligence ingest derives\n * model/token/cost and conversation linkage by reading these exact keys off the\n * flat span attribute bag (`parseGenAiAttribution` / `parseConversationLinkage`\n * in the intelligence API); leaving them inside the serialized `message.metadata`\n * blob would attribute every auto-instrumented LLM call as model/tokens/cost\n * unknown. Promotion is unconditional (not gated on `includePayloads`): these\n * are attribution signal, not prompt/completion content. `genAiUsageAttributes`\n * already guarantees the writer never emits a fabricated zero, so a key absent\n * here stays absent on the span — \"cost not computed\", never \"free\".\n */\nconst GEN_AI_PROMOTED_KEYS: readonly string[] = Object.freeze([\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_OPERATION_NAME,\n GEN_AI_CONVERSATION_ID,\n]);\n\n/**\n * Build the parent-span attributes for a trace, with a deliberate precedence:\n * partner-supplied `resourceAttributes` are the LOWEST layer, so the trusted\n * identity attributes (agent.trace.id, session.id, user.id) and metadata always\n * override a colliding key. A partner cannot spoof span identity by passing a\n * `resourceAttributes` key like `session.id`.\n */\nexport function buildTraceSpanAttributes(\n trace: ExecutionTrace,\n flattenedMetadata?: Record<string, SpanAttrValue>,\n): Record<string, SpanAttrValue> {\n return {\n // Lowest precedence: partner/project/domain tags (tangle.*). Filtered by\n // being spread first — everything below wins on a key collision.\n ...((trace.scope.resourceAttributes ?? {}) as Record<\n string,\n SpanAttrValue\n >),\n \"agent.trace.id\": trace.id,\n \"agent.task\": trace.task,\n \"agent.outcome\": trace.outcome,\n // Langfuse-specific attributes for proper mapping\n \"langfuse.trace.id\": trace.id,\n \"langfuse.trace.name\": trace.task,\n ...(trace.scope.projectRef && {\n \"langfuse.project\": trace.scope.projectRef,\n }),\n ...(trace.scope.sessionId && {\n \"langfuse.session.id\": trace.scope.sessionId,\n \"session.id\": trace.scope.sessionId,\n }),\n ...(trace.scope.userId && {\n \"langfuse.user.id\": trace.scope.userId,\n \"user.id\": trace.scope.userId,\n }),\n ...(flattenedMetadata ?? {}),\n };\n}\n\n/** Configuration for the OTEL sink */\nexport interface OtelSinkConfig {\n /** Service name for the tracer */\n serviceName?: string;\n /** Include raw input/output in span attributes (may contain sensitive data) */\n includePayloads?: boolean;\n}\n\n// Lazy-loaded OTEL API types\nlet otelApi: typeof import(\"@opentelemetry/api\") | null = null;\n\n/**\n * Load the OTEL API dynamically\n */\nasync function getOtelApi() {\n if (!otelApi) {\n try {\n otelApi = await import(\"@opentelemetry/api\");\n } catch {\n return null;\n }\n }\n return otelApi;\n}\n\n/**\n * OpenTelemetry sink — exports via whatever OTEL SDK config was set at\n * application startup (Langfuse, Jaeger, any OTLP backend).\n */\nexport class OtelTelemetrySink implements TelemetrySink {\n readonly name = \"otel\";\n private readonly config: OtelSinkConfig;\n\n constructor(config: OtelSinkConfig = {}) {\n this.config = {\n serviceName: config.serviceName ?? \"agent-sdk\",\n includePayloads: config.includePayloads ?? false,\n };\n }\n\n async send(events: TelemetryEvent[]): Promise<void> {\n const api = await getOtelApi();\n if (!api) return;\n\n // biome-ignore lint/style/noNonNullAssertion: serviceName is required in OtelSinkConfig\n const tracer = api.trace.getTracer(this.config.serviceName!, \"1.0.0\");\n\n for (const event of events) {\n // `agent_execution` events carry the full ExecutionTrace under\n // `data.trace` (see TraceCollector.flush). Export it as a proper span\n // tree rather than a single flat telemetry span — this is the path that\n // ships tagged agent traces (partner/project/user) to the OTLP backend.\n if (\n event.type === \"agent_execution\" &&\n this.isExecutionTrace(event.data.trace)\n ) {\n await this.exportTrace(event.data.trace);\n continue;\n }\n\n const span = tracer.startSpan(`telemetry.${event.type}.${event.name}`, {\n attributes: {\n \"telemetry.event.type\": event.type,\n \"telemetry.event.name\": event.name,\n },\n startTime: event.timestamp,\n });\n\n // Add event data as attributes\n if (event.data && typeof event.data === \"object\") {\n for (const [key, value] of Object.entries(event.data)) {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n span.setAttribute(`telemetry.data.${key}`, value);\n }\n }\n }\n\n span.setStatus({ code: api.SpanStatusCode.OK });\n span.end();\n }\n }\n\n /**\n * Export an execution trace as OTEL spans.\n *\n * Creates a parent span for the trace and child spans for each event.\n * Uses the global tracer, so spans flow through the configured processor.\n */\n async exportTrace(trace: ExecutionTrace): Promise<void> {\n const api = await getOtelApi();\n if (!api) return;\n\n // biome-ignore lint/style/noNonNullAssertion: serviceName is required in OtelSinkConfig\n const tracer = api.trace.getTracer(this.config.serviceName!, \"1.0.0\");\n\n // Create parent span for the entire trace\n const traceSpan = tracer.startSpan(\"agent.task\", {\n attributes: buildTraceSpanAttributes(\n trace,\n trace.metadata ? this.flattenMetadata(trace.metadata) : undefined,\n ),\n startTime: trace.startedAt,\n });\n\n // Export each trace event as child spans using the parent context\n const parentContext = api.trace.setSpan(api.context.active(), traceSpan);\n\n for (const event of trace.events) {\n if (!event?.type) continue;\n api.context.with(parentContext, () => {\n this.exportTraceEvent(tracer, api, event);\n });\n }\n\n // Export signals as events on the parent span\n for (const signal of trace.signals) {\n traceSpan.addEvent(`signal.${signal.signal}`, {}, signal.timestamp);\n }\n\n // Set final status based on outcome\n if (trace.outcome === \"failure\") {\n traceSpan.setStatus({\n code: api.SpanStatusCode.ERROR,\n message: \"Task failed\",\n });\n } else {\n traceSpan.setStatus({ code: api.SpanStatusCode.OK });\n }\n\n traceSpan.end(trace.completedAt || new Date());\n }\n\n /**\n * Export a single trace event as an OTEL span.\n */\n private exportTraceEvent(\n tracer: ReturnType<typeof import(\"@opentelemetry/api\").trace.getTracer>,\n api: typeof import(\"@opentelemetry/api\"),\n event: TraceEvent,\n ): void {\n const spanName = this.getSpanName(event);\n const attributes = this.getEventAttributes(event);\n const startTime = this.getEventStartTime(event);\n const endTime = this.getEventEndTime(event);\n\n const span = tracer.startSpan(spanName, {\n attributes,\n startTime,\n });\n\n // Handle errors\n if (event.type === \"error\") {\n span.setStatus({\n code: api.SpanStatusCode.ERROR,\n message: event.message,\n });\n span.recordException(new Error(event.message));\n } else if (\n event.type === \"message.part.updated\" &&\n isToolPart(event.part)\n ) {\n const status = event.part.state.status;\n if (status === \"failed\" || status === \"error\") {\n const err =\n typeof event.part.state.error === \"string\"\n ? event.part.state.error\n : \"Tool execution failed\";\n span.setStatus({\n code: api.SpanStatusCode.ERROR,\n message: err,\n });\n span.recordException(new Error(err));\n } else {\n span.setStatus({ code: api.SpanStatusCode.OK });\n }\n } else if (event.type === \"build_result\" && !event.success) {\n span.setStatus({\n code: api.SpanStatusCode.ERROR,\n message: \"Build failed\",\n });\n } else if (event.type === \"test_result\" && !event.success) {\n span.setStatus({\n code: api.SpanStatusCode.ERROR,\n message: \"Tests failed\",\n });\n } else if (\n event.type === \"custom\" &&\n event.name === \"startup.phase\" &&\n event.data.status === \"error\"\n ) {\n span.setStatus({\n code: api.SpanStatusCode.ERROR,\n message:\n typeof event.data.error === \"string\"\n ? event.data.error\n : \"Startup phase failed\",\n });\n } else {\n span.setStatus({ code: api.SpanStatusCode.OK });\n }\n\n span.end(endTime);\n }\n\n private getEventStartTime(event: TraceEvent): Date {\n if (event.type === \"custom\" && event.name === \"startup.phase\") {\n const durationMs = this.getStartupPhaseDurationMs(event);\n if (typeof durationMs === \"number\") {\n return new Date(event.timestamp.getTime() - durationMs);\n }\n }\n\n return event.timestamp;\n }\n\n private getEventEndTime(event: TraceEvent): Date {\n if (event.type === \"custom\" && event.name === \"startup.phase\") {\n return event.timestamp;\n }\n\n if (\"durationMs\" in event && typeof event.durationMs === \"number\") {\n return new Date(event.timestamp.getTime() + event.durationMs);\n }\n\n return event.timestamp;\n }\n\n private getStartupPhaseDurationMs(\n event: Extract<TraceEvent, { type: \"custom\" }>,\n ): number | undefined {\n return typeof event.data.durationMs === \"number\"\n ? event.data.durationMs\n : undefined;\n }\n\n /**\n * Get span name for a trace event.\n */\n private getSpanName(event: TraceEvent): string {\n switch (event.type) {\n case \"message.part.updated\":\n if (isToolPart(event.part)) {\n return `tool.${event.part.tool}.${event.part.state.status}`;\n }\n if (isReasoningPart(event.part)) return \"llm.reasoning\";\n if (isTextPart(event.part)) return \"llm.text\";\n return \"message.file\";\n case \"message.updated\":\n return \"message.updated\";\n case \"error\":\n return `error.${event.category}`;\n case \"diagnostic\":\n return `diagnostic.${event.language || \"unknown\"}`;\n case \"file_change\":\n return `file.${event.changeType}`;\n case \"build_result\":\n return `build.${event.success ? \"success\" : \"failure\"}`;\n case \"test_result\":\n return `test.${event.success ? \"success\" : \"failure\"}`;\n case \"environment\":\n return \"environment.snapshot\";\n case \"custom\":\n if (event.name === \"startup.phase\") {\n const phase =\n typeof event.data.phase === \"string\" ? event.data.phase : \"unknown\";\n return `startup.${phase}`;\n }\n return `custom.${event.name}`;\n default:\n return \"unknown\";\n }\n }\n\n /**\n * Get OTEL attributes for a trace event.\n */\n private getEventAttributes(\n event: TraceEvent,\n ): Record<string, string | number | boolean | undefined> {\n const attrs: Record<string, string | number | boolean | undefined> = {\n \"event.type\": event.type,\n };\n\n switch (event.type) {\n case \"message.part.updated\":\n attrs[\"message.part.id\"] = event.part.id;\n attrs[\"message.part.type\"] = event.part.type;\n\n if (isToolPart(event.part)) {\n attrs[\"tool.name\"] = event.part.tool;\n const status = event.part.state.status;\n attrs[\"tool.status\"] = status;\n if (this.config.includePayloads && event.part.state.input) {\n attrs[\"tool.input\"] = JSON.stringify(event.part.state.input).slice(\n 0,\n 4096,\n );\n }\n if (\n this.config.includePayloads &&\n status === \"completed\" &&\n event.part.state.output !== undefined\n ) {\n attrs[\"tool.output\"] = JSON.stringify(\n event.part.state.output,\n ).slice(0, 4096);\n }\n if (\n this.config.includePayloads &&\n (status === \"error\" || status === \"failed\") &&\n typeof event.part.state.error === \"string\"\n ) {\n attrs[\"tool.error\"] = event.part.state.error.slice(0, 4096);\n }\n } else if (isTextPart(event.part) || isReasoningPart(event.part)) {\n attrs[\"message.part.text_length\"] = event.part.text.length;\n if (this.config.includePayloads) {\n attrs[\"message.part.text\"] = event.part.text.slice(0, 4096);\n }\n } else if (isFilePart(event.part)) {\n attrs[\"file.name\"] = event.part.filename;\n if (event.part.mediaType)\n attrs[\"file.media_type\"] = event.part.mediaType;\n } else {\n attrs[\"subtask.id\"] = event.part.id;\n attrs[\"subtask.session_id\"] = event.part.sessionID;\n }\n break;\n\n case \"message.updated\": {\n const text = event.text ?? event.finalText;\n if (typeof text === \"string\") {\n attrs[\"message.text_length\"] = text.length;\n if (this.config.includePayloads) {\n attrs[\"message.text\"] = text.slice(0, 4096);\n }\n }\n if (this.config.includePayloads && event.tokenUsage !== undefined) {\n attrs[\"message.token_usage\"] = JSON.stringify(event.tokenUsage).slice(\n 0,\n 4096,\n );\n }\n if (this.config.includePayloads && event.timing !== undefined) {\n attrs[\"message.timing\"] = JSON.stringify(event.timing).slice(0, 4096);\n }\n if (\n this.config.includePayloads &&\n event.toolInvocations !== undefined\n ) {\n attrs[\"message.tool_invocations\"] = JSON.stringify(\n event.toolInvocations,\n ).slice(0, 4096);\n }\n if (event.metadata) {\n for (const key of GEN_AI_PROMOTED_KEYS) {\n const value = event.metadata[key];\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n attrs[key] = value;\n }\n }\n }\n if (this.config.includePayloads && event.metadata !== undefined) {\n attrs[\"message.metadata\"] = JSON.stringify(event.metadata).slice(\n 0,\n 4096,\n );\n }\n break;\n }\n\n case \"error\":\n attrs[\"error.category\"] = event.category;\n attrs[\"error.message\"] = event.message;\n if (event.code) attrs[\"error.code\"] = event.code;\n if (event.source) attrs[\"error.source\"] = event.source;\n break;\n\n case \"diagnostic\":\n attrs[\"diagnostic.file\"] = event.file;\n if (event.language) attrs[\"diagnostic.language\"] = event.language;\n attrs[\"diagnostic.error_delta\"] = event.errorDelta;\n attrs[\"diagnostic.errors_after\"] = event.afterSummary.errors;\n attrs[\"diagnostic.warnings_after\"] = event.afterSummary.warnings;\n break;\n\n case \"file_change\":\n attrs[\"file.path\"] = event.file;\n attrs[\"file.change_type\"] = event.changeType;\n if (event.linesAdded) attrs[\"file.lines_added\"] = event.linesAdded;\n if (event.linesRemoved)\n attrs[\"file.lines_removed\"] = event.linesRemoved;\n break;\n\n case \"build_result\":\n attrs[\"build.success\"] = event.success;\n if (event.command) attrs[\"build.command\"] = event.command;\n if (event.durationMs) attrs[\"build.duration_ms\"] = event.durationMs;\n if (event.errorCount) attrs[\"build.error_count\"] = event.errorCount;\n if (event.warningCount)\n attrs[\"build.warning_count\"] = event.warningCount;\n break;\n\n case \"test_result\":\n attrs[\"test.success\"] = event.success;\n if (event.command) attrs[\"test.command\"] = event.command;\n if (event.durationMs) attrs[\"test.duration_ms\"] = event.durationMs;\n if (event.passed) attrs[\"test.passed\"] = event.passed;\n if (event.failed) attrs[\"test.failed\"] = event.failed;\n if (event.skipped) attrs[\"test.skipped\"] = event.skipped;\n break;\n\n case \"environment\":\n if (event.workingDir) attrs[\"env.working_dir\"] = event.workingDir;\n if (event.platform) attrs[\"env.platform\"] = event.platform;\n break;\n\n case \"custom\":\n attrs[\"custom.name\"] = event.name;\n if (event.name === \"startup.phase\") {\n attrs[\"startup.id\"] =\n typeof event.data.startupId === \"string\"\n ? event.data.startupId\n : undefined;\n attrs[\"startup.phase\"] =\n typeof event.data.phase === \"string\" ? event.data.phase : undefined;\n attrs[\"startup.service\"] =\n typeof event.data.service === \"string\"\n ? event.data.service\n : undefined;\n attrs[\"startup.status\"] =\n typeof event.data.status === \"string\"\n ? event.data.status\n : undefined;\n }\n for (const [key, value] of Object.entries(event.data)) {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n attrs[`custom.data.${key}`] = value;\n }\n }\n break;\n }\n\n return attrs;\n }\n\n /**\n * Flatten metadata object into span attributes\n */\n private flattenMetadata(\n metadata: Record<string, unknown>,\n prefix = \"metadata\",\n ): Record<string, string | number | boolean> {\n const attrs: Record<string, string | number | boolean> = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n attrs[`${prefix}.${key}`] = value;\n }\n }\n return attrs;\n }\n\n /** Narrow an opaque telemetry `data.trace` to an ExecutionTrace. */\n private isExecutionTrace(value: unknown): value is ExecutionTrace {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"scope\" in value &&\n \"events\" in value &&\n Array.isArray((value as { events: unknown }).events)\n );\n }\n\n async flush(): Promise<void> {\n // Global tracer handles flushing via the SDK\n }\n\n async close(): Promise<void> {\n // Global tracer handles shutdown via the SDK\n }\n}\n\n/**\n * Create an OTEL sink that uses the global tracer.\n *\n * The OTEL SDK must be initialized separately before using this sink.\n */\nexport function createOtelSink(config?: OtelSinkConfig): OtelTelemetrySink {\n return new OtelTelemetrySink(config);\n}\n\n/**\n * Create an OTEL sink from environment variables.\n *\n * Returns a sink that uses the global tracer (which should be initialized\n * separately via NodeSDK with LangfuseSpanProcessor or OTLPTraceExporter).\n */\nexport function createOtelSinkFromEnv(): OtelTelemetrySink | null {\n const enabled = process.env.TELEMETRY_ENABLED === \"true\";\n const hasLangfuse =\n process.env.LANGFUSE_PUBLIC_KEY && process.env.LANGFUSE_SECRET_KEY;\n const hasOtlp = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\n if (!enabled || (!hasLangfuse && !hasOtlp)) {\n return null;\n }\n\n return new OtelTelemetrySink({\n serviceName: process.env.OTEL_SERVICE_NAME || \"orchestrator\",\n includePayloads: process.env.OTEL_INCLUDE_PAYLOADS === \"true\",\n });\n}\n","/**\n * Telemetry Sinks\n *\n * Output destinations for telemetry data including\n * HTTP endpoints, console, and custom handlers.\n */\n\nimport type { TelemetryEvent, TelemetrySink } from \"./types.js\";\n\n// HTTP sink for sending telemetry to a remote endpoint\nexport class HttpTelemetrySink implements TelemetrySink {\n readonly name = \"http\";\n private readonly endpoint: string;\n private readonly headers: Record<string, string>;\n private readonly fetchFn: typeof fetch;\n private readonly timeout: number;\n private pendingRequests: Promise<void>[] = [];\n\n constructor(config: {\n endpoint: string;\n headers?: Record<string, string>;\n fetch?: typeof fetch;\n timeout?: number;\n }) {\n this.endpoint = config.endpoint;\n this.headers = {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n };\n this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);\n this.timeout = config.timeout ?? 30000;\n }\n\n async send(events: TelemetryEvent[]): Promise<void> {\n const request = this.fetchFn(this.endpoint, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify({ events }),\n signal: AbortSignal.timeout(this.timeout),\n }).then((res) => {\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n }\n });\n\n this.pendingRequests.push(request);\n\n try {\n await request;\n } finally {\n this.pendingRequests = this.pendingRequests.filter((r) => r !== request);\n }\n }\n\n async flush(): Promise<void> {\n await Promise.all(this.pendingRequests);\n }\n\n async close(): Promise<void> {\n await this.flush();\n }\n}\n\n// Console sink for development/debugging\nexport class ConsoleTelemetrySink implements TelemetrySink {\n readonly name = \"console\";\n private readonly prefix: string;\n private readonly logLevel: \"log\" | \"debug\" | \"info\";\n private readonly formatJson: boolean;\n\n constructor(config?: {\n prefix?: string;\n logLevel?: \"log\" | \"debug\" | \"info\";\n formatJson?: boolean;\n }) {\n this.prefix = config?.prefix ?? \"[telemetry]\";\n this.logLevel = config?.logLevel ?? \"debug\";\n this.formatJson = config?.formatJson ?? true;\n }\n\n async send(events: TelemetryEvent[]): Promise<void> {\n for (const event of events) {\n const message = this.formatJson\n ? JSON.stringify(event, null, 2)\n : `${event.type}:${event.name} ${JSON.stringify(event.data)}`;\n\n console[this.logLevel](`${this.prefix} ${message}`);\n }\n }\n\n async flush(): Promise<void> {\n // No-op for console\n }\n\n async close(): Promise<void> {\n // No-op for console\n }\n}\n\n// Test sink for capturing events in tests (in-memory array, not connected to memory system)\nexport class TestTelemetrySink implements TelemetrySink {\n readonly name = \"test\";\n readonly events: TelemetryEvent[] = [];\n\n async send(events: TelemetryEvent[]): Promise<void> {\n this.events.push(...events);\n }\n\n async flush(): Promise<void> {\n // No-op\n }\n\n async close(): Promise<void> {\n // No-op\n }\n\n clear(): void {\n this.events.length = 0;\n }\n\n getEvents(): TelemetryEvent[] {\n return [...this.events];\n }\n\n getEventsByType(type: string): TelemetryEvent[] {\n return this.events.filter((e) => e.type === type);\n }\n\n getEventsByName(name: string): TelemetryEvent[] {\n return this.events.filter((e) => e.name === name);\n }\n}\n\n// Multi-sink that sends to multiple destinations\nexport class MultiTelemetrySink implements TelemetrySink {\n readonly name = \"multi\";\n private readonly sinks: TelemetrySink[];\n\n constructor(sinks: TelemetrySink[]) {\n this.sinks = sinks;\n }\n\n async send(events: TelemetryEvent[]): Promise<void> {\n await Promise.all(this.sinks.map((sink) => sink.send(events)));\n }\n\n async flush(): Promise<void> {\n await Promise.all(this.sinks.map((sink) => sink.flush()));\n }\n\n async close(): Promise<void> {\n await Promise.all(this.sinks.map((sink) => sink.close()));\n }\n}\n\n// Factory functions\nexport function createHttpSink(config: {\n endpoint: string;\n headers?: Record<string, string>;\n fetch?: typeof fetch;\n timeout?: number;\n}): HttpTelemetrySink {\n return new HttpTelemetrySink(config);\n}\n\nexport function createConsoleSink(config?: {\n prefix?: string;\n logLevel?: \"log\" | \"debug\" | \"info\";\n}): ConsoleTelemetrySink {\n return new ConsoleTelemetrySink(config);\n}\n\nexport function createTestSink(): TestTelemetrySink {\n return new TestTelemetrySink();\n}\n","/**\n * SSE Event Parser\n *\n * Converts SSE stream events to typed TraceEvents.\n * Used by both orchestrator and simulation to ensure consistent event capture.\n *\n * message.part.updated events are passed through directly without transformation.\n * This preserves full fidelity to what OpenCode outputs.\n */\n\nimport {\n parseSSEData as coreParseSSEData,\n type Part,\n type SSEEventData,\n} from \"@tangle-network/agent-core\";\nimport type {\n CustomEvent,\n ErrorEvent,\n MessagePartUpdatedEventWithTimestamp,\n MessageUpdatedEvent,\n Signal,\n SignalEvent,\n ToolPart,\n ToolState,\n TraceEvent,\n} from \"./trace-types.js\";\n\n// Re-export from sdk-core for backwards compatibility\nexport type { SSEEventData } from \"@tangle-network/agent-core\";\nexport const parseSSEData = coreParseSSEData;\n\n/** Valid signals from SSE status events */\nconst VALID_SIGNALS: Signal[] = [\n \"build_passed\",\n \"build_failed\",\n \"tests_passed\",\n \"tests_failed\",\n \"lint_passed\",\n \"lint_failed\",\n \"user_approved\",\n \"user_rejected\",\n \"task_completed\",\n \"task_abandoned\",\n \"runtime_error\",\n \"timeout\",\n];\n\n/**\n * Provider event shapes that carry a tool-invocation payload either at\n * the top level or under one of the well-known wrappers. Recognized here\n * (and not in the consumer) so trace events look identical regardless of\n * which provider emitted the call.\n */\nconst TOOL_INVOCATION_TYPES = new Set([\n \"tool-invocation\",\n `tool_${\"call\"}`,\n \"computer-use\",\n \"computer_call\",\n]);\n\n/**\n * Best-effort: lift a raw tool-invocation event into a canonical\n * `message.part.updated` event with a `ToolPart`. Returns null for\n * payloads we cannot recognize, which lets the caller fall through to\n * a `custom` trace event rather than silently misclassifying.\n *\n * Wire contract — what shapes are expected here:\n *\n * This normalizer runs at the trace-event boundary, AFTER provider\n * adapters have done their own normalization. Adapters\n * (sdk-provider-opencode, sdk-provider-amp, sdk-provider-claude-code,\n * etc.) translate provider-native events into our internal\n * AgentStreamEvent shape — typically wrapping tool invocations\n * under `toolInvocation`/`tool_invocation`/`computerUse`/\n * `computer_use`, with `type` set to one of `TOOL_INVOCATION_TYPES`.\n *\n * Provider-native shapes that adapters are expected to unwrap:\n *\n * OpenAI Responses / Chat Completions:\n * `{id, type: \"function\", function: {name, arguments}}` →\n * adapter wraps into an internal tool-invocation event with `toolInvocation: {\n * toolCallId: id, toolName: function.name, args: function.arguments}}`\n *\n * Anthropic tool_use blocks:\n * `{type: \"tool_use\", id, name, input}` →\n * adapter wraps into an internal tool-invocation event with `toolInvocation`.\n *\n * The flat-with-`id` fallback (`source.id` in the callId chain) is\n * a defensive safety net for cases where an adapter forwards the\n * provider's flat shape verbatim. It is NOT a substitute for\n * adapter-side normalization — the literal OpenAI\n * `{type: \"function\", function: {...}}` wrapper is NOT recognized\n * here because `\"function\"` is intentionally not in\n * TOOL_INVOCATION_TYPES (the word collides with too many\n * non-tool-call uses of `function` as a top-level key).\n */\nfunction normalizeRawToolInvocation(\n event: SSEEventData,\n timestamp: Date,\n): MessagePartUpdatedEventWithTimestamp | null {\n const top = event as Record<string, unknown>;\n const wrapped = (top.toolInvocation ??\n top.tool_invocation ??\n top.computerUse ??\n top.computer_use) as Record<string, unknown> | undefined;\n const innerType = typeof top.type === \"string\" ? top.type : undefined;\n const sourceType =\n typeof top.event === \"object\" && top.event !== null\n ? ((top.event as Record<string, unknown>).type as string | undefined)\n : undefined;\n const isToolType =\n (innerType && TOOL_INVOCATION_TYPES.has(innerType)) ||\n (sourceType && TOOL_INVOCATION_TYPES.has(sourceType));\n if (!wrapped && !isToolType) {\n return null;\n }\n const source = (wrapped ?? top) as Record<string, unknown>;\n const isComputerUse =\n innerType === \"computer-use\" ||\n innerType === \"computer_call\" ||\n sourceType === \"computer-use\" ||\n sourceType === \"computer_call\";\n\n const callId = String(\n source.toolCallId ??\n source.tool_call_id ??\n source.callId ??\n source.callID ??\n source.id ??\n \"\",\n );\n const baseName = String(\n source.toolName ?? source.tool_name ?? source.name ?? \"\",\n );\n const toolName = isComputerUse\n ? `computer-use:${\n ((source.action as { type?: string } | undefined)?.type as string) ||\n \"action\"\n }`\n : baseName;\n if (!callId || !toolName) {\n return null;\n }\n\n const stateObj =\n (source.state as Record<string, unknown> | undefined) ?? undefined;\n const input = (source.args ??\n source.arguments ??\n source.input ??\n stateObj?.input ??\n {}) as Record<string, unknown> | undefined;\n // OpenCode-shaped state nests `output` under `state`; flat provider\n // shapes put it at the top level. Check both.\n const output = (source.result ?? source.output ?? stateObj?.output) as\n | unknown\n | undefined;\n const errorMsg =\n typeof source.error === \"string\"\n ? (source.error as string)\n : typeof stateObj?.error === \"string\"\n ? (stateObj.error as string)\n : undefined;\n const stateStatus =\n typeof stateObj?.status === \"string\"\n ? (stateObj.status as string)\n : undefined;\n const explicitStatus =\n typeof source.status === \"string\" ? (source.status as string) : undefined;\n const status =\n stateStatus ??\n explicitStatus ??\n (output !== undefined ? \"completed\" : \"running\");\n\n let toolState: ToolState;\n switch (status) {\n case \"completed\":\n toolState = {\n status: \"completed\",\n input: (input ?? {}) as Record<string, unknown>,\n // ToolStateCompleted.output is `unknown` (required, not optional).\n // Some providers emit `state: {status: \"completed\"}` for side-\n // effect-only tool calls (writes, notifications) without a\n // return value. Coerce undefined → null so the field is always\n // present on the wire (JSON.stringify drops `undefined` keys\n // entirely), and so consumers can rely on `state.output` being\n // defined whenever `state.status === \"completed\"`.\n output: output ?? null,\n };\n break;\n case \"queued\":\n // Some providers report \"queued\" before \"pending\"; treat both\n // as pending in our ToolState (which doesn't have a separate\n // queued variant). The call-site filter then drops both.\n case \"pending\":\n toolState = {\n status: \"pending\",\n input: (input ?? {}) as Record<string, unknown>,\n };\n break;\n case \"running\":\n case \"in_progress\":\n toolState = {\n status: \"running\",\n input: (input ?? {}) as Record<string, unknown>,\n };\n break;\n case \"error\":\n case \"failed\":\n toolState = {\n status,\n input: (input ?? {}) as Record<string, unknown>,\n output,\n error: errorMsg,\n };\n break;\n default:\n // Unknown status — surface as running so the consumer at least\n // gets the call shape; downstream UIs can rely on\n // running/completed parts arriving for the same callId.\n toolState = {\n status: \"running\",\n input: (input ?? {}) as Record<string, unknown>,\n };\n }\n\n // PartBase contract on a SYNTHESIZED ToolPart:\n //\n // - `id` = `callId`. The tool call is uniquely identified by\n // its callID, so reusing it here lets a consumer\n // group/lookup-by-id without risk of collision across\n // provider tool calls.\n // - `sessionID` and `messageID` = \"\" (empty string). The\n // originating SSE payload does NOT carry these; they\n // are an OpenCode-shape concept that doesn't exist\n // for raw provider events. Empty string is the honest\n // \"we don't know\" — using a sentinel like\n // \"__synthesized__\" was considered but would force\n // every consumer of `ToolPart.sessionID` to special-\n // case the string, whereas standard \"is this empty?\"\n // checks already work consistently with `\"\"`.\n //\n // Consumers that group parts by `sessionID` should treat empty-\n // string as \"unsynthesized origin\" and not mix it with real\n // session traffic — the trace event itself is a record of the\n // call shape, not of session membership.\n const part: ToolPart = {\n id: callId,\n sessionID: \"\",\n messageID: \"\",\n type: \"tool\",\n callID: callId,\n tool: toolName,\n state: toolState,\n };\n\n return {\n type: \"message.part.updated\",\n part,\n timestamp,\n } satisfies MessagePartUpdatedEventWithTimestamp;\n}\n\n/**\n * Convert SSE event data to a typed TraceEvent.\n * Returns null for events that should be skipped (heartbeats, status-only, etc.)\n *\n * message.part.updated events are passed through directly - no transformation.\n * This preserves full fidelity to what OpenCode outputs.\n *\n * `raw` events that carry a recognized tool-invocation payload are\n * normalized into the same `message.part.updated` shape (with a\n * `ToolPart`) so consumers don't need to reach into provider-specific\n * raw shapes to render tool calls. Unrecognized raw events fall\n * through to a `custom` trace event.\n */\nexport function sseToTraceEvent(event: SSEEventData): TraceEvent | null {\n const timestamp = new Date();\n\n // Recognize tool-invocation payloads regardless of whether the\n // outer wrapper survived as `raw` (orchestrator path), came through\n // as the inner provider type (direct sandbox SDK path), or was\n // emitted with a top-level inner-type marker.\n if (\n event.type === \"raw\" ||\n (typeof event.type === \"string\" && TOOL_INVOCATION_TYPES.has(event.type))\n ) {\n const normalized = normalizeRawToolInvocation(event, timestamp);\n if (normalized) {\n // Apply the same filter that the `message.part.updated`\n // branch below applies — the contract for `message.part.updated`\n // trace events is \"no pending tool states\", and a normalized\n // raw event lands in the same shape, so it must obey the same\n // filter. Without this, a consumer that processes both kinds\n // of events would see pending tool calls from raw\n // normalization but not from direct message.part.updated, an\n // inconsistency in the trace contract. (\"queued\" raw events\n // are mapped to \"pending\" inside the normalizer so this single\n // check covers both source statuses.)\n const status = (normalized.part as { state?: { status?: string } }).state\n ?.status;\n if (status === \"pending\") {\n return null;\n }\n return normalized;\n }\n // Unrecognized shape → custom event so observability still\n // captures the payload but doesn't pretend it's a tool call.\n // Preserve the original event type as the custom event's `name`\n // so trace consumers filtering by `name` can find e.g. a\n // malformed legacy tool invocation under its original name instead of\n // every unrecognized variant collapsing to `name: \"raw\"`.\n //\n // Match the `data` shape produced by the `default:` arm in the\n // switch below (`{eventType, ...event}`) so consumers that\n // inspect `data.eventType` on custom events get a uniform shape\n // regardless of whether the unrecognized event came in as a raw\n // tool-invocation type or as an unknown plain type.\n const customName = typeof event.type === \"string\" ? event.type : \"raw\";\n return {\n type: \"custom\",\n timestamp,\n name: customName,\n data: { eventType: customName, ...event } as Record<string, unknown>,\n } satisfies CustomEvent;\n }\n\n switch (event.type) {\n case \"message.part.updated\": {\n // Pass through message.part.updated events directly - no transformation\n // This preserves full fidelity to what OpenCode outputs\n const part = (event as { part?: Part }).part;\n if (!part) {\n return null;\n }\n\n // Skip pending/queued tool states - they don't contain meaningful data yet\n if (part.type === \"tool\") {\n const status = (part as { state?: { status?: string } }).state?.status;\n if (status === \"pending\" || status === \"queued\") {\n return null;\n }\n }\n\n return {\n type: \"message.part.updated\",\n part,\n timestamp,\n delta: (event as { delta?: string }).delta,\n _meta: (event as { _meta?: unknown })._meta as\n | MessagePartUpdatedEventWithTimestamp[\"_meta\"]\n | undefined,\n } satisfies MessagePartUpdatedEventWithTimestamp;\n }\n\n case \"error\":\n return {\n type: \"error\",\n timestamp,\n category: \"runtime\",\n message: String(event.message || event.error || \"Unknown error\"),\n code: typeof event.code === \"string\" ? event.code : undefined,\n } satisfies ErrorEvent;\n\n case \"status\":\n // Status events don't become trace events, they become signals\n return null;\n\n case \"result\": {\n const finalText =\n typeof (event as { finalText?: unknown }).finalText === \"string\"\n ? (event as { finalText?: string }).finalText\n : typeof (event as { text?: unknown }).text === \"string\"\n ? (event as { text?: string }).text\n : undefined;\n\n return {\n type: \"message.updated\",\n timestamp,\n text: finalText,\n finalText,\n tokenUsage: (event as { tokenUsage?: unknown }).tokenUsage as\n | Record<string, unknown>\n | undefined,\n timing: (event as { timing?: unknown }).timing as\n | Record<string, unknown>\n | undefined,\n toolInvocations: (event as { toolInvocations?: unknown })\n .toolInvocations as unknown[] | undefined,\n metadata: (event as { metadata?: unknown }).metadata as\n | Record<string, unknown>\n | undefined,\n } satisfies MessageUpdatedEvent;\n }\n case \"done\":\n // Terminal events - skip as trace events\n return null;\n\n default:\n // Capture unknown events as custom type for extensibility\n if (event.type && event.type !== \"heartbeat\") {\n return {\n type: \"custom\",\n timestamp,\n name: event.type,\n data: { eventType: event.type, ...event },\n } satisfies CustomEvent;\n }\n return null;\n }\n}\n\n/**\n * Extract a signal from an SSE event if present.\n */\nexport function sseToSignal(event: SSEEventData): SignalEvent | null {\n if (event.type === \"status\" && typeof event.status === \"string\") {\n if (VALID_SIGNALS.includes(event.status as Signal)) {\n return {\n signal: event.status as Signal,\n timestamp: new Date(),\n };\n }\n }\n return null;\n}\n\n/**\n * Check if an SSE event indicates completion (success or failure).\n */\nexport function isCompletionEvent(event: SSEEventData): {\n complete: boolean;\n success: boolean;\n} {\n if (event.type === \"done\" || event.type === \"result\") {\n const success = !(\"success\" in event && event.success === false);\n return { complete: true, success };\n }\n if (event.type === \"error\") {\n return { complete: true, success: false };\n }\n return { complete: false, success: false };\n}\n","/**\n * Trace Collector\n *\n * Collects execution traces and exports them to telemetry sinks.\n * Designed for cross-user data aggregation and analysis.\n */\n\nimport type {\n ExecutionTrace,\n Outcome,\n Signal,\n TraceEvent,\n TraceScope,\n} from \"./trace-types.js\";\nimport type { TelemetrySink } from \"./types.js\";\n\n// Web Crypto instead of a static `node:crypto` import keeps this module's barrel\n// (`@tangle-network/sdk-telemetry`) browser-safe — hub-sdk re-exports the barrel\n// and platform-web ships it to the browser, where a `node:crypto` import is\n// externalized and blanks the page. `globalThis.crypto` is present in Node 20+\n// and secure browser contexts (HTTPS / localhost) but ABSENT over plain HTTP, so\n// fall back to a Math.random v4 there — a trace id needs uniqueness, not\n// cryptographic strength.\nconst randomUUID = (): string => {\n const webCrypto = globalThis.crypto;\n if (webCrypto?.randomUUID) return webCrypto.randomUUID();\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (ch) => {\n const r = (Math.random() * 16) | 0;\n return (ch === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n};\n\nexport interface TraceCollectorConfig {\n /** Sink to export traces to */\n sink: TelemetrySink;\n /** Max traces to buffer before auto-flush */\n maxBufferSize?: number;\n /** Auto-flush interval in ms (0 = manual only) */\n flushIntervalMs?: number;\n /** Default scope for new traces */\n defaultScope?: TraceScope;\n}\n\n/** Active trace context for tracking a single execution */\nexport interface TraceContext {\n traceId: string;\n startedAt: Date;\n /** Add a typed event to the trace */\n addEvent<T extends TraceEvent>(event: Omit<T, \"timestamp\">): void;\n /** Add a signal (success/failure indicator) */\n addSignal(signal: Signal, metadata?: Record<string, unknown>): void;\n /** Complete the trace successfully */\n complete(metadata?: Record<string, unknown>): ExecutionTrace;\n /** Complete the trace with failure */\n fail(\n error: string | Error,\n metadata?: Record<string, unknown>,\n ): ExecutionTrace;\n /** Abandon the trace */\n abandon(): ExecutionTrace;\n}\n\ninterface ActiveTrace {\n id: string;\n scope: TraceScope;\n task: string;\n events: TraceEvent[];\n signals: Array<{\n signal: Signal;\n timestamp: Date;\n metadata?: Record<string, unknown>;\n }>;\n startedAt: Date;\n metadata?: Record<string, unknown>;\n}\n\nexport class TraceCollector {\n private sink: TelemetrySink;\n private config: Required<\n Omit<TraceCollectorConfig, \"sink\" | \"defaultScope\">\n > & { defaultScope?: TraceScope };\n private activeTraces = new Map<string, ActiveTrace>();\n private completedTraces: ExecutionTrace[] = [];\n private flushTimer?: ReturnType<typeof setInterval>;\n private sequence = 0;\n\n constructor(config: TraceCollectorConfig) {\n this.sink = config.sink;\n this.config = {\n maxBufferSize: config.maxBufferSize ?? 100,\n flushIntervalMs: config.flushIntervalMs ?? 0,\n defaultScope: config.defaultScope,\n };\n\n if (this.config.flushIntervalMs > 0) {\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.config.flushIntervalMs);\n }\n }\n\n /** Start tracking a new execution */\n startTrace(\n task: string,\n scope?: TraceScope,\n metadata?: Record<string, unknown>,\n ): TraceContext {\n const traceId = `trace_${randomUUID()}`;\n const startedAt = new Date();\n const traceScope = scope ?? this.config.defaultScope ?? {};\n\n const activeTrace: ActiveTrace = {\n id: traceId,\n scope: traceScope,\n task,\n events: [],\n signals: [],\n startedAt,\n metadata,\n };\n\n this.activeTraces.set(traceId, activeTrace);\n\n return {\n traceId,\n startedAt,\n\n addEvent: <T extends TraceEvent>(event: Omit<T, \"timestamp\">) => {\n const trace = this.activeTraces.get(traceId);\n if (trace) {\n trace.events.push({ ...event, timestamp: new Date() } as T);\n }\n },\n\n addSignal: (signal, signalMeta) => {\n const trace = this.activeTraces.get(traceId);\n if (trace) {\n trace.signals.push({\n signal,\n timestamp: new Date(),\n metadata: signalMeta,\n });\n }\n },\n\n complete: (completeMeta) => {\n return this.completeTrace(traceId, \"success\", completeMeta);\n },\n\n fail: (error, failMeta) => {\n const trace = this.activeTraces.get(traceId);\n if (trace) {\n const errorMsg = error instanceof Error ? error.message : error;\n trace.events.push({\n type: \"error\",\n timestamp: new Date(),\n category: \"runtime\",\n message: errorMsg,\n stack: error instanceof Error ? error.stack : undefined,\n });\n trace.signals.push({\n signal: \"runtime_error\",\n timestamp: new Date(),\n });\n }\n return this.completeTrace(traceId, \"failure\", failMeta);\n },\n\n abandon: () => {\n return this.completeTrace(traceId, \"unknown\", { abandoned: true });\n },\n };\n }\n\n /** Convenience: track a complete execution with a wrapper function */\n async track<T>(\n task: string,\n fn: (ctx: TraceContext) => Promise<T>,\n options?: { scope?: TraceScope; metadata?: Record<string, unknown> },\n ): Promise<{ result: T; trace: ExecutionTrace }> {\n const ctx = this.startTrace(task, options?.scope, options?.metadata);\n try {\n const result = await fn(ctx);\n const trace = ctx.complete();\n return { result, trace };\n } catch (error) {\n ctx.fail(error instanceof Error ? error : String(error));\n throw error;\n }\n }\n\n /** Flush completed traces to sink */\n async flush(): Promise<void> {\n if (this.completedTraces.length === 0) return;\n\n const events = this.completedTraces.map((trace) => ({\n id: `evt_${randomUUID()}`,\n type: \"agent_execution\" as const,\n name: \"execution_trace\",\n projectRef: trace.scope.projectRef,\n sessionId: trace.scope.sessionId,\n userId: trace.scope.userId,\n data: {\n traceId: trace.id,\n task: trace.task,\n outcome: trace.outcome,\n eventCount: trace.events.length,\n signalCount: trace.signals.length,\n durationMs: trace.completedAt\n ? trace.completedAt.getTime() - trace.startedAt.getTime()\n : undefined,\n // Full trace data for detailed analysis\n trace,\n },\n timestamp: trace.completedAt ?? new Date(),\n sequence: this.sequence++,\n }));\n\n await this.sink.send(events);\n this.completedTraces = [];\n }\n\n /** Get count of pending traces */\n getPendingCount(): number {\n return this.completedTraces.length;\n }\n\n /** Get active trace count */\n getActiveCount(): number {\n return this.activeTraces.size;\n }\n\n /** Shutdown collector */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n await this.flush();\n await this.sink.flush();\n await this.sink.close();\n }\n\n private completeTrace(\n traceId: string,\n outcome: Outcome,\n metadata?: Record<string, unknown>,\n ): ExecutionTrace {\n const active = this.activeTraces.get(traceId);\n if (!active) {\n throw new Error(`Trace not found: ${traceId}`);\n }\n\n this.activeTraces.delete(traceId);\n\n const completedTrace: ExecutionTrace = {\n id: active.id,\n scope: active.scope,\n task: active.task,\n events: active.events,\n signals: active.signals,\n outcome: this.classifyOutcome(active.signals, outcome),\n startedAt: active.startedAt,\n completedAt: new Date(),\n metadata: { ...active.metadata, ...metadata },\n };\n\n this.completedTraces.push(completedTrace);\n\n if (this.completedTraces.length >= this.config.maxBufferSize) {\n this.flush().catch(() => {});\n }\n\n return completedTrace;\n }\n\n private classifyOutcome(\n signals: Array<{ signal: Signal }>,\n defaultOutcome: Outcome,\n ): Outcome {\n const signalSet = new Set(signals.map((s) => s.signal));\n\n if (signalSet.has(\"task_completed\")) {\n if (\n signalSet.has(\"tests_passed\") ||\n signalSet.has(\"build_passed\") ||\n signalSet.has(\"user_approved\")\n ) {\n return \"success\";\n }\n return \"partial\";\n }\n\n if (\n signalSet.has(\"runtime_error\") ||\n signalSet.has(\"timeout\") ||\n signalSet.has(\"tests_failed\") ||\n signalSet.has(\"build_failed\") ||\n signalSet.has(\"user_rejected\")\n ) {\n return \"failure\";\n }\n\n if (signalSet.has(\"task_abandoned\")) {\n return \"unknown\";\n }\n\n return defaultOutcome;\n }\n}\n\nexport function createTraceCollector(\n config: TraceCollectorConfig,\n): TraceCollector {\n return new TraceCollector(config);\n}\n","/**\n * Telemetry Tracker\n *\n * Core telemetry collection and batching system.\n * Supports offline persistence, sampling, and multiple sinks.\n */\n\nimport type { Storage } from \"@tangle-network/agent-core\";\nimport type {\n PerformanceMetric,\n TelemetryConfig,\n TelemetryEvent,\n TelemetryEventType,\n TelemetrySink,\n} from \"./types.js\";\n\nconst DEFAULT_CONFIG: Required<\n Omit<TelemetryConfig, \"endpoint\" | \"projectRef\" | \"userId\" | \"sessionId\">\n> = {\n enabled: true,\n flushIntervalMs: 30000,\n maxBatchSize: 100,\n maxQueueSize: 1000,\n sampleRate: 1.0,\n excludePatterns: [],\n includeMetadata: true,\n persistOffline: true,\n};\n\nexport class TelemetryTracker {\n private readonly config: Required<\n Omit<TelemetryConfig, \"endpoint\" | \"projectRef\" | \"userId\" | \"sessionId\">\n > &\n TelemetryConfig;\n private readonly sinks: TelemetrySink[] = [];\n private readonly queue: TelemetryEvent[] = [];\n private readonly storage?: Storage;\n private sequence = 0;\n private flushTimer?: ReturnType<typeof setInterval>;\n private sessionStartTime?: Date;\n private isFlushing = false;\n\n constructor(config: TelemetryConfig, storage?: Storage) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.storage = storage;\n\n if (this.config.enabled && this.config.flushIntervalMs > 0) {\n this.startFlushTimer();\n }\n }\n\n addSink(sink: TelemetrySink): this {\n this.sinks.push(sink);\n return this;\n }\n\n removeSink(name: string): this {\n const index = this.sinks.findIndex((s) => s.name === name);\n if (index !== -1) {\n this.sinks.splice(index, 1);\n }\n return this;\n }\n\n startSession(metadata?: Record<string, unknown>): string {\n const sessionId =\n this.config.sessionId ??\n `session-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n this.sessionStartTime = new Date();\n\n this.track(\"session_start\", \"session.started\", {\n ...metadata,\n sessionId,\n startTime: this.sessionStartTime.toISOString(),\n });\n\n return sessionId;\n }\n\n endSession(metadata?: Record<string, unknown>): void {\n const duration = this.sessionStartTime\n ? Date.now() - this.sessionStartTime.getTime()\n : 0;\n\n this.track(\"session_end\", \"session.ended\", {\n ...metadata,\n durationMs: duration,\n eventCount: this.sequence,\n });\n\n this.flush();\n }\n\n track(\n type: TelemetryEventType,\n name: string,\n data: Record<string, unknown> = {},\n ): void {\n if (!this.config.enabled) return;\n\n if (!this.shouldSample()) return;\n\n if (this.isExcluded(name)) return;\n\n const event: TelemetryEvent = {\n id: `evt-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n type,\n name,\n projectRef: this.config.projectRef,\n sessionId: this.config.sessionId,\n userId: this.config.userId,\n data: this.config.includeMetadata ? this.enrichData(data) : data,\n timestamp: new Date(),\n sequence: ++this.sequence,\n };\n\n this.enqueue(event);\n }\n\n trackPerformance(metric: Omit<PerformanceMetric, \"timestamp\">): void {\n this.track(\"performance\", `perf.${metric.name}`, {\n value: metric.value,\n unit: metric.unit,\n tags: metric.tags,\n });\n }\n\n trackError(error: Error | string, context?: Record<string, unknown>): void {\n const errorData =\n error instanceof Error\n ? {\n message: error.message,\n name: error.name,\n stack: error.stack,\n }\n : { message: error };\n\n this.track(\"error\", \"error.occurred\", {\n ...errorData,\n ...context,\n });\n }\n\n trackApiRequest(\n method: string,\n path: string,\n status: number,\n durationMs: number,\n metadata?: Record<string, unknown>,\n ): void {\n this.track(\"api_request\", `api.${method.toLowerCase()}.${path}`, {\n method,\n path,\n status,\n durationMs,\n ...metadata,\n });\n }\n\n trackAgentExecution(\n identifier: string,\n executionId: string,\n durationMs: number,\n success: boolean,\n metadata?: Record<string, unknown>,\n ): void {\n this.track(\"agent_execution\", `agent.${identifier}`, {\n identifier,\n executionId,\n durationMs,\n success,\n ...metadata,\n });\n }\n\n async flush(): Promise<void> {\n if (this.isFlushing || this.queue.length === 0) return;\n\n this.isFlushing = true;\n\n try {\n const events = this.queue.splice(0, this.config.maxBatchSize);\n\n await Promise.all(this.sinks.map((sink) => sink.send(events)));\n\n if (this.config.persistOffline && this.storage) {\n await this.clearPersistedEvents(events.map((e) => e.id));\n }\n } catch (_error) {\n if (this.config.persistOffline && this.storage) {\n await this.persistEvents(this.queue);\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n async close(): Promise<void> {\n this.stopFlushTimer();\n await this.flush();\n await Promise.all(this.sinks.map((sink) => sink.close()));\n }\n\n getQueueSize(): number {\n return this.queue.length;\n }\n\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n enable(): void {\n (this.config as { enabled: boolean }).enabled = true;\n this.startFlushTimer();\n }\n\n disable(): void {\n (this.config as { enabled: boolean }).enabled = false;\n this.stopFlushTimer();\n }\n\n private enqueue(event: TelemetryEvent): void {\n if (this.queue.length >= this.config.maxQueueSize) {\n this.queue.shift();\n }\n this.queue.push(event);\n\n if (this.queue.length >= this.config.maxBatchSize) {\n this.flush();\n }\n\n if (this.config.persistOffline && this.storage) {\n this.persistEvent(event);\n }\n }\n\n private shouldSample(): boolean {\n return Math.random() < this.config.sampleRate;\n }\n\n private isExcluded(name: string): boolean {\n return this.config.excludePatterns.some((pattern) => {\n if (pattern.includes(\"*\")) {\n const regex = new RegExp(\n `^${pattern.replace(/\\*/g, \".*\").replace(/\\?/g, \".\")}$`,\n );\n return regex.test(name);\n }\n return name === pattern;\n });\n }\n\n private enrichData(data: Record<string, unknown>): Record<string, unknown> {\n return {\n ...data,\n _meta: {\n timestamp: Date.now(),\n platform:\n typeof navigator !== \"undefined\" ? navigator.userAgent : \"node\",\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n },\n };\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(\n () => this.flush(),\n this.config.flushIntervalMs,\n );\n }\n\n private stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n }\n\n private async persistEvent(event: TelemetryEvent): Promise<void> {\n if (!this.storage) return;\n const key = `telemetry:${event.id}`;\n await this.storage.set(key, event);\n }\n\n private async persistEvents(events: TelemetryEvent[]): Promise<void> {\n if (!this.storage) return;\n await Promise.all(events.map((e) => this.persistEvent(e)));\n }\n\n private async clearPersistedEvents(ids: string[]): Promise<void> {\n if (!this.storage) return;\n await Promise.all(ids.map((id) => this.storage?.delete(`telemetry:${id}`)));\n }\n\n async loadPersistedEvents(): Promise<TelemetryEvent[]> {\n if (!this.storage) return [];\n\n const keys = await this.storage.keys();\n const telemetryKeys = keys.filter((k) => k.startsWith(\"telemetry:\"));\n const events: TelemetryEvent[] = [];\n\n for (const key of telemetryKeys) {\n const event = await this.storage.get<TelemetryEvent>(key);\n if (event) events.push(event);\n }\n\n return events.sort((a, b) => a.sequence - b.sequence);\n }\n}\n\nexport function createTelemetryTracker(\n config: TelemetryConfig,\n storage?: Storage,\n): TelemetryTracker {\n return new TelemetryTracker(config, storage);\n}\n","/**\n * Usage Tracker\n *\n * Automatic usage recording with batching, aggregation,\n * and integration with interceptor system.\n */\n\nimport type { Storage } from \"@tangle-network/agent-core\";\nimport type {\n AgentMetric,\n ApiMetric,\n UsageProvider,\n UsageRecord,\n UsageSummary,\n UsageTrackerConfig,\n} from \"./types.js\";\n\nconst DEFAULT_CONFIG = {\n autoTrack: true,\n trackApiCalls: true,\n trackAgentExecutions: true,\n trackErrors: true,\n batchSize: 50,\n flushIntervalMs: 30000,\n};\n\n// Cost multipliers for different operations\nconst OPERATION_COSTS: Record<string, number> = {\n \"api.get\": 0.001,\n \"api.post\": 0.002,\n \"api.put\": 0.002,\n \"api.delete\": 0.001,\n \"agent.execution\": 0.01,\n \"agent.streaming\": 0.015,\n \"token.input\": 0.00001,\n \"token.output\": 0.00003,\n default: 0.001,\n};\n\nexport class UsageTracker {\n private readonly config: Required<Omit<UsageTrackerConfig, \"userId\">> &\n UsageTrackerConfig;\n private readonly provider?: UsageProvider;\n private readonly storage?: Storage;\n private readonly queue: Omit<UsageRecord, \"id\" | \"timestamp\">[] = [];\n private flushTimer?: ReturnType<typeof setInterval>;\n private isFlushing = false;\n\n // Aggregated metrics for current session\n private sessionMetrics = {\n totalCredits: 0,\n totalOperations: 0,\n totalDurationMs: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n operationCounts: new Map<string, number>(),\n startTime: Date.now(),\n };\n\n constructor(\n config: UsageTrackerConfig,\n provider?: UsageProvider,\n storage?: Storage,\n ) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.provider = provider;\n this.storage = storage;\n\n if (this.config.flushIntervalMs > 0) {\n this.startFlushTimer();\n }\n }\n\n recordApiCall(metric: ApiMetric): void {\n if (!this.config.trackApiCalls) return;\n\n const operation = `api.${metric.method.toLowerCase()}`;\n const credits = this.calculateCost(operation, {\n durationMs: metric.durationMs,\n requestSize: metric.requestSize,\n responseSize: metric.responseSize,\n });\n\n this.record({\n projectRef: this.config.projectRef,\n userId: this.config.userId,\n operation,\n credits,\n durationMs: metric.durationMs,\n metadata: {\n path: metric.path,\n status: metric.status,\n requestSize: metric.requestSize,\n responseSize: metric.responseSize,\n error: metric.error,\n },\n });\n }\n\n recordAgentExecution(metric: AgentMetric): void {\n if (!this.config.trackAgentExecutions) return;\n\n const operation = \"agent.execution\";\n const tokenCost =\n (metric.inputTokens ?? 0) * OPERATION_COSTS[\"token.input\"] +\n (metric.outputTokens ?? 0) * OPERATION_COSTS[\"token.output\"];\n\n const baseCost = this.calculateCost(operation, {\n durationMs: metric.durationMs,\n });\n\n const credits = baseCost + tokenCost;\n\n this.record({\n projectRef: metric.projectRef,\n userId: this.config.userId,\n operation,\n credits,\n durationMs: metric.durationMs,\n inputTokens: metric.inputTokens,\n outputTokens: metric.outputTokens,\n metadata: {\n identifier: metric.identifier,\n executionId: metric.executionId,\n toolCalls: metric.toolCalls,\n success: metric.success,\n error: metric.error,\n },\n });\n }\n\n recordError(error: Error | string, operation: string): void {\n if (!this.config.trackErrors) return;\n\n this.record({\n projectRef: this.config.projectRef,\n userId: this.config.userId,\n operation: `error.${operation}`,\n credits: 0,\n metadata: {\n error: error instanceof Error ? error.message : error,\n stack: error instanceof Error ? error.stack : undefined,\n },\n });\n }\n\n record(usage: Omit<UsageRecord, \"id\" | \"timestamp\">): void {\n this.queue.push(usage);\n\n // Update session metrics\n this.sessionMetrics.totalCredits += usage.credits;\n this.sessionMetrics.totalOperations++;\n this.sessionMetrics.totalDurationMs += usage.durationMs ?? 0;\n this.sessionMetrics.totalInputTokens += usage.inputTokens ?? 0;\n this.sessionMetrics.totalOutputTokens += usage.outputTokens ?? 0;\n\n const count = this.sessionMetrics.operationCounts.get(usage.operation) ?? 0;\n this.sessionMetrics.operationCounts.set(usage.operation, count + 1);\n\n if (this.queue.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.isFlushing || this.queue.length === 0) return;\n\n this.isFlushing = true;\n const records = this.queue.splice(0, this.config.batchSize);\n const providerFailed = new Set<(typeof records)[number]>();\n const providerDone = new Set<(typeof records)[number]>();\n\n try {\n if (this.provider) {\n const results = await Promise.allSettled(\n records.map((r) => this.provider?.recordUsage(r)),\n );\n for (let i = 0; i < records.length; i++) {\n if (results[i].status === \"rejected\") {\n providerFailed.add(records[i]);\n } else {\n providerDone.add(records[i]);\n }\n }\n if (providerFailed.size > 0) {\n this.queue.unshift(...providerFailed);\n }\n }\n\n if (this.storage) {\n // Only persist records that weren't rejected by the provider.\n // Provider-failed records are re-queued above and will be\n // persisted on a successful retry — persisting them now would\n // cause double-counting when the retry also persists.\n const toPersist =\n providerFailed.size > 0\n ? records.filter((r) => !providerFailed.has(r))\n : records;\n if (toPersist.length > 0) {\n await this.persistRecords(toPersist);\n }\n }\n } catch (error) {\n // Storage persistence failure — re-queue ONLY records that (a)\n // weren't written to storage yet, AND (b) haven't already been\n // processed by the provider. Provider-accepted records are not\n // re-queued even if their storage write failed: the provider is\n // the billing source of truth and re-sending would double-bill.\n // Records before `firstFailedIndex` were successfully persisted\n // and must not be re-queued either — re-queuing them would write\n // a second copy under fresh keys on the retry (records were\n // previously `Omit<UsageRecord, \"id\" | \"timestamp\">`, so each\n // flush pass generates new ids).\n const firstFailed =\n typeof (error as { firstFailedIndex?: number })?.firstFailedIndex ===\n \"number\"\n ? (error as { firstFailedIndex: number }).firstFailedIndex\n : 0;\n const unwritten = records.slice(firstFailed);\n if (providerDone.size > 0) {\n console.warn(\n `[usage-tracker] storage persistence failed for ${providerDone.size} provider-accepted records — local audit trail incomplete`,\n error instanceof Error ? error.message : String(error),\n );\n }\n const unhandled = unwritten.filter(\n (r) => !providerFailed.has(r) && !providerDone.has(r),\n );\n if (unhandled.length > 0) {\n this.queue.unshift(...unhandled);\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n async getUsage(options?: {\n since?: Date;\n until?: Date;\n limit?: number;\n }): Promise<UsageRecord[]> {\n if (this.provider) {\n return this.provider.getUsage({\n projectRef: this.config.projectRef,\n ...options,\n });\n }\n\n // Load from local storage\n return this.loadPersistedRecords(options);\n }\n\n async getSummary(\n period: UsageSummary[\"period\"] = \"day\",\n ): Promise<UsageSummary> {\n if (this.provider) {\n return this.provider.getSummary(this.config.projectRef, period);\n }\n\n // Generate local summary\n return this.generateLocalSummary(period);\n }\n\n getSessionMetrics(): {\n totalCredits: number;\n totalOperations: number;\n totalDurationMs: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n operationBreakdown: Record<string, number>;\n sessionDurationMs: number;\n } {\n return {\n totalCredits: this.sessionMetrics.totalCredits,\n totalOperations: this.sessionMetrics.totalOperations,\n totalDurationMs: this.sessionMetrics.totalDurationMs,\n totalInputTokens: this.sessionMetrics.totalInputTokens,\n totalOutputTokens: this.sessionMetrics.totalOutputTokens,\n operationBreakdown: Object.fromEntries(\n this.sessionMetrics.operationCounts,\n ),\n sessionDurationMs: Date.now() - this.sessionMetrics.startTime,\n };\n }\n\n resetSessionMetrics(): void {\n this.sessionMetrics = {\n totalCredits: 0,\n totalOperations: 0,\n totalDurationMs: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n operationCounts: new Map(),\n startTime: Date.now(),\n };\n }\n\n async close(): Promise<void> {\n this.stopFlushTimer();\n await this.flush();\n }\n\n private calculateCost(\n operation: string,\n factors: {\n durationMs?: number;\n requestSize?: number;\n responseSize?: number;\n },\n ): number {\n const baseCost = OPERATION_COSTS[operation] ?? OPERATION_COSTS.default;\n let cost = baseCost;\n\n // Add duration-based cost (per second)\n if (factors.durationMs) {\n cost += (factors.durationMs / 1000) * 0.001;\n }\n\n // Add size-based cost (per KB)\n if (factors.requestSize) {\n cost += (factors.requestSize / 1024) * 0.0001;\n }\n if (factors.responseSize) {\n cost += (factors.responseSize / 1024) * 0.0001;\n }\n\n return Math.round(cost * 100000) / 100000;\n }\n\n private async persistRecords(\n records: Omit<UsageRecord, \"id\" | \"timestamp\">[],\n ): Promise<void> {\n if (!this.storage) return;\n\n const timestamp = Date.now();\n for (let i = 0; i < records.length; i++) {\n const record = records[i];\n const id = `usage-${timestamp}-${i}`;\n try {\n await this.storage.set(`usage:${id}`, {\n ...record,\n id,\n timestamp: new Date(),\n });\n } catch (err) {\n // Attach the failed index so flush()'s catch block can re-queue\n // only records[i..] — records 0..i-1 were already written to\n // storage. Without this, a mid-batch failure would re-queue\n // the full batch and the retry would persist records 0..i-1\n // a second time under fresh `usage:{T2}-N` keys, duplicating\n // audit rows. Using Object.assign preserves the original\n // error's type and stack for observability.\n throw Object.assign(\n err instanceof Error ? err : new Error(String(err)),\n { firstFailedIndex: i },\n );\n }\n }\n }\n\n private async loadPersistedRecords(options?: {\n since?: Date;\n until?: Date;\n limit?: number;\n }): Promise<UsageRecord[]> {\n if (!this.storage) return [];\n\n const keys = await this.storage.keys();\n const usageKeys = keys.filter((k) => k.startsWith(\"usage:\"));\n const records: UsageRecord[] = [];\n\n for (const key of usageKeys) {\n const record = await this.storage.get<UsageRecord>(key);\n if (!record) continue;\n\n if (options?.since && record.timestamp < options.since) continue;\n if (options?.until && record.timestamp > options.until) continue;\n\n records.push(record);\n }\n\n records.sort(\n (a, b) =>\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n\n return options?.limit ? records.slice(0, options.limit) : records;\n }\n\n private async generateLocalSummary(\n period: UsageSummary[\"period\"],\n ): Promise<UsageSummary> {\n const now = new Date();\n const periodStart = this.getPeriodStart(now, period);\n const records = await this.loadPersistedRecords({\n since: periodStart,\n until: now,\n });\n\n const operationBreakdown: Record<string, number> = {};\n let totalCredits = 0;\n let totalDurationMs = 0;\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n\n for (const record of records) {\n totalCredits += record.credits;\n totalDurationMs += record.durationMs ?? 0;\n totalInputTokens += record.inputTokens ?? 0;\n totalOutputTokens += record.outputTokens ?? 0;\n operationBreakdown[record.operation] =\n (operationBreakdown[record.operation] ?? 0) + 1;\n }\n\n return {\n projectRef: this.config.projectRef,\n period,\n periodStart,\n periodEnd: now,\n totalCredits,\n totalOperations: records.length,\n totalDurationMs,\n totalInputTokens,\n totalOutputTokens,\n operationBreakdown,\n };\n }\n\n private getPeriodStart(now: Date, period: UsageSummary[\"period\"]): Date {\n const start = new Date(now);\n\n switch (period) {\n case \"hour\":\n start.setMinutes(0, 0, 0);\n break;\n case \"day\":\n start.setHours(0, 0, 0, 0);\n break;\n case \"week\":\n start.setDate(start.getDate() - start.getDay());\n start.setHours(0, 0, 0, 0);\n break;\n case \"month\":\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n break;\n }\n\n return start;\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(\n () => this.flush(),\n this.config.flushIntervalMs,\n );\n }\n\n private stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n }\n}\n\n// Interceptor factory for automatic API tracking\nexport function createUsageInterceptor(tracker: UsageTracker) {\n return {\n onRequest(ctx: {\n request: { method: string; url: string };\n startedAt: number;\n }) {\n return ctx;\n },\n\n onResponse<T>(ctx: {\n request: { method: string; url: string };\n response: { status: number };\n startedAt: number;\n }): T {\n const durationMs = Date.now() - ctx.startedAt;\n tracker.recordApiCall({\n path: new URL(ctx.request.url).pathname,\n method: ctx.request.method,\n status: ctx.response.status,\n durationMs,\n timestamp: new Date(),\n });\n return undefined as T;\n },\n\n onError(ctx: {\n request: { method: string; url: string };\n error: Error;\n startedAt: number;\n }) {\n const durationMs = Date.now() - ctx.startedAt;\n tracker.recordApiCall({\n path: new URL(ctx.request.url).pathname,\n method: ctx.request.method,\n status: 0,\n durationMs,\n error: ctx.error.message,\n timestamp: new Date(),\n });\n },\n };\n}\n\nexport function createUsageTracker(\n config: UsageTrackerConfig,\n provider?: UsageProvider,\n storage?: Storage,\n): UsageTracker {\n return new UsageTracker(config, provider, storage);\n}\n"],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ //#region src/stage-span.d.ts
2
+ /**
3
+ * One span shape for every lifecycle-op stage across every hop.
4
+ *
5
+ * The problem this solves: each service used to time its own stages with an
6
+ * isolated `Date.now()` map (worker `stageMs`, orchestrator `startupDiagnostics`,
7
+ * host `StartupBootstrapRecorder`, sidecar Prometheus) joined by disjoint ids,
8
+ * so no single query reconstructs one operation end-to-end. `StageSpan` is the
9
+ * shared record; `SpanRecorder` collects them under one correlation id (`ptid`)
10
+ * and renders the same `Server-Timing` slice everywhere. Existing recorders
11
+ * emit through this rather than hand-rolling a parallel timer.
12
+ *
13
+ * Runtime-portability: this module imports nothing Node-only (no `process`, no
14
+ * `@repo/shared`, no otel) so it is safe to bundle into the Cloudflare Worker
15
+ * via the `@tangle-network/sdk-telemetry/stage-span` subpath. The monotonic
16
+ * clock is `performance.now()`, which is a global in both Node 18+ and Workers.
17
+ */
18
+ /** Request header that carries the one correlation id across every hop. */
19
+ declare const PROVISION_TRACE_ID_HEADER = "x-tangle-trace-id";
20
+ /** Correlation id prefix minted at the edge (`ptid_<uuid>`). */
21
+ declare const PTID_PREFIX = "ptid_";
22
+ type LifecycleOp = "create" | "branch" | "resume" | "suspend" | "snapshot" | "restore" | "exec" | "delete" | "turn";
23
+ type SpanComponent = "client" | "cf-worker" | "sandbox-api" | "orchestrator" | "host-agent" | "sidecar" | "storage" | "container";
24
+ type SpanStatus = "completed" | "skipped" | "error";
25
+ /**
26
+ * One timed stage of one lifecycle op, keyed by (ptid, op, stage, component).
27
+ * `startNs`/`endNs` are monotonic-clock nanoseconds (see {@link monotonicNs});
28
+ * only their difference is meaningful — they are NOT wall-clock timestamps.
29
+ * `parentStage` builds a tree: a span with `parentStage` set is a child that
30
+ * rolls up under its parent and does NOT count toward the top-level residual.
31
+ */
32
+ interface StageSpan {
33
+ ptid: string;
34
+ op: LifecycleOp;
35
+ stage: string;
36
+ component: SpanComponent;
37
+ startNs: bigint;
38
+ endNs: bigint;
39
+ status: SpanStatus;
40
+ parentStage?: string;
41
+ attrs?: Record<string, string | number>;
42
+ }
43
+ /** Monotonic clock → nanoseconds. Portable across Node and Workers. */
44
+ declare function monotonicNs(): bigint;
45
+ /** Duration of a span in fractional milliseconds. */
46
+ declare function spanDurationMs(span: Pick<StageSpan, "startNs" | "endNs">): number;
47
+ declare function isAdoptablePtid(value: string | null | undefined): value is string;
48
+ /** Mint a fresh edge-origin correlation id. */
49
+ declare function mintPtid(): string;
50
+ /**
51
+ * Adopt an inbound `x-tangle-trace-id` when present and well-formed (so the
52
+ * SDK can supply a true client-origin root), otherwise mint one. This is the
53
+ * single decision every hop runs — the edge mints, every downstream hop adopts
54
+ * the forwarded value, so the id is born once and identical everywhere.
55
+ */
56
+ declare function readOrMintPtid(headerValue: string | null | undefined): string;
57
+ /** Input to {@link SpanRecorder.span} — ptid/op are fixed by the recorder. */
58
+ type StageSpanInput = Omit<StageSpan, "ptid" | "op">;
59
+ /** Options carried when recording a stage from a known duration. */
60
+ interface RecordOptions {
61
+ status?: SpanStatus;
62
+ parentStage?: string;
63
+ attrs?: Record<string, string | number>;
64
+ }
65
+ /** Parsed `name;dur=<ms>` entry (and any `desc`) from a Server-Timing header. */
66
+ interface ServerTimingEntry {
67
+ name: string;
68
+ dur?: number;
69
+ desc?: string;
70
+ }
71
+ /**
72
+ * Collects {@link StageSpan}s for ONE op under ONE `ptid` and renders them to a
73
+ * `Server-Timing` slice / a durable span array / a residual reconciliation.
74
+ * There is exactly one recorder per op; existing per-service timers become thin
75
+ * adapters that call {@link record} or {@link stage} instead of keeping their
76
+ * own map.
77
+ */
78
+ declare class SpanRecorder {
79
+ readonly ptid: string;
80
+ readonly op: LifecycleOp;
81
+ private readonly spans;
82
+ constructor(ptid: string, op: LifecycleOp);
83
+ /** Push a fully-formed span (start/end already measured). Returns it. */
84
+ span(input: StageSpanInput): StageSpan;
85
+ /**
86
+ * Time an async fn on the monotonic clock and record the span. A throw is
87
+ * recorded as `status:"error"` and re-thrown — the timing is never lost.
88
+ */
89
+ stage<T>(stage: string, component: SpanComponent, fn: () => Promise<T>, opts?: RecordOptions): Promise<T>;
90
+ /**
91
+ * Adapter for a stage already timed elsewhere (an existing `Date.now()` map,
92
+ * a fold-back duration from a downstream hop): synthesize a span of the given
93
+ * millisecond duration ending now. Only the duration is load-bearing.
94
+ */
95
+ record(stage: string, component: SpanComponent, durationMs: number, opts?: RecordOptions): StageSpan;
96
+ /** All spans in record order (durable store payload — the full tree). */
97
+ list(): readonly StageSpan[];
98
+ /** Top-level spans (no `parentStage`) — the ones the residual accounts for. */
99
+ topLevel(): StageSpan[];
100
+ /**
101
+ * Residual accounting: `sum(topLevelLeaves) + residual === total`, computed on
102
+ * the SAME rounded integers the header emits so the identity is exact. When
103
+ * `total` is omitted it is the wall span (max end − min start) across all
104
+ * spans. `residual` is the unaccounted gap — negative when stages overlap
105
+ * (parallelized work), the honest signal the CI dark-gap gate reads.
106
+ */
107
+ reconcile(total?: number): {
108
+ total: number;
109
+ leavesSum: number;
110
+ residual: number;
111
+ leaves: Array<{
112
+ stage: string;
113
+ dur: number;
114
+ }>;
115
+ };
116
+ /** Wall duration across all recorded spans, in ms. */
117
+ wallMs(): number;
118
+ /**
119
+ * Render the `Server-Timing` slice for this op: one `stage;dur=` per
120
+ * top-level span, then `<op>_unaccounted;dur=<residual>`, then
121
+ * `total;dur=<total>`. By construction the non-total entries sum to `total`,
122
+ * so a browser Network tab and the durable store agree. Skipped spans carry
123
+ * `desc="skipped"`; string attrs surface as `desc`. Nested children are
124
+ * omitted from the header (they live in {@link list}) to keep the sum exact.
125
+ */
126
+ toServerTiming(opts?: {
127
+ total?: number;
128
+ includePtid?: boolean;
129
+ }): string;
130
+ }
131
+ /**
132
+ * Parse a `Server-Timing` header value into its entries. Reused by the SDK
133
+ * (reading the create response) and the CI dark-gap gate. Tolerant of the
134
+ * `name;dur=X;desc="Y"` and bare-`name` forms Hono/the worker emit.
135
+ */
136
+ declare function parseServerTiming(header: string | null | undefined): ServerTimingEntry[];
137
+ //#endregion
138
+ export { LifecycleOp, PROVISION_TRACE_ID_HEADER, PTID_PREFIX, RecordOptions, ServerTimingEntry, SpanComponent, SpanRecorder, SpanStatus, StageSpan, StageSpanInput, isAdoptablePtid, mintPtid, monotonicNs, parseServerTiming, readOrMintPtid, spanDurationMs };
139
+ //# sourceMappingURL=stage-span.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-span.d.mts","names":[],"sources":["../src/stage-span.ts"],"mappings":";;AAkBA;;;;;AAGA;;;;;AAEA;;;;;AAWA;AAAA,cAhBa,yBAAA;;cAGA,WAAA;AAAA,KAED,WAAA;AAAA,KAWA,aAAA;AAAA,KAUA,UAAA;;;;AASZ;;;;UAAiB,SAAA;EACf,IAAA;EACA,EAAA,EAAI,WAAA;EACJ,KAAA;EACA,SAAA,EAAW,aAAA;EACX,OAAA;EACA,KAAA;EACA,MAAA,EAAQ,UAAA;EACR,WAAA;EACA,KAAA,GAAQ,MAAA;AAAA;;iBAIM,WAAA,CAAA;;iBAKA,cAAA,CACd,IAAA,EAAM,IAAA,CAAK,SAAA;AAAA,iBAaG,eAAA,CACd,KAAA,8BACC,KAAA;;iBAKa,QAAA,CAAA;;;AA1BhB;;;;iBAoCgB,cAAA,CAAe,WAAA;AA/B/B;AAAA,KAoCY,cAAA,GAAiB,IAAA,CAAK,SAAA;;UAGjB,aAAA;EACf,MAAA,GAAS,UAAA;EACT,WAAA;EACA,KAAA,GAAQ,MAAA;AAAA;;UAIO,iBAAA;EACf,IAAA;EACA,GAAA;EACA,IAAA;AAAA;AA5BF;;;;;AAUA;;AAVA,cA4Ca,YAAA;EAAA,SACF,IAAA;EAAA,SACA,EAAA,EAAI,WAAA;EAAA,iBACI,KAAA;cAEL,IAAA,UAAc,EAAA,EAAI,WAAA;;EAM9B,IAAA,CAAK,KAAA,EAAO,cAAA,GAAiB,SAAA;EAxCY;AAG3C;;;EA+CQ,KAAA,GAAA,CACJ,KAAA,UACA,SAAA,EAAW,aAAA,EACX,EAAA,QAAU,OAAA,CAAQ,CAAA,GAClB,IAAA,GAAO,aAAA,GACN,OAAA,CAAQ,CAAA;EAnDX;;;;;EA6EA,MAAA,CACE,KAAA,UACA,SAAA,EAAW,aAAA,EACX,UAAA,UACA,IAAA,GAAO,aAAA,GACN,SAAA;EAhFW;EAgGd,IAAA,CAAA,YAAiB,SAAA;EA5Fe;EAiGhC,QAAA,CAAA,GAAY,SAAA;EAjGoB;;;;;;AAmBlC;EAyFE,SAAA,CAAU,KAAA;IACR,KAAA;IACA,SAAA;IACA,QAAA;IACA,MAAA,EAAQ,KAAA;MAAQ,KAAA;MAAe,GAAA;IAAA;EAAA;EApExB;EAqFT,MAAA,CAAA;EApFG;;;;;;;;EAuGH,cAAA,CAAe,IAAA;IAAS,KAAA;IAAgB,WAAA;EAAA;AAAA;;;;;;iBAgC1B,iBAAA,CACd,MAAA,8BACC,iBAAA"}