canary-agent 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6EMOTHN5.js → chunk-43MHWWT6.js} +2 -2
- package/dist/{chunk-6EMOTHN5.js.map → chunk-43MHWWT6.js.map} +1 -1
- package/dist/{chunk-F5OWE4P5.cjs → chunk-DRKEGQ7U.cjs} +2 -2
- package/dist/{chunk-F5OWE4P5.cjs.map → chunk-DRKEGQ7U.cjs.map} +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/dist/journal/server.cjs +1 -1
- package/dist/journal/server.cjs.map +1 -1
- package/dist/journal/server.js +1 -1
- package/dist/journal/server.js.map +1 -1
- package/dist/openclaw.cjs +6 -6
- package/dist/openclaw.js +1 -1
- package/dist/setup.js +109 -0
- package/package.json +5 -3
- package/skills/canary-feedback/SKILL.md +127 -0
- package/skills/canary-feedback/install.sh +53 -0
- package/skills/canary-feedback/manifest.yaml +45 -0
- package/skills/canary-feedback/mcp-config.json +13 -0
|
@@ -882,7 +882,7 @@ function init(config) {
|
|
|
882
882
|
"Invalid API key: must start with 'cnry_sk_'. Get your key at https://app.canary.dev"
|
|
883
883
|
);
|
|
884
884
|
}
|
|
885
|
-
const endpoint = config.endpoint ?? "
|
|
885
|
+
const endpoint = config.endpoint ?? "https://canary-production-89d8.up.railway.app";
|
|
886
886
|
_autoReport = config.autoReport ?? true;
|
|
887
887
|
const autoFlush = config.autoFlush ?? true;
|
|
888
888
|
_buffer3 = new EventBuffer(
|
|
@@ -967,4 +967,4 @@ export {
|
|
|
967
967
|
survey,
|
|
968
968
|
getBuffer
|
|
969
969
|
};
|
|
970
|
-
//# sourceMappingURL=chunk-
|
|
970
|
+
//# sourceMappingURL=chunk-43MHWWT6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffer.ts","../src/interceptors/http.ts","../src/domains.ts","../src/normalize.ts","../src/extract.ts","../src/context.ts","../src/agent-detect.ts","../src/interceptors/fetch.ts","../src/reporter.ts","../src/adapters/mcp.ts","../src/index.ts"],"sourcesContent":["/**\n * Event buffer with ring buffer, gzip flush, retry, and session cache.\n *\n * Mirrors sdk/src/canary/buffer.py.\n */\n\nimport { gzipSync } from \"node:zlib\";\nimport https from \"node:https\";\nimport http from \"node:http\";\nimport type { CanaryEvent } from \"./types.js\";\n\nconst MAX_LEN = 10_000;\nconst BATCH_SIZE = 100;\nconst FLUSH_INTERVAL_MS = 10_000;\nconst MAX_EVENTS_PER_SESSION = 500;\nconst MAX_SESSION_CACHE_EVENTS = 5_000;\nconst RETRY_ATTEMPTS = 3;\nconst RETRY_BACKOFF_MS = [1000, 2000, 4000];\n\nexport class EventBuffer {\n private _buffer: CanaryEvent[] = [];\n private _sessionCache: Map<string, CanaryEvent[]> = new Map();\n private _sessionCacheEventCount = 0;\n private _apiKey: string;\n private _endpoint: string;\n private _timer: ReturnType<typeof setInterval> | null = null;\n private _stopped = false;\n /** Reference to the original (unpatched) https.request for anti-recursion. */\n private _originalHttpsRequest: typeof https.request;\n /** Reference to the original (unpatched) http.request for anti-recursion. */\n private _originalHttpRequest: typeof http.request;\n\n constructor(\n apiKey: string,\n endpoint = \"http://localhost:8000\",\n autoFlush = true,\n originalHttpsRequest?: typeof https.request,\n originalHttpRequest?: typeof http.request\n ) {\n this._apiKey = apiKey;\n this._endpoint = endpoint.replace(/\\/+$/, \"\");\n this._originalHttpsRequest = originalHttpsRequest ?? https.request;\n this._originalHttpRequest = originalHttpRequest ?? http.request;\n\n if (autoFlush) {\n this._timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);\n this._timer.unref();\n }\n }\n\n /** Add an event to the ring buffer. */\n push(event: CanaryEvent): void {\n if (this._buffer.length >= MAX_LEN) {\n this._buffer.shift(); // drop oldest\n }\n this._buffer.push(event);\n\n // Retain in session cache\n const sessionId = (event as unknown as Record<string, unknown>).framework_session_id;\n if (typeof sessionId === \"string\" && sessionId) {\n if (this._sessionCacheEventCount < MAX_SESSION_CACHE_EVENTS) {\n let sessionEvents = this._sessionCache.get(sessionId);\n if (!sessionEvents) {\n sessionEvents = [];\n this._sessionCache.set(sessionId, sessionEvents);\n }\n if (sessionEvents.length < MAX_EVENTS_PER_SESSION) {\n sessionEvents.push(event);\n this._sessionCacheEventCount++;\n }\n }\n }\n }\n\n /** Return a copy of events for the given session from the cache. */\n getSessionEvents(sessionId: string): CanaryEvent[] {\n return [...(this._sessionCache.get(sessionId) ?? [])];\n }\n\n /** Remove session events from the cache. */\n clearSession(sessionId: string): void {\n const events = this._sessionCache.get(sessionId);\n if (events) {\n this._sessionCacheEventCount = Math.max(\n 0,\n this._sessionCacheEventCount - events.length\n );\n this._sessionCache.delete(sessionId);\n }\n }\n\n /** Remove and return up to `count` events from the buffer. */\n drain(count: number): CanaryEvent[] {\n const n = Math.min(count, this._buffer.length);\n return this._buffer.splice(0, n);\n }\n\n /** Get all buffered events (without draining). For reporter access. */\n peek(): CanaryEvent[] {\n return [...this._buffer];\n }\n\n /** Number of buffered events. */\n get length(): number {\n return this._buffer.length;\n }\n\n /** Drain a batch and send to backend. */\n async flush(): Promise<void> {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) return;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n return;\n } catch {\n if (attempt < RETRY_ATTEMPTS - 1) {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n // All retries failed — events are lost (no disk persistence in Node SDK v1)\n }\n\n /** Stop flush timer and drain all remaining events. */\n async shutdown(): Promise<void> {\n this._stopped = true;\n if (this._timer) {\n clearInterval(this._timer);\n this._timer = null;\n }\n\n while (this._buffer.length > 0) {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) break;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n break;\n } catch {\n if (attempt === RETRY_ATTEMPTS - 1) {\n // Final failure — events lost\n } else {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n }\n }\n\n /** POST events to the backend with gzip compression. */\n private _send(events: CanaryEvent[]): Promise<void> {\n const payload = JSON.stringify({\n events,\n sdk_version: \"0.1.0\",\n });\n const compressed = gzipSync(Buffer.from(payload, \"utf-8\"));\n\n const url = new URL(`${this._endpoint}/v1/events`);\n const isHttps = url.protocol === \"https:\";\n const requestFn = isHttps ? this._originalHttpsRequest : this._originalHttpRequest;\n\n return new Promise<void>((resolve, reject) => {\n const req = requestFn(\n {\n hostname: url.hostname,\n port: url.port || (isHttps ? 443 : 80),\n path: url.pathname,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Encoding\": \"gzip\",\n Authorization: `Bearer ${this._apiKey}`,\n \"Content-Length\": compressed.length,\n },\n timeout: 5000,\n },\n (res) => {\n // Consume the response\n res.resume();\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve();\n } else {\n reject(new Error(`HTTP ${res.statusCode}`));\n }\n }\n );\n\n req.on(\"error\", reject);\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request timeout\"));\n });\n req.write(compressed);\n req.end();\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Patch http/https modules to intercept outgoing HTTP calls.\n *\n * Mirrors sdk/src/canary/interceptors/sync_interceptor.py.\n */\n\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport { performance } from \"node:perf_hooks\";\nimport type { EventBuffer } from \"../buffer.js\";\nimport { matchProvider } from \"../domains.js\";\nimport { normalizeEndpoint } from \"../normalize.js\";\nimport { extractResponseFields } from \"../extract.js\";\nimport { getSessionSignals } from \"../context.js\";\n\nconst MAX_BODY_CAPTURE = 4096;\n\n// ── Noise filtering ────────────────────────────────────────────────\n\nlet _sdkEndpointHost: string | null = null;\n\nexport function setSdkEndpointHost(host: string): void {\n _sdkEndpointHost = host;\n}\n\nconst EXCLUDED_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"0.0.0.0\", \"::1\"]);\n\nfunction isExcluded(hostname: string): boolean {\n if (EXCLUDED_HOSTS.has(hostname)) return true;\n if (hostname.endsWith(\".local\") || hostname.endsWith(\".internal\")) return true;\n if (hostname === _sdkEndpointHost) return true;\n return false;\n}\n\n// Save originals\nexport const originalHttpRequest = http.request;\nexport const originalHttpGet = http.get;\nexport const originalHttpsRequest = https.request;\nexport const originalHttpsGet = https.get;\n\nlet _buffer: EventBuffer | null = null;\n\nfunction wrapRequest(\n original: typeof http.request,\n defaultProtocol: string\n): typeof http.request {\n const request = original as unknown as {\n (\n url: string | URL,\n callback?: (res: http.IncomingMessage) => void\n ): http.ClientRequest;\n (\n url: string | URL,\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n ): http.ClientRequest;\n (\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n ): http.ClientRequest;\n };\n\n return function patchedRequest(\n this: unknown,\n ...args: Parameters<typeof http.request>\n ): http.ClientRequest {\n // Parse the overloaded arguments\n let url: string | URL | http.RequestOptions;\n let options: http.RequestOptions | undefined;\n let callback: ((res: http.IncomingMessage) => void) | undefined;\n\n if (typeof args[0] === \"string\" || args[0] instanceof URL) {\n url = args[0];\n if (typeof args[1] === \"function\") {\n callback = args[1] as (res: http.IncomingMessage) => void;\n } else {\n options = args[1] as http.RequestOptions;\n callback = args[2] as ((res: http.IncomingMessage) => void) | undefined;\n }\n } else {\n options = args[0] as http.RequestOptions;\n callback = args[1] as ((res: http.IncomingMessage) => void) | undefined;\n url = \"\";\n }\n\n // Extract hostname\n let hostname: string | undefined;\n let pathname = \"/\";\n let method = \"GET\";\n\n if (typeof url === \"string\" && url) {\n try {\n const parsed = new URL(url);\n hostname = parsed.hostname;\n pathname = parsed.pathname;\n } catch {\n hostname = undefined;\n }\n } else if (url instanceof URL) {\n hostname = url.hostname;\n pathname = url.pathname;\n }\n\n if (options) {\n hostname = hostname || (options.hostname as string) || (options.host as string);\n if (options.path) pathname = options.path;\n if (options.method) method = options.method;\n }\n\n // Remove port from hostname\n if (hostname && hostname.includes(\":\")) {\n hostname = hostname.split(\":\")[0];\n }\n\n // Skip excluded hosts (localhost, SDK endpoint, etc.)\n if (hostname && isExcluded(hostname)) {\n return original.apply(this, args);\n }\n\n // Identify provider\n const provider = hostname ? matchProvider(hostname) : null;\n if (!provider || !_buffer) {\n return original.apply(this, args);\n }\n\n const start = performance.now();\n const signals = getSessionSignals();\n\n // Wrap the callback to intercept the response\n const wrappedCallback = (res: http.IncomingMessage) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n res.on(\"data\", (chunk: Buffer) => {\n if (totalSize < MAX_BODY_CAPTURE) {\n chunks.push(chunk);\n totalSize += chunk.length;\n }\n });\n\n res.on(\"end\", () => {\n const latencyMs = performance.now() - start;\n const status = res.statusCode ?? 0;\n const body = Buffer.concat(chunks).toString(\"utf-8\").slice(0, MAX_BODY_CAPTURE);\n\n const event: Record<string, unknown> = {\n event_type: \"http\",\n provider,\n endpoint_pattern: normalizeEndpoint(pathname),\n hostname,\n method: method.toUpperCase(),\n status,\n latency_ms: Math.round(latencyMs * 100) / 100,\n ts: Date.now() / 1000,\n ...signals,\n };\n\n if (status >= 400) {\n event.error_body = body;\n } else if (status >= 200 && status < 300) {\n const fields = extractResponseFields(body);\n if (fields) {\n event.response_fields = fields;\n }\n }\n\n _buffer!.push(event as any);\n });\n\n if (callback) callback(res);\n };\n\n // Call the original with wrapped callback\n if (typeof args[0] === \"string\" || args[0] instanceof URL) {\n if (options) {\n return request(args[0], options, wrappedCallback);\n } else {\n return request(args[0], wrappedCallback);\n }\n } else {\n return request(options ?? {}, wrappedCallback);\n }\n } as typeof http.request;\n}\n\n/** Patch http and https modules to intercept tracked API calls. */\nexport function patchHttp(buffer: EventBuffer): void {\n _buffer = buffer;\n http.request = wrapRequest(originalHttpRequest, \"http:\");\n http.get = wrapRequest(originalHttpGet, \"http:\") as typeof http.get;\n https.request = wrapRequest(originalHttpsRequest, \"https:\");\n https.get = wrapRequest(originalHttpsGet, \"https:\") as typeof https.get;\n}\n\n/** Restore original http/https methods. */\nexport function unpatchHttp(): void {\n _buffer = null;\n http.request = originalHttpRequest;\n http.get = originalHttpGet;\n https.request = originalHttpsRequest;\n https.get = originalHttpsGet;\n}\n","/**\n * Domain-to-provider mapping for HTTP call interception.\n *\n * Mirrors sdk/src/canary/domains.py exactly.\n */\n\nexport const COMMON_API_DOMAINS: Record<string, string> = {\n // Payments\n \"api.stripe.com\": \"stripe\",\n \"hooks.stripe.com\": \"stripe\",\n \"api.squareup.com\": \"square\",\n \"api.paypal.com\": \"paypal\",\n \"api.braintreegateway.com\": \"braintree\",\n // AI / ML\n \"api.openai.com\": \"openai\",\n \"api.anthropic.com\": \"anthropic\",\n \"api.cohere.ai\": \"cohere\",\n \"generativelanguage.googleapis.com\": \"google-ai\",\n \"api.replicate.com\": \"replicate\",\n \"api-inference.huggingface.co\": \"huggingface\",\n \"api.mistral.ai\": \"mistral\",\n // Communication\n \"api.twilio.com\": \"twilio\",\n \"api.sendgrid.com\": \"sendgrid\",\n \"api.mailgun.net\": \"mailgun\",\n \"api.postmarkapp.com\": \"postmark\",\n \"api.resend.com\": \"resend\",\n // Developer tools\n \"api.github.com\": \"github\",\n \"gitlab.com\": \"gitlab\",\n \"api.slack.com\": \"slack\",\n \"discord.com\": \"discord\",\n \"api.linear.app\": \"linear\",\n // Cloud / Infrastructure\n \"api.cloudflare.com\": \"cloudflare\",\n \"api.vercel.com\": \"vercel\",\n \"api.heroku.com\": \"heroku\",\n \"api.netlify.com\": \"netlify\",\n // Data / Analytics\n \"api.segment.io\": \"segment\",\n \"api.mixpanel.com\": \"mixpanel\",\n \"api.amplitude.com\": \"amplitude\",\n // Productivity / SaaS\n \"api.notion.so\": \"notion\",\n \"api.airtable.com\": \"airtable\",\n \"api.hubspot.com\": \"hubspot\",\n \"api.salesforce.com\": \"salesforce\",\n // Social\n \"graph.facebook.com\": \"facebook\",\n \"api.twitter.com\": \"twitter\",\n \"api.x.com\": \"twitter\",\n // Finance\n \"api.plaid.com\": \"plaid\",\n \"sandbox.plaid.com\": \"plaid\",\n // Commerce\n \"api.shopify.com\": \"shopify\",\n // Maps / Location\n \"maps.googleapis.com\": \"google-maps\",\n // Auth\n \"api.auth0.com\": \"auth0\",\n // Search\n \"api.algolia.com\": \"algolia\",\n // Storage\n \"api.supabase.co\": \"supabase\",\n \"api.firebase.google.com\": \"firebase\",\n};\n\n// Build reverse lookup from registrable domain -> provider\nconst DOMAIN_SUFFIXES: Record<string, string> = {};\nfor (const [domain, provider] of Object.entries(COMMON_API_DOMAINS)) {\n const parts = domain.split(\".\");\n if (parts.length >= 2) {\n const registrable = parts.slice(-2).join(\".\");\n DOMAIN_SUFFIXES[registrable] = provider;\n }\n}\n\n/**\n * Return the provider name for a given host, or null if unknown.\n *\n * Checks exact match first, then falls back to registrable domain suffix.\n */\nexport function matchProvider(host: string): string | null {\n host = host.toLowerCase().trim();\n\n // Exact match\n if (host in COMMON_API_DOMAINS) {\n return COMMON_API_DOMAINS[host];\n }\n\n // Subdomain match\n const parts = host.split(\".\");\n if (parts.length >= 2) {\n const registrable = parts.slice(-2).join(\".\");\n if (registrable in DOMAIN_SUFFIXES) {\n return DOMAIN_SUFFIXES[registrable];\n }\n }\n\n return null;\n}\n\n/**\n * Derive provider name from hostname by extracting the main domain.\n * \"api.brave.com\" → \"brave\", \"api.search.brave.com\" → \"brave\"\n */\nfunction deriveFromHostname(hostname: string): string {\n const parts = hostname.split(\".\");\n if (parts.length >= 2) {\n // Take the second-to-last part (main domain name)\n return parts[parts.length - 2];\n }\n return hostname;\n}\n\n/**\n * Three-tier provider resolution:\n * 1. Exact/suffix match from domain map\n * 2. Derive from hostname (extract main domain name)\n * 3. Fallback to full hostname\n */\nexport function resolveProvider(hostname: string | undefined): string {\n if (!hostname) return \"unknown\";\n const matched = matchProvider(hostname);\n if (matched) return matched;\n return deriveFromHostname(hostname.toLowerCase().trim());\n}\n","/**\n * Normalize URL paths by replacing dynamic segments with {id} placeholders.\n *\n * Mirrors sdk/src/canary/normalize.py exactly.\n */\n\n// UUID pattern (8-4-4-4-12 hex)\nconst UUID_RE =\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g;\n\n// Stripe-style IDs: short prefix (2-4 lowercase) + underscore + alphanumeric with digit\nconst STRIPE_ID_RE = /^[a-z]{2,4}_[A-Za-z0-9]*\\d[A-Za-z0-9]*$/;\n\n// Purely numeric segments (at least 2 digits)\nconst NUMERIC_RE = /^\\d{2,}$/;\n\n/**\n * Normalize a URL path, replacing dynamic segments with {id}.\n *\n * - Strips query strings and fragments\n * - Replaces UUIDs, Stripe-style IDs, and numeric IDs with {id}\n * - Preserves version prefixes like /v1/, /v2/\n */\nexport function normalizeEndpoint(path: string): string {\n // Strip query string and fragment\n path = path.split(\"?\")[0].split(\"#\")[0];\n\n // Replace UUIDs inline first\n path = path.replace(UUID_RE, \"{id}\");\n\n // Split into segments and process each\n const parts = path.split(\"/\");\n const normalized: string[] = [];\n\n for (const part of parts) {\n if (!part) {\n normalized.push(part);\n } else if (part === \"{id}\") {\n normalized.push(part);\n } else if (STRIPE_ID_RE.test(part)) {\n normalized.push(\"{id}\");\n } else if (NUMERIC_RE.test(part)) {\n normalized.push(\"{id}\");\n } else {\n normalized.push(part);\n }\n }\n\n return normalized.join(\"/\");\n}\n","/**\n * Extract allowlisted fields from HTTP response bodies.\n *\n * Mirrors sdk/src/canary/extract.py exactly.\n */\n\nconst COMMON_RESPONSE_KEYS = new Set([\n \"model\",\n \"status\",\n \"object\",\n \"type\",\n \"finish_reason\",\n \"stop_reason\",\n \"error_code\",\n \"error_message\",\n \"usage\",\n \"created\",\n \"currency\",\n \"livemode\",\n]);\n\nconst USAGE_KEYS = new Set([\n \"prompt_tokens\",\n \"completion_tokens\",\n \"total_tokens\",\n \"input_tokens\",\n \"output_tokens\",\n]);\n\nconst MAX_BODY_SIZE = 4096;\n\n/**\n * Extract allowlisted fields from a response body string.\n *\n * Returns a record of extracted fields, or undefined if body is not JSON\n * or no matching keys are found.\n */\nexport function extractResponseFields(\n body: string\n): Record<string, unknown> | undefined {\n if (body.length > MAX_BODY_SIZE) {\n body = body.slice(0, MAX_BODY_SIZE);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(body);\n } catch {\n return undefined;\n }\n\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n return undefined;\n }\n\n const obj = data as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n\n for (const key of COMMON_RESPONSE_KEYS) {\n if (key in obj) {\n const val = obj[key];\n if (key === \"usage\" && typeof val === \"object\" && val !== null && !Array.isArray(val)) {\n const usageObj = val as Record<string, unknown>;\n const usage: Record<string, unknown> = {};\n for (const uk of USAGE_KEYS) {\n if (uk in usageObj) {\n usage[uk] = usageObj[uk];\n }\n }\n if (Object.keys(usage).length > 0) {\n result[\"usage\"] = usage;\n }\n } else {\n result[key] = val;\n }\n }\n }\n\n // Extract finish_reason from choices[0] (OpenAI style)\n if (!(\"finish_reason\" in result) && \"choices\" in obj) {\n const choices = obj[\"choices\"];\n if (Array.isArray(choices) && choices.length > 0) {\n const choice = choices[0];\n if (typeof choice === \"object\" && choice !== null && \"finish_reason\" in choice) {\n result[\"finish_reason\"] = (choice as Record<string, unknown>)[\"finish_reason\"];\n }\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n","/**\n * Session correlation via AsyncLocalStorage.\n *\n * Node.js equivalent of sdk/src/canary/context.py using\n * AsyncLocalStorage instead of ContextVar.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomUUID } from \"node:crypto\";\nimport { detectAgent } from \"./agent-detect.js\";\n\ninterface SessionContext {\n frameworkSessionId: string;\n}\n\nconst sessionStorage = new AsyncLocalStorage<SessionContext>();\nconst sdkInstanceId = randomUUID().replace(/-/g, \"\");\nlet callSequence = 0;\nlet defaultFrameworkSessionId = \"\";\n\nexport interface SessionSignals {\n sdk_instance_id: string;\n process_id: number;\n thread_id: number;\n call_sequence: number;\n framework_session_id?: string;\n agent_name?: string;\n agent_version?: string;\n}\n\n/**\n * Return session correlation signals for the current execution context.\n */\nexport function getSessionSignals(): SessionSignals {\n callSequence++;\n\n const store = sessionStorage.getStore();\n const signals: SessionSignals = {\n sdk_instance_id: sdkInstanceId,\n process_id: process.pid,\n thread_id: 0,\n call_sequence: callSequence,\n };\n\n const frameworkSessionId =\n store?.frameworkSessionId || defaultFrameworkSessionId;\n if (frameworkSessionId) {\n signals.framework_session_id = frameworkSessionId;\n }\n\n const agentInfo = detectAgent();\n if (agentInfo.agent_name) {\n signals.agent_name = agentInfo.agent_name;\n }\n if (agentInfo.agent_version) {\n signals.agent_version = agentInfo.agent_version;\n }\n\n return signals;\n}\n\n/**\n * Set the framework session ID for the current async context.\n *\n * If no async context is active, this becomes the default session ID used\n * by subsequent intercepted calls on this process.\n */\nexport function setFrameworkSession(sessionId: string): void {\n const store = sessionStorage.getStore();\n if (store) {\n store.frameworkSessionId = sessionId;\n return;\n }\n\n defaultFrameworkSessionId = sessionId;\n}\n\n/**\n * Clear the framework session ID for the current async context.\n */\nexport function clearFrameworkSession(): void {\n const store = sessionStorage.getStore();\n if (store) {\n store.frameworkSessionId = \"\";\n return;\n }\n\n defaultFrameworkSessionId = \"\";\n}\n\n/**\n * Run a function with a scoped framework session ID.\n *\n * The session ID propagates through async call chains automatically.\n */\nexport function runWithSession<T>(sessionId: string, fn: () => T): T {\n return sessionStorage.run({ frameworkSessionId: sessionId }, fn);\n}\n\n/**\n * Reset all context state. For testing only.\n */\nexport function resetContext(): void {\n callSequence = 0;\n defaultFrameworkSessionId = \"\";\n}\n","/**\n * Detect which AI agent framework is running via environment variables.\n */\n\nexport interface AgentInfo {\n agent_name: string | null;\n agent_version: string | null;\n}\n\nexport const AGENT_ENV_MAP: ReadonlyArray<{ envKey: string; name: string; versionKey?: string; useValue?: boolean }> = [\n { envKey: \"CANARY_AGENT_NAME\", name: \"\", useValue: true },\n { envKey: \"CLAUDE_CODE\", name: \"claude_code\" },\n { envKey: \"CURSOR_TRACE_ID\", name: \"cursor\" },\n { envKey: \"OPENCLAW_SESSION_ID\", name: \"openclaw\", versionKey: \"OPENCLAW_VERSION\" },\n { envKey: \"WINDSURF_SESSION_ID\", name: \"windsurf\" },\n { envKey: \"CLINE_TASK_ID\", name: \"cline\" },\n { envKey: \"AIDER_VERSION\", name: \"aider\", versionKey: \"AIDER_VERSION\" },\n { envKey: \"DEVIN_SESSION_ID\", name: \"devin\" },\n { envKey: \"CODEX_CLI\", name: \"codex\" },\n { envKey: \"GITHUB_COPILOT\", name: \"github_copilot\" },\n { envKey: \"CODY_SESSION_ID\", name: \"sourcegraph_cody\" },\n];\n\nlet cachedInfo: AgentInfo | null = null;\n\nexport function detectAgent(): AgentInfo {\n if (cachedInfo) return cachedInfo;\n\n for (const entry of AGENT_ENV_MAP) {\n const val = process.env[entry.envKey];\n // Check !== undefined so empty strings (presence flags) still detect\n if (val !== undefined) {\n cachedInfo = {\n agent_name: entry.useValue ? (val || null) : entry.name,\n agent_version: entry.versionKey ? (process.env[entry.versionKey] ?? null) : null,\n };\n if (cachedInfo.agent_name) return cachedInfo;\n }\n }\n\n cachedInfo = { agent_name: null, agent_version: null };\n return cachedInfo;\n}\n\nexport function resetAgentCache(): void {\n cachedInfo = null;\n}\n","/**\n * Patch globalThis.fetch to intercept outgoing HTTP calls (Node 18+).\n *\n * Mirrors the fetch interception logic from the Python SDK's async interceptor.\n */\n\nimport { performance } from \"node:perf_hooks\";\nimport type { EventBuffer } from \"../buffer.js\";\nimport { matchProvider } from \"../domains.js\";\nimport { normalizeEndpoint } from \"../normalize.js\";\nimport { extractResponseFields } from \"../extract.js\";\nimport { getSessionSignals } from \"../context.js\";\n\nconst MAX_BODY_CAPTURE = 4096;\nconst CLONE_READ_TIMEOUT_MS = 5000;\n\n// ── Noise filtering ────────────────────────────────────────────────\n\nlet _sdkEndpointHost: string | null = null;\n\nexport function setSdkEndpointHost(host: string): void {\n _sdkEndpointHost = host;\n}\n\nconst EXCLUDED_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"0.0.0.0\", \"::1\"]);\n\nfunction isExcluded(hostname: string): boolean {\n if (EXCLUDED_HOSTS.has(hostname)) return true;\n if (hostname.endsWith(\".local\") || hostname.endsWith(\".internal\")) return true;\n if (hostname === _sdkEndpointHost) return true;\n return false;\n}\n\nlet _originalFetch: typeof globalThis.fetch | null = null;\nlet _buffer: EventBuffer | null = null;\n\ntype FetchInput = Parameters<typeof globalThis.fetch>[0];\ntype FetchInit = Parameters<typeof globalThis.fetch>[1];\n\n/** Patch globalThis.fetch to intercept tracked API calls. */\nexport function patchFetch(buffer: EventBuffer): void {\n if (typeof globalThis.fetch === \"undefined\") return; // Node < 18\n\n _originalFetch = globalThis.fetch;\n _buffer = buffer;\n\n globalThis.fetch = async function patchedFetch(\n input: FetchInput,\n init?: FetchInit\n ): Promise<Response> {\n // Parse URL\n let url: URL;\n let method = \"GET\";\n\n try {\n if (typeof input === \"string\") {\n url = new URL(input);\n } else if (input instanceof URL) {\n url = input;\n } else if (typeof Request !== \"undefined\" && input instanceof Request) {\n url = new URL(input.url);\n method = input.method;\n } else {\n return _originalFetch!(input, init);\n }\n } catch {\n return _originalFetch!(input, init);\n }\n\n if (init?.method) method = init.method;\n\n // Skip excluded hosts (localhost, SDK endpoint, etc.)\n if (isExcluded(url.hostname)) {\n return _originalFetch!(input, init);\n }\n\n const provider = matchProvider(url.hostname);\n if (!provider || !_buffer) {\n return _originalFetch!(input, init);\n }\n\n const start = performance.now();\n const signals = getSessionSignals();\n\n const response = await _originalFetch!(input, init);\n const latencyMs = performance.now() - start;\n const status = response.status;\n\n // Clone response to read body without consuming caller's stream\n let body = \"\";\n try {\n const clone = response.clone();\n const text = await Promise.race([\n clone.text(),\n new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error(\"timeout\")), CLONE_READ_TIMEOUT_MS)\n ),\n ]);\n body = text.slice(0, MAX_BODY_CAPTURE);\n } catch {\n // Couldn't read body — continue without it\n }\n\n const event: Record<string, unknown> = {\n event_type: \"http\",\n provider,\n endpoint_pattern: normalizeEndpoint(url.pathname),\n hostname: url.hostname,\n method: method.toUpperCase(),\n status,\n latency_ms: Math.round(latencyMs * 100) / 100,\n ts: Date.now() / 1000,\n ...signals,\n };\n\n if (status >= 400) {\n event.error_body = body;\n } else if (status >= 200 && status < 300 && body) {\n const fields = extractResponseFields(body);\n if (fields) {\n event.response_fields = fields;\n }\n }\n\n _buffer.push(event as any);\n return response;\n };\n}\n\n/** Restore original globalThis.fetch. */\nexport function unpatchFetch(): void {\n if (_originalFetch) {\n globalThis.fetch = _originalFetch;\n _originalFetch = null;\n }\n _buffer = null;\n}\n","/**\n * Auto-generate feedback events from session data at shutdown.\n *\n * Mirrors sdk/src/canary/reporter.py exactly.\n */\n\nimport type { CanaryEvent, FeedbackEvent } from \"./types.js\";\n\nconst MAX_FRICTION_POINTS = 10;\nconst MAX_FRICTION_LENGTH = 200;\n\nexport class SessionReporter {\n /**\n * Analyze events and return feedback events for each session.\n */\n generateFeedback(events: CanaryEvent[]): FeedbackEvent[] {\n // Group events by session\n const sessions = new Map<string | undefined, CanaryEvent[]>();\n\n for (const event of events) {\n if (event.event_type === \"feedback\") continue; // skip existing feedback\n const sid = (event as unknown as Record<string, unknown>).framework_session_id as\n | string\n | undefined;\n let list = sessions.get(sid);\n if (!list) {\n list = [];\n sessions.set(sid, list);\n }\n list.push(event);\n }\n\n const feedbackEvents: FeedbackEvent[] = [];\n for (const [sessionId, sessionEvents] of sessions) {\n const feedback = this._analyzeSession(sessionId, sessionEvents);\n if (feedback) feedbackEvents.push(feedback);\n }\n\n return feedbackEvents;\n }\n\n private _analyzeSession(\n sessionId: string | undefined,\n events: CanaryEvent[]\n ): FeedbackEvent | null {\n const httpEvents = events.filter((e) => e.event_type === \"http\") as Array<\n CanaryEvent & { event_type: \"http\" }\n >;\n const actionEvents = events.filter(\n (e) => e.event_type === \"action\"\n ) as Array<CanaryEvent & { event_type: \"action\" }>;\n\n if (httpEvents.length === 0 && actionEvents.length === 0) return null;\n\n const errorEvents = httpEvents.filter(\n (e) => typeof e.status === \"number\" && e.status >= 400\n );\n const totalCalls = httpEvents.length;\n const errorCount = errorEvents.length;\n const errorRate = totalCalls > 0 ? errorCount / totalCalls : 0;\n const worked = errorRate < 0.5;\n\n const frictionPoints = this._extractFrictionPoints(errorEvents);\n const context = this._buildContext(httpEvents, actionEvents, errorCount);\n\n const errorProviders = [\n ...new Set(\n errorEvents\n .map((e) => e.provider)\n .filter((p): p is string => typeof p === \"string\")\n ),\n ].sort();\n\n const feedback: FeedbackEvent = {\n event_type: \"feedback\",\n source: \"auto_report\",\n worked,\n context,\n ts: Date.now() / 1000,\n };\n\n if (frictionPoints.length > 0) {\n feedback.friction_points = frictionPoints;\n }\n if (sessionId) {\n feedback.framework_session_id = sessionId;\n }\n if (errorProviders.length > 0) {\n feedback.provider = errorProviders[0];\n }\n\n return feedback;\n }\n\n private _extractFrictionPoints(\n errorEvents: Array<CanaryEvent & { event_type: \"http\" }>\n ): string[] {\n const frictionPoints: string[] = [];\n const seen = new Set<string>();\n\n for (const event of errorEvents) {\n const provider = event.provider ?? \"unknown\";\n const endpoint = event.endpoint_pattern ?? \"\";\n const errorBody = (event as unknown as Record<string, unknown>).error_body as string ?? \"\";\n const status = event.status ?? 0;\n\n let point: string;\n if (!errorBody) {\n point = `${provider} ${endpoint}: HTTP ${status}`;\n } else {\n const message = this._extractErrorMessage(errorBody);\n point = `${provider} ${endpoint}: ${message}`;\n }\n\n point = point.slice(0, MAX_FRICTION_LENGTH);\n if (!seen.has(point)) {\n seen.add(point);\n frictionPoints.push(point);\n }\n\n if (frictionPoints.length >= MAX_FRICTION_POINTS) break;\n }\n\n return frictionPoints;\n }\n\n private _extractErrorMessage(errorBody: string): string {\n try {\n const data = JSON.parse(errorBody);\n if (typeof data === \"object\" && data !== null) {\n for (const key of [\"message\", \"error\", \"detail\", \"error_message\"]) {\n const val = data[key];\n if (typeof val === \"string\") return val;\n if (typeof val === \"object\" && val !== null) {\n const msg = val.message ?? val.type;\n if (msg) return String(msg);\n }\n }\n }\n } catch {\n // Not JSON\n }\n\n const firstLine = errorBody.trim().split(\"\\n\")[0];\n return firstLine.slice(0, MAX_FRICTION_LENGTH);\n }\n\n private _buildContext(\n httpEvents: Array<CanaryEvent & { event_type: \"http\" }>,\n actionEvents: Array<CanaryEvent & { event_type: \"action\" }>,\n errorCount: number\n ): string {\n const parts: string[] = [];\n\n const providers = [\n ...new Set(\n httpEvents\n .map((e) => e.provider)\n .filter((p): p is string => typeof p === \"string\")\n ),\n ].sort();\n if (providers.length > 0) {\n parts.push(`APIs: ${providers.join(\", \")}`);\n }\n\n const pages = actionEvents\n .filter((e) => e.action_type === \"page_navigate\" && e.action_args)\n .map((e) => (e.action_args as unknown as Record<string, unknown>)?.url)\n .filter((u): u is string => typeof u === \"string\" && u.length > 0);\n if (pages.length > 0) {\n parts.push(`${pages.length} pages visited`);\n }\n\n if (httpEvents.length > 0) {\n parts.push(`${httpEvents.length} HTTP calls, ${errorCount} errors`);\n }\n\n if (actionEvents.length > 0) {\n parts.push(`${actionEvents.length} browser actions`);\n }\n\n return parts.length > 0 ? parts.join(\"; \") : \"Session completed\";\n }\n}\n","/**\n * MCP adapter — wraps tool handlers for observability.\n *\n * Mirrors sdk/src/canary/adapters/mcp.py.\n */\n\nimport { performance } from \"node:perf_hooks\";\nimport type { EventBuffer } from \"../buffer.js\";\nimport type { ActionEvent } from \"../types.js\";\n\nconst MAX_RESULT_SIZE = 4096;\n\nexport class CanaryMCPMiddleware {\n private _buffer: EventBuffer;\n private _sessionId: string | undefined;\n\n constructor(buffer: EventBuffer, sessionId?: string) {\n this._buffer = buffer;\n this._sessionId = sessionId;\n }\n\n /**\n * Wrap a tool handler to capture execution time, arguments, and results.\n */\n wrap<T extends (...args: any[]) => any>(\n handler: T,\n toolName: string\n ): T {\n const self = this;\n\n const wrapped = async function (\n this: unknown,\n ...args: Parameters<T>\n ): Promise<ReturnType<T>> {\n const start = performance.now();\n let result: ReturnType<T>;\n let error: string | undefined;\n\n try {\n result = await handler.apply(this, args);\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n self._pushEvent(toolName, args, undefined, error, start);\n throw err;\n }\n\n self._pushEvent(toolName, args, result, undefined, start);\n return result;\n } as unknown as T;\n\n return wrapped;\n }\n\n /**\n * Manually record a tool call event.\n */\n onToolCall(\n toolName: string,\n args: Record<string, unknown>,\n result?: unknown,\n error?: string,\n latencyMs?: number\n ): void {\n const event: ActionEvent = {\n event_type: \"action\",\n source: \"mcp\",\n action_type: toolName,\n action_args: args,\n latency_ms: latencyMs ?? 0,\n ts: Date.now() / 1000,\n };\n\n if (error) {\n event.action_result = { error };\n event.status = 500;\n } else if (result !== undefined) {\n const serialized = JSON.stringify(result);\n event.action_result = {\n value:\n serialized.length > MAX_RESULT_SIZE\n ? serialized.slice(0, MAX_RESULT_SIZE)\n : serialized,\n };\n }\n\n if (this._sessionId) {\n event.framework_session_id = this._sessionId;\n }\n\n this._buffer.push(event);\n }\n\n private _pushEvent(\n toolName: string,\n args: unknown[],\n result: unknown,\n error: string | undefined,\n startTime: number\n ): void {\n const latencyMs = performance.now() - startTime;\n\n // Try to convert args array to object\n let argsObj: Record<string, unknown> = {};\n if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n argsObj = args[0] as Record<string, unknown>;\n } else if (args.length > 0) {\n argsObj = { args };\n }\n\n this.onToolCall(toolName, argsObj, result, error, latencyMs);\n }\n}\n","/**\n * canary-agent — API observability for Node.js AI agents.\n *\n * Mirrors sdk/src/canary/__init__.py.\n */\n\nimport { EventBuffer } from \"./buffer.js\";\nimport {\n patchHttp,\n unpatchHttp,\n patchFetch,\n unpatchFetch,\n originalHttpRequest,\n originalHttpsRequest,\n setHttpSdkEndpointHost,\n setFetchSdkEndpointHost,\n} from \"./interceptors/index.js\";\nimport { SessionReporter } from \"./reporter.js\";\nimport {\n runWithSession,\n setFrameworkSession,\n clearFrameworkSession,\n getSessionSignals,\n} from \"./context.js\";\nimport type { CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nimport { detectAgent } from \"./agent-detect.js\";\n\nlet _initialized = false;\nlet _buffer: EventBuffer | null = null;\nlet _autoReport = true;\n\n/**\n * Initialize Canary SDK. Patches http/https and fetch to intercept API calls.\n */\nexport function init(config: CanaryConfig): void {\n if (_initialized) return;\n\n if (!config.apiKey.startsWith(\"cnry_sk_\")) {\n throw new Error(\n \"Invalid API key: must start with 'cnry_sk_'. \" +\n \"Get your key at https://app.canary.dev\"\n );\n }\n\n const endpoint = config.endpoint ?? \"http://localhost:8000\";\n _autoReport = config.autoReport ?? true;\n const autoFlush = config.autoFlush ?? true;\n\n _buffer = new EventBuffer(\n config.apiKey,\n endpoint,\n autoFlush,\n originalHttpsRequest,\n originalHttpRequest\n );\n\n // Tell interceptors to skip the SDK's own endpoint\n try {\n const endpointUrl = new URL(endpoint);\n setHttpSdkEndpointHost(endpointUrl.hostname);\n setFetchSdkEndpointHost(endpointUrl.hostname);\n } catch {\n // Invalid endpoint URL — skip exclusion setup\n }\n\n patchHttp(_buffer);\n patchFetch(_buffer);\n _initialized = true;\n}\n\n/**\n * Shut down the SDK: unpatch interceptors, generate auto-report, flush events.\n */\nexport async function shutdown(): Promise<void> {\n if (!_initialized || !_buffer) return;\n\n unpatchHttp();\n unpatchFetch();\n\n if (_autoReport) {\n const reporter = new SessionReporter();\n const allEvents = _buffer.peek();\n const feedbackEvents = reporter.generateFeedback(allEvents);\n for (const event of feedbackEvents) {\n _buffer.push(event);\n }\n }\n\n await _buffer.shutdown();\n _buffer = null;\n _initialized = false;\n}\n\n/**\n * Submit manual feedback.\n */\nexport function survey(options: {\n worked?: boolean;\n context?: string;\n frictionPoints?: string[];\n provider?: string;\n sessionId?: string;\n}): void {\n if (!_buffer) {\n throw new Error(\"Canary SDK not initialized. Call init() first.\");\n }\n\n const agentInfo = detectAgent();\n const event: FeedbackEvent = {\n event_type: \"feedback\",\n source: \"manual\",\n worked: options.worked ?? true,\n context: options.context ?? \"\",\n ts: Date.now() / 1000,\n agent_name: agentInfo.agent_name ?? undefined,\n agent_version: agentInfo.agent_version ?? undefined,\n };\n\n if (options.frictionPoints && options.frictionPoints.length > 0) {\n event.friction_points = options.frictionPoints;\n }\n if (options.provider) {\n event.provider = options.provider;\n }\n if (options.sessionId) {\n event.framework_session_id = options.sessionId;\n }\n\n _buffer.push(event);\n}\n\n/**\n * Get the global EventBuffer (for advanced usage / adapters).\n */\nexport function getBuffer(): EventBuffer | null {\n return _buffer;\n}\n\n// Re-export session helpers\nexport { runWithSession, setFrameworkSession, clearFrameworkSession, getSessionSignals };\n\n// Re-export types\nexport type { CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nexport type { HttpEvent, ActionEvent } from \"./types.js\";\nexport { EventBuffer } from \"./buffer.js\";\nexport { SessionReporter } from \"./reporter.js\";\nexport { CanaryMCPMiddleware } from \"./adapters/mcp.js\";\nexport { matchProvider, resolveProvider, COMMON_API_DOMAINS } from \"./domains.js\";\nexport { normalizeEndpoint } from \"./normalize.js\";\nexport { extractResponseFields } from \"./extract.js\";\nexport { detectAgent, resetAgentCache } from \"./agent-detect.js\";\n"],"mappings":"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","names":["originalHttpsRequest","originalHttpRequest","http","https","http","https","performance","MAX_BODY_CAPTURE","_sdkEndpointHost","setSdkEndpointHost","EXCLUDED_HOSTS","isExcluded","_buffer","init","performance","performance","_buffer","setSdkEndpointHost"]}
|
|
1
|
+
{"version":3,"sources":["../src/buffer.ts","../src/interceptors/http.ts","../src/domains.ts","../src/normalize.ts","../src/extract.ts","../src/context.ts","../src/agent-detect.ts","../src/interceptors/fetch.ts","../src/reporter.ts","../src/adapters/mcp.ts","../src/index.ts"],"sourcesContent":["/**\n * Event buffer with ring buffer, gzip flush, retry, and session cache.\n *\n * Mirrors sdk/src/canary/buffer.py.\n */\n\nimport { gzipSync } from \"node:zlib\";\nimport https from \"node:https\";\nimport http from \"node:http\";\nimport type { CanaryEvent } from \"./types.js\";\n\nconst MAX_LEN = 10_000;\nconst BATCH_SIZE = 100;\nconst FLUSH_INTERVAL_MS = 10_000;\nconst MAX_EVENTS_PER_SESSION = 500;\nconst MAX_SESSION_CACHE_EVENTS = 5_000;\nconst RETRY_ATTEMPTS = 3;\nconst RETRY_BACKOFF_MS = [1000, 2000, 4000];\n\nexport class EventBuffer {\n private _buffer: CanaryEvent[] = [];\n private _sessionCache: Map<string, CanaryEvent[]> = new Map();\n private _sessionCacheEventCount = 0;\n private _apiKey: string;\n private _endpoint: string;\n private _timer: ReturnType<typeof setInterval> | null = null;\n private _stopped = false;\n /** Reference to the original (unpatched) https.request for anti-recursion. */\n private _originalHttpsRequest: typeof https.request;\n /** Reference to the original (unpatched) http.request for anti-recursion. */\n private _originalHttpRequest: typeof http.request;\n\n constructor(\n apiKey: string,\n endpoint = \"http://localhost:8000\",\n autoFlush = true,\n originalHttpsRequest?: typeof https.request,\n originalHttpRequest?: typeof http.request\n ) {\n this._apiKey = apiKey;\n this._endpoint = endpoint.replace(/\\/+$/, \"\");\n this._originalHttpsRequest = originalHttpsRequest ?? https.request;\n this._originalHttpRequest = originalHttpRequest ?? http.request;\n\n if (autoFlush) {\n this._timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);\n this._timer.unref();\n }\n }\n\n /** Add an event to the ring buffer. */\n push(event: CanaryEvent): void {\n if (this._buffer.length >= MAX_LEN) {\n this._buffer.shift(); // drop oldest\n }\n this._buffer.push(event);\n\n // Retain in session cache\n const sessionId = (event as unknown as Record<string, unknown>).framework_session_id;\n if (typeof sessionId === \"string\" && sessionId) {\n if (this._sessionCacheEventCount < MAX_SESSION_CACHE_EVENTS) {\n let sessionEvents = this._sessionCache.get(sessionId);\n if (!sessionEvents) {\n sessionEvents = [];\n this._sessionCache.set(sessionId, sessionEvents);\n }\n if (sessionEvents.length < MAX_EVENTS_PER_SESSION) {\n sessionEvents.push(event);\n this._sessionCacheEventCount++;\n }\n }\n }\n }\n\n /** Return a copy of events for the given session from the cache. */\n getSessionEvents(sessionId: string): CanaryEvent[] {\n return [...(this._sessionCache.get(sessionId) ?? [])];\n }\n\n /** Remove session events from the cache. */\n clearSession(sessionId: string): void {\n const events = this._sessionCache.get(sessionId);\n if (events) {\n this._sessionCacheEventCount = Math.max(\n 0,\n this._sessionCacheEventCount - events.length\n );\n this._sessionCache.delete(sessionId);\n }\n }\n\n /** Remove and return up to `count` events from the buffer. */\n drain(count: number): CanaryEvent[] {\n const n = Math.min(count, this._buffer.length);\n return this._buffer.splice(0, n);\n }\n\n /** Get all buffered events (without draining). For reporter access. */\n peek(): CanaryEvent[] {\n return [...this._buffer];\n }\n\n /** Number of buffered events. */\n get length(): number {\n return this._buffer.length;\n }\n\n /** Drain a batch and send to backend. */\n async flush(): Promise<void> {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) return;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n return;\n } catch {\n if (attempt < RETRY_ATTEMPTS - 1) {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n // All retries failed — events are lost (no disk persistence in Node SDK v1)\n }\n\n /** Stop flush timer and drain all remaining events. */\n async shutdown(): Promise<void> {\n this._stopped = true;\n if (this._timer) {\n clearInterval(this._timer);\n this._timer = null;\n }\n\n while (this._buffer.length > 0) {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) break;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n break;\n } catch {\n if (attempt === RETRY_ATTEMPTS - 1) {\n // Final failure — events lost\n } else {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n }\n }\n\n /** POST events to the backend with gzip compression. */\n private _send(events: CanaryEvent[]): Promise<void> {\n const payload = JSON.stringify({\n events,\n sdk_version: \"0.1.0\",\n });\n const compressed = gzipSync(Buffer.from(payload, \"utf-8\"));\n\n const url = new URL(`${this._endpoint}/v1/events`);\n const isHttps = url.protocol === \"https:\";\n const requestFn = isHttps ? this._originalHttpsRequest : this._originalHttpRequest;\n\n return new Promise<void>((resolve, reject) => {\n const req = requestFn(\n {\n hostname: url.hostname,\n port: url.port || (isHttps ? 443 : 80),\n path: url.pathname,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Encoding\": \"gzip\",\n Authorization: `Bearer ${this._apiKey}`,\n \"Content-Length\": compressed.length,\n },\n timeout: 5000,\n },\n (res) => {\n // Consume the response\n res.resume();\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve();\n } else {\n reject(new Error(`HTTP ${res.statusCode}`));\n }\n }\n );\n\n req.on(\"error\", reject);\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request timeout\"));\n });\n req.write(compressed);\n req.end();\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Patch http/https modules to intercept outgoing HTTP calls.\n *\n * Mirrors sdk/src/canary/interceptors/sync_interceptor.py.\n */\n\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport { performance } from \"node:perf_hooks\";\nimport type { EventBuffer } from \"../buffer.js\";\nimport { matchProvider } from \"../domains.js\";\nimport { normalizeEndpoint } from \"../normalize.js\";\nimport { extractResponseFields } from \"../extract.js\";\nimport { getSessionSignals } from \"../context.js\";\n\nconst MAX_BODY_CAPTURE = 4096;\n\n// ── Noise filtering ────────────────────────────────────────────────\n\nlet _sdkEndpointHost: string | null = null;\n\nexport function setSdkEndpointHost(host: string): void {\n _sdkEndpointHost = host;\n}\n\nconst EXCLUDED_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"0.0.0.0\", \"::1\"]);\n\nfunction isExcluded(hostname: string): boolean {\n if (EXCLUDED_HOSTS.has(hostname)) return true;\n if (hostname.endsWith(\".local\") || hostname.endsWith(\".internal\")) return true;\n if (hostname === _sdkEndpointHost) return true;\n return false;\n}\n\n// Save originals\nexport const originalHttpRequest = http.request;\nexport const originalHttpGet = http.get;\nexport const originalHttpsRequest = https.request;\nexport const originalHttpsGet = https.get;\n\nlet _buffer: EventBuffer | null = null;\n\nfunction wrapRequest(\n original: typeof http.request,\n defaultProtocol: string\n): typeof http.request {\n const request = original as unknown as {\n (\n url: string | URL,\n callback?: (res: http.IncomingMessage) => void\n ): http.ClientRequest;\n (\n url: string | URL,\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n ): http.ClientRequest;\n (\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n ): http.ClientRequest;\n };\n\n return function patchedRequest(\n this: unknown,\n ...args: Parameters<typeof http.request>\n ): http.ClientRequest {\n // Parse the overloaded arguments\n let url: string | URL | http.RequestOptions;\n let options: http.RequestOptions | undefined;\n let callback: ((res: http.IncomingMessage) => void) | undefined;\n\n if (typeof args[0] === \"string\" || args[0] instanceof URL) {\n url = args[0];\n if (typeof args[1] === \"function\") {\n callback = args[1] as (res: http.IncomingMessage) => void;\n } else {\n options = args[1] as http.RequestOptions;\n callback = args[2] as ((res: http.IncomingMessage) => void) | undefined;\n }\n } else {\n options = args[0] as http.RequestOptions;\n callback = args[1] as ((res: http.IncomingMessage) => void) | undefined;\n url = \"\";\n }\n\n // Extract hostname\n let hostname: string | undefined;\n let pathname = \"/\";\n let method = \"GET\";\n\n if (typeof url === \"string\" && url) {\n try {\n const parsed = new URL(url);\n hostname = parsed.hostname;\n pathname = parsed.pathname;\n } catch {\n hostname = undefined;\n }\n } else if (url instanceof URL) {\n hostname = url.hostname;\n pathname = url.pathname;\n }\n\n if (options) {\n hostname = hostname || (options.hostname as string) || (options.host as string);\n if (options.path) pathname = options.path;\n if (options.method) method = options.method;\n }\n\n // Remove port from hostname\n if (hostname && hostname.includes(\":\")) {\n hostname = hostname.split(\":\")[0];\n }\n\n // Skip excluded hosts (localhost, SDK endpoint, etc.)\n if (hostname && isExcluded(hostname)) {\n return original.apply(this, args);\n }\n\n // Identify provider\n const provider = hostname ? matchProvider(hostname) : null;\n if (!provider || !_buffer) {\n return original.apply(this, args);\n }\n\n const start = performance.now();\n const signals = getSessionSignals();\n\n // Wrap the callback to intercept the response\n const wrappedCallback = (res: http.IncomingMessage) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n res.on(\"data\", (chunk: Buffer) => {\n if (totalSize < MAX_BODY_CAPTURE) {\n chunks.push(chunk);\n totalSize += chunk.length;\n }\n });\n\n res.on(\"end\", () => {\n const latencyMs = performance.now() - start;\n const status = res.statusCode ?? 0;\n const body = Buffer.concat(chunks).toString(\"utf-8\").slice(0, MAX_BODY_CAPTURE);\n\n const event: Record<string, unknown> = {\n event_type: \"http\",\n provider,\n endpoint_pattern: normalizeEndpoint(pathname),\n hostname,\n method: method.toUpperCase(),\n status,\n latency_ms: Math.round(latencyMs * 100) / 100,\n ts: Date.now() / 1000,\n ...signals,\n };\n\n if (status >= 400) {\n event.error_body = body;\n } else if (status >= 200 && status < 300) {\n const fields = extractResponseFields(body);\n if (fields) {\n event.response_fields = fields;\n }\n }\n\n _buffer!.push(event as any);\n });\n\n if (callback) callback(res);\n };\n\n // Call the original with wrapped callback\n if (typeof args[0] === \"string\" || args[0] instanceof URL) {\n if (options) {\n return request(args[0], options, wrappedCallback);\n } else {\n return request(args[0], wrappedCallback);\n }\n } else {\n return request(options ?? {}, wrappedCallback);\n }\n } as typeof http.request;\n}\n\n/** Patch http and https modules to intercept tracked API calls. */\nexport function patchHttp(buffer: EventBuffer): void {\n _buffer = buffer;\n http.request = wrapRequest(originalHttpRequest, \"http:\");\n http.get = wrapRequest(originalHttpGet, \"http:\") as typeof http.get;\n https.request = wrapRequest(originalHttpsRequest, \"https:\");\n https.get = wrapRequest(originalHttpsGet, \"https:\") as typeof https.get;\n}\n\n/** Restore original http/https methods. */\nexport function unpatchHttp(): void {\n _buffer = null;\n http.request = originalHttpRequest;\n http.get = originalHttpGet;\n https.request = originalHttpsRequest;\n https.get = originalHttpsGet;\n}\n","/**\n * Domain-to-provider mapping for HTTP call interception.\n *\n * Mirrors sdk/src/canary/domains.py exactly.\n */\n\nexport const COMMON_API_DOMAINS: Record<string, string> = {\n // Payments\n \"api.stripe.com\": \"stripe\",\n \"hooks.stripe.com\": \"stripe\",\n \"api.squareup.com\": \"square\",\n \"api.paypal.com\": \"paypal\",\n \"api.braintreegateway.com\": \"braintree\",\n // AI / ML\n \"api.openai.com\": \"openai\",\n \"api.anthropic.com\": \"anthropic\",\n \"api.cohere.ai\": \"cohere\",\n \"generativelanguage.googleapis.com\": \"google-ai\",\n \"api.replicate.com\": \"replicate\",\n \"api-inference.huggingface.co\": \"huggingface\",\n \"api.mistral.ai\": \"mistral\",\n // Communication\n \"api.twilio.com\": \"twilio\",\n \"api.sendgrid.com\": \"sendgrid\",\n \"api.mailgun.net\": \"mailgun\",\n \"api.postmarkapp.com\": \"postmark\",\n \"api.resend.com\": \"resend\",\n // Developer tools\n \"api.github.com\": \"github\",\n \"gitlab.com\": \"gitlab\",\n \"api.slack.com\": \"slack\",\n \"discord.com\": \"discord\",\n \"api.linear.app\": \"linear\",\n // Cloud / Infrastructure\n \"api.cloudflare.com\": \"cloudflare\",\n \"api.vercel.com\": \"vercel\",\n \"api.heroku.com\": \"heroku\",\n \"api.netlify.com\": \"netlify\",\n // Data / Analytics\n \"api.segment.io\": \"segment\",\n \"api.mixpanel.com\": \"mixpanel\",\n \"api.amplitude.com\": \"amplitude\",\n // Productivity / SaaS\n \"api.notion.so\": \"notion\",\n \"api.airtable.com\": \"airtable\",\n \"api.hubspot.com\": \"hubspot\",\n \"api.salesforce.com\": \"salesforce\",\n // Social\n \"graph.facebook.com\": \"facebook\",\n \"api.twitter.com\": \"twitter\",\n \"api.x.com\": \"twitter\",\n // Finance\n \"api.plaid.com\": \"plaid\",\n \"sandbox.plaid.com\": \"plaid\",\n // Commerce\n \"api.shopify.com\": \"shopify\",\n // Maps / Location\n \"maps.googleapis.com\": \"google-maps\",\n // Auth\n \"api.auth0.com\": \"auth0\",\n // Search\n \"api.algolia.com\": \"algolia\",\n // Storage\n \"api.supabase.co\": \"supabase\",\n \"api.firebase.google.com\": \"firebase\",\n};\n\n// Build reverse lookup from registrable domain -> provider\nconst DOMAIN_SUFFIXES: Record<string, string> = {};\nfor (const [domain, provider] of Object.entries(COMMON_API_DOMAINS)) {\n const parts = domain.split(\".\");\n if (parts.length >= 2) {\n const registrable = parts.slice(-2).join(\".\");\n DOMAIN_SUFFIXES[registrable] = provider;\n }\n}\n\n/**\n * Return the provider name for a given host, or null if unknown.\n *\n * Checks exact match first, then falls back to registrable domain suffix.\n */\nexport function matchProvider(host: string): string | null {\n host = host.toLowerCase().trim();\n\n // Exact match\n if (host in COMMON_API_DOMAINS) {\n return COMMON_API_DOMAINS[host];\n }\n\n // Subdomain match\n const parts = host.split(\".\");\n if (parts.length >= 2) {\n const registrable = parts.slice(-2).join(\".\");\n if (registrable in DOMAIN_SUFFIXES) {\n return DOMAIN_SUFFIXES[registrable];\n }\n }\n\n return null;\n}\n\n/**\n * Derive provider name from hostname by extracting the main domain.\n * \"api.brave.com\" → \"brave\", \"api.search.brave.com\" → \"brave\"\n */\nfunction deriveFromHostname(hostname: string): string {\n const parts = hostname.split(\".\");\n if (parts.length >= 2) {\n // Take the second-to-last part (main domain name)\n return parts[parts.length - 2];\n }\n return hostname;\n}\n\n/**\n * Three-tier provider resolution:\n * 1. Exact/suffix match from domain map\n * 2. Derive from hostname (extract main domain name)\n * 3. Fallback to full hostname\n */\nexport function resolveProvider(hostname: string | undefined): string {\n if (!hostname) return \"unknown\";\n const matched = matchProvider(hostname);\n if (matched) return matched;\n return deriveFromHostname(hostname.toLowerCase().trim());\n}\n","/**\n * Normalize URL paths by replacing dynamic segments with {id} placeholders.\n *\n * Mirrors sdk/src/canary/normalize.py exactly.\n */\n\n// UUID pattern (8-4-4-4-12 hex)\nconst UUID_RE =\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g;\n\n// Stripe-style IDs: short prefix (2-4 lowercase) + underscore + alphanumeric with digit\nconst STRIPE_ID_RE = /^[a-z]{2,4}_[A-Za-z0-9]*\\d[A-Za-z0-9]*$/;\n\n// Purely numeric segments (at least 2 digits)\nconst NUMERIC_RE = /^\\d{2,}$/;\n\n/**\n * Normalize a URL path, replacing dynamic segments with {id}.\n *\n * - Strips query strings and fragments\n * - Replaces UUIDs, Stripe-style IDs, and numeric IDs with {id}\n * - Preserves version prefixes like /v1/, /v2/\n */\nexport function normalizeEndpoint(path: string): string {\n // Strip query string and fragment\n path = path.split(\"?\")[0].split(\"#\")[0];\n\n // Replace UUIDs inline first\n path = path.replace(UUID_RE, \"{id}\");\n\n // Split into segments and process each\n const parts = path.split(\"/\");\n const normalized: string[] = [];\n\n for (const part of parts) {\n if (!part) {\n normalized.push(part);\n } else if (part === \"{id}\") {\n normalized.push(part);\n } else if (STRIPE_ID_RE.test(part)) {\n normalized.push(\"{id}\");\n } else if (NUMERIC_RE.test(part)) {\n normalized.push(\"{id}\");\n } else {\n normalized.push(part);\n }\n }\n\n return normalized.join(\"/\");\n}\n","/**\n * Extract allowlisted fields from HTTP response bodies.\n *\n * Mirrors sdk/src/canary/extract.py exactly.\n */\n\nconst COMMON_RESPONSE_KEYS = new Set([\n \"model\",\n \"status\",\n \"object\",\n \"type\",\n \"finish_reason\",\n \"stop_reason\",\n \"error_code\",\n \"error_message\",\n \"usage\",\n \"created\",\n \"currency\",\n \"livemode\",\n]);\n\nconst USAGE_KEYS = new Set([\n \"prompt_tokens\",\n \"completion_tokens\",\n \"total_tokens\",\n \"input_tokens\",\n \"output_tokens\",\n]);\n\nconst MAX_BODY_SIZE = 4096;\n\n/**\n * Extract allowlisted fields from a response body string.\n *\n * Returns a record of extracted fields, or undefined if body is not JSON\n * or no matching keys are found.\n */\nexport function extractResponseFields(\n body: string\n): Record<string, unknown> | undefined {\n if (body.length > MAX_BODY_SIZE) {\n body = body.slice(0, MAX_BODY_SIZE);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(body);\n } catch {\n return undefined;\n }\n\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n return undefined;\n }\n\n const obj = data as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n\n for (const key of COMMON_RESPONSE_KEYS) {\n if (key in obj) {\n const val = obj[key];\n if (key === \"usage\" && typeof val === \"object\" && val !== null && !Array.isArray(val)) {\n const usageObj = val as Record<string, unknown>;\n const usage: Record<string, unknown> = {};\n for (const uk of USAGE_KEYS) {\n if (uk in usageObj) {\n usage[uk] = usageObj[uk];\n }\n }\n if (Object.keys(usage).length > 0) {\n result[\"usage\"] = usage;\n }\n } else {\n result[key] = val;\n }\n }\n }\n\n // Extract finish_reason from choices[0] (OpenAI style)\n if (!(\"finish_reason\" in result) && \"choices\" in obj) {\n const choices = obj[\"choices\"];\n if (Array.isArray(choices) && choices.length > 0) {\n const choice = choices[0];\n if (typeof choice === \"object\" && choice !== null && \"finish_reason\" in choice) {\n result[\"finish_reason\"] = (choice as Record<string, unknown>)[\"finish_reason\"];\n }\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n","/**\n * Session correlation via AsyncLocalStorage.\n *\n * Node.js equivalent of sdk/src/canary/context.py using\n * AsyncLocalStorage instead of ContextVar.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomUUID } from \"node:crypto\";\nimport { detectAgent } from \"./agent-detect.js\";\n\ninterface SessionContext {\n frameworkSessionId: string;\n}\n\nconst sessionStorage = new AsyncLocalStorage<SessionContext>();\nconst sdkInstanceId = randomUUID().replace(/-/g, \"\");\nlet callSequence = 0;\nlet defaultFrameworkSessionId = \"\";\n\nexport interface SessionSignals {\n sdk_instance_id: string;\n process_id: number;\n thread_id: number;\n call_sequence: number;\n framework_session_id?: string;\n agent_name?: string;\n agent_version?: string;\n}\n\n/**\n * Return session correlation signals for the current execution context.\n */\nexport function getSessionSignals(): SessionSignals {\n callSequence++;\n\n const store = sessionStorage.getStore();\n const signals: SessionSignals = {\n sdk_instance_id: sdkInstanceId,\n process_id: process.pid,\n thread_id: 0,\n call_sequence: callSequence,\n };\n\n const frameworkSessionId =\n store?.frameworkSessionId || defaultFrameworkSessionId;\n if (frameworkSessionId) {\n signals.framework_session_id = frameworkSessionId;\n }\n\n const agentInfo = detectAgent();\n if (agentInfo.agent_name) {\n signals.agent_name = agentInfo.agent_name;\n }\n if (agentInfo.agent_version) {\n signals.agent_version = agentInfo.agent_version;\n }\n\n return signals;\n}\n\n/**\n * Set the framework session ID for the current async context.\n *\n * If no async context is active, this becomes the default session ID used\n * by subsequent intercepted calls on this process.\n */\nexport function setFrameworkSession(sessionId: string): void {\n const store = sessionStorage.getStore();\n if (store) {\n store.frameworkSessionId = sessionId;\n return;\n }\n\n defaultFrameworkSessionId = sessionId;\n}\n\n/**\n * Clear the framework session ID for the current async context.\n */\nexport function clearFrameworkSession(): void {\n const store = sessionStorage.getStore();\n if (store) {\n store.frameworkSessionId = \"\";\n return;\n }\n\n defaultFrameworkSessionId = \"\";\n}\n\n/**\n * Run a function with a scoped framework session ID.\n *\n * The session ID propagates through async call chains automatically.\n */\nexport function runWithSession<T>(sessionId: string, fn: () => T): T {\n return sessionStorage.run({ frameworkSessionId: sessionId }, fn);\n}\n\n/**\n * Reset all context state. For testing only.\n */\nexport function resetContext(): void {\n callSequence = 0;\n defaultFrameworkSessionId = \"\";\n}\n","/**\n * Detect which AI agent framework is running via environment variables.\n */\n\nexport interface AgentInfo {\n agent_name: string | null;\n agent_version: string | null;\n}\n\nexport const AGENT_ENV_MAP: ReadonlyArray<{ envKey: string; name: string; versionKey?: string; useValue?: boolean }> = [\n { envKey: \"CANARY_AGENT_NAME\", name: \"\", useValue: true },\n { envKey: \"CLAUDE_CODE\", name: \"claude_code\" },\n { envKey: \"CURSOR_TRACE_ID\", name: \"cursor\" },\n { envKey: \"OPENCLAW_SESSION_ID\", name: \"openclaw\", versionKey: \"OPENCLAW_VERSION\" },\n { envKey: \"WINDSURF_SESSION_ID\", name: \"windsurf\" },\n { envKey: \"CLINE_TASK_ID\", name: \"cline\" },\n { envKey: \"AIDER_VERSION\", name: \"aider\", versionKey: \"AIDER_VERSION\" },\n { envKey: \"DEVIN_SESSION_ID\", name: \"devin\" },\n { envKey: \"CODEX_CLI\", name: \"codex\" },\n { envKey: \"GITHUB_COPILOT\", name: \"github_copilot\" },\n { envKey: \"CODY_SESSION_ID\", name: \"sourcegraph_cody\" },\n];\n\nlet cachedInfo: AgentInfo | null = null;\n\nexport function detectAgent(): AgentInfo {\n if (cachedInfo) return cachedInfo;\n\n for (const entry of AGENT_ENV_MAP) {\n const val = process.env[entry.envKey];\n // Check !== undefined so empty strings (presence flags) still detect\n if (val !== undefined) {\n cachedInfo = {\n agent_name: entry.useValue ? (val || null) : entry.name,\n agent_version: entry.versionKey ? (process.env[entry.versionKey] ?? null) : null,\n };\n if (cachedInfo.agent_name) return cachedInfo;\n }\n }\n\n cachedInfo = { agent_name: null, agent_version: null };\n return cachedInfo;\n}\n\nexport function resetAgentCache(): void {\n cachedInfo = null;\n}\n","/**\n * Patch globalThis.fetch to intercept outgoing HTTP calls (Node 18+).\n *\n * Mirrors the fetch interception logic from the Python SDK's async interceptor.\n */\n\nimport { performance } from \"node:perf_hooks\";\nimport type { EventBuffer } from \"../buffer.js\";\nimport { matchProvider } from \"../domains.js\";\nimport { normalizeEndpoint } from \"../normalize.js\";\nimport { extractResponseFields } from \"../extract.js\";\nimport { getSessionSignals } from \"../context.js\";\n\nconst MAX_BODY_CAPTURE = 4096;\nconst CLONE_READ_TIMEOUT_MS = 5000;\n\n// ── Noise filtering ────────────────────────────────────────────────\n\nlet _sdkEndpointHost: string | null = null;\n\nexport function setSdkEndpointHost(host: string): void {\n _sdkEndpointHost = host;\n}\n\nconst EXCLUDED_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"0.0.0.0\", \"::1\"]);\n\nfunction isExcluded(hostname: string): boolean {\n if (EXCLUDED_HOSTS.has(hostname)) return true;\n if (hostname.endsWith(\".local\") || hostname.endsWith(\".internal\")) return true;\n if (hostname === _sdkEndpointHost) return true;\n return false;\n}\n\nlet _originalFetch: typeof globalThis.fetch | null = null;\nlet _buffer: EventBuffer | null = null;\n\ntype FetchInput = Parameters<typeof globalThis.fetch>[0];\ntype FetchInit = Parameters<typeof globalThis.fetch>[1];\n\n/** Patch globalThis.fetch to intercept tracked API calls. */\nexport function patchFetch(buffer: EventBuffer): void {\n if (typeof globalThis.fetch === \"undefined\") return; // Node < 18\n\n _originalFetch = globalThis.fetch;\n _buffer = buffer;\n\n globalThis.fetch = async function patchedFetch(\n input: FetchInput,\n init?: FetchInit\n ): Promise<Response> {\n // Parse URL\n let url: URL;\n let method = \"GET\";\n\n try {\n if (typeof input === \"string\") {\n url = new URL(input);\n } else if (input instanceof URL) {\n url = input;\n } else if (typeof Request !== \"undefined\" && input instanceof Request) {\n url = new URL(input.url);\n method = input.method;\n } else {\n return _originalFetch!(input, init);\n }\n } catch {\n return _originalFetch!(input, init);\n }\n\n if (init?.method) method = init.method;\n\n // Skip excluded hosts (localhost, SDK endpoint, etc.)\n if (isExcluded(url.hostname)) {\n return _originalFetch!(input, init);\n }\n\n const provider = matchProvider(url.hostname);\n if (!provider || !_buffer) {\n return _originalFetch!(input, init);\n }\n\n const start = performance.now();\n const signals = getSessionSignals();\n\n const response = await _originalFetch!(input, init);\n const latencyMs = performance.now() - start;\n const status = response.status;\n\n // Clone response to read body without consuming caller's stream\n let body = \"\";\n try {\n const clone = response.clone();\n const text = await Promise.race([\n clone.text(),\n new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error(\"timeout\")), CLONE_READ_TIMEOUT_MS)\n ),\n ]);\n body = text.slice(0, MAX_BODY_CAPTURE);\n } catch {\n // Couldn't read body — continue without it\n }\n\n const event: Record<string, unknown> = {\n event_type: \"http\",\n provider,\n endpoint_pattern: normalizeEndpoint(url.pathname),\n hostname: url.hostname,\n method: method.toUpperCase(),\n status,\n latency_ms: Math.round(latencyMs * 100) / 100,\n ts: Date.now() / 1000,\n ...signals,\n };\n\n if (status >= 400) {\n event.error_body = body;\n } else if (status >= 200 && status < 300 && body) {\n const fields = extractResponseFields(body);\n if (fields) {\n event.response_fields = fields;\n }\n }\n\n _buffer.push(event as any);\n return response;\n };\n}\n\n/** Restore original globalThis.fetch. */\nexport function unpatchFetch(): void {\n if (_originalFetch) {\n globalThis.fetch = _originalFetch;\n _originalFetch = null;\n }\n _buffer = null;\n}\n","/**\n * Auto-generate feedback events from session data at shutdown.\n *\n * Mirrors sdk/src/canary/reporter.py exactly.\n */\n\nimport type { CanaryEvent, FeedbackEvent } from \"./types.js\";\n\nconst MAX_FRICTION_POINTS = 10;\nconst MAX_FRICTION_LENGTH = 200;\n\nexport class SessionReporter {\n /**\n * Analyze events and return feedback events for each session.\n */\n generateFeedback(events: CanaryEvent[]): FeedbackEvent[] {\n // Group events by session\n const sessions = new Map<string | undefined, CanaryEvent[]>();\n\n for (const event of events) {\n if (event.event_type === \"feedback\") continue; // skip existing feedback\n const sid = (event as unknown as Record<string, unknown>).framework_session_id as\n | string\n | undefined;\n let list = sessions.get(sid);\n if (!list) {\n list = [];\n sessions.set(sid, list);\n }\n list.push(event);\n }\n\n const feedbackEvents: FeedbackEvent[] = [];\n for (const [sessionId, sessionEvents] of sessions) {\n const feedback = this._analyzeSession(sessionId, sessionEvents);\n if (feedback) feedbackEvents.push(feedback);\n }\n\n return feedbackEvents;\n }\n\n private _analyzeSession(\n sessionId: string | undefined,\n events: CanaryEvent[]\n ): FeedbackEvent | null {\n const httpEvents = events.filter((e) => e.event_type === \"http\") as Array<\n CanaryEvent & { event_type: \"http\" }\n >;\n const actionEvents = events.filter(\n (e) => e.event_type === \"action\"\n ) as Array<CanaryEvent & { event_type: \"action\" }>;\n\n if (httpEvents.length === 0 && actionEvents.length === 0) return null;\n\n const errorEvents = httpEvents.filter(\n (e) => typeof e.status === \"number\" && e.status >= 400\n );\n const totalCalls = httpEvents.length;\n const errorCount = errorEvents.length;\n const errorRate = totalCalls > 0 ? errorCount / totalCalls : 0;\n const worked = errorRate < 0.5;\n\n const frictionPoints = this._extractFrictionPoints(errorEvents);\n const context = this._buildContext(httpEvents, actionEvents, errorCount);\n\n const errorProviders = [\n ...new Set(\n errorEvents\n .map((e) => e.provider)\n .filter((p): p is string => typeof p === \"string\")\n ),\n ].sort();\n\n const feedback: FeedbackEvent = {\n event_type: \"feedback\",\n source: \"auto_report\",\n worked,\n context,\n ts: Date.now() / 1000,\n };\n\n if (frictionPoints.length > 0) {\n feedback.friction_points = frictionPoints;\n }\n if (sessionId) {\n feedback.framework_session_id = sessionId;\n }\n if (errorProviders.length > 0) {\n feedback.provider = errorProviders[0];\n }\n\n return feedback;\n }\n\n private _extractFrictionPoints(\n errorEvents: Array<CanaryEvent & { event_type: \"http\" }>\n ): string[] {\n const frictionPoints: string[] = [];\n const seen = new Set<string>();\n\n for (const event of errorEvents) {\n const provider = event.provider ?? \"unknown\";\n const endpoint = event.endpoint_pattern ?? \"\";\n const errorBody = (event as unknown as Record<string, unknown>).error_body as string ?? \"\";\n const status = event.status ?? 0;\n\n let point: string;\n if (!errorBody) {\n point = `${provider} ${endpoint}: HTTP ${status}`;\n } else {\n const message = this._extractErrorMessage(errorBody);\n point = `${provider} ${endpoint}: ${message}`;\n }\n\n point = point.slice(0, MAX_FRICTION_LENGTH);\n if (!seen.has(point)) {\n seen.add(point);\n frictionPoints.push(point);\n }\n\n if (frictionPoints.length >= MAX_FRICTION_POINTS) break;\n }\n\n return frictionPoints;\n }\n\n private _extractErrorMessage(errorBody: string): string {\n try {\n const data = JSON.parse(errorBody);\n if (typeof data === \"object\" && data !== null) {\n for (const key of [\"message\", \"error\", \"detail\", \"error_message\"]) {\n const val = data[key];\n if (typeof val === \"string\") return val;\n if (typeof val === \"object\" && val !== null) {\n const msg = val.message ?? val.type;\n if (msg) return String(msg);\n }\n }\n }\n } catch {\n // Not JSON\n }\n\n const firstLine = errorBody.trim().split(\"\\n\")[0];\n return firstLine.slice(0, MAX_FRICTION_LENGTH);\n }\n\n private _buildContext(\n httpEvents: Array<CanaryEvent & { event_type: \"http\" }>,\n actionEvents: Array<CanaryEvent & { event_type: \"action\" }>,\n errorCount: number\n ): string {\n const parts: string[] = [];\n\n const providers = [\n ...new Set(\n httpEvents\n .map((e) => e.provider)\n .filter((p): p is string => typeof p === \"string\")\n ),\n ].sort();\n if (providers.length > 0) {\n parts.push(`APIs: ${providers.join(\", \")}`);\n }\n\n const pages = actionEvents\n .filter((e) => e.action_type === \"page_navigate\" && e.action_args)\n .map((e) => (e.action_args as unknown as Record<string, unknown>)?.url)\n .filter((u): u is string => typeof u === \"string\" && u.length > 0);\n if (pages.length > 0) {\n parts.push(`${pages.length} pages visited`);\n }\n\n if (httpEvents.length > 0) {\n parts.push(`${httpEvents.length} HTTP calls, ${errorCount} errors`);\n }\n\n if (actionEvents.length > 0) {\n parts.push(`${actionEvents.length} browser actions`);\n }\n\n return parts.length > 0 ? parts.join(\"; \") : \"Session completed\";\n }\n}\n","/**\n * MCP adapter — wraps tool handlers for observability.\n *\n * Mirrors sdk/src/canary/adapters/mcp.py.\n */\n\nimport { performance } from \"node:perf_hooks\";\nimport type { EventBuffer } from \"../buffer.js\";\nimport type { ActionEvent } from \"../types.js\";\n\nconst MAX_RESULT_SIZE = 4096;\n\nexport class CanaryMCPMiddleware {\n private _buffer: EventBuffer;\n private _sessionId: string | undefined;\n\n constructor(buffer: EventBuffer, sessionId?: string) {\n this._buffer = buffer;\n this._sessionId = sessionId;\n }\n\n /**\n * Wrap a tool handler to capture execution time, arguments, and results.\n */\n wrap<T extends (...args: any[]) => any>(\n handler: T,\n toolName: string\n ): T {\n const self = this;\n\n const wrapped = async function (\n this: unknown,\n ...args: Parameters<T>\n ): Promise<ReturnType<T>> {\n const start = performance.now();\n let result: ReturnType<T>;\n let error: string | undefined;\n\n try {\n result = await handler.apply(this, args);\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n self._pushEvent(toolName, args, undefined, error, start);\n throw err;\n }\n\n self._pushEvent(toolName, args, result, undefined, start);\n return result;\n } as unknown as T;\n\n return wrapped;\n }\n\n /**\n * Manually record a tool call event.\n */\n onToolCall(\n toolName: string,\n args: Record<string, unknown>,\n result?: unknown,\n error?: string,\n latencyMs?: number\n ): void {\n const event: ActionEvent = {\n event_type: \"action\",\n source: \"mcp\",\n action_type: toolName,\n action_args: args,\n latency_ms: latencyMs ?? 0,\n ts: Date.now() / 1000,\n };\n\n if (error) {\n event.action_result = { error };\n event.status = 500;\n } else if (result !== undefined) {\n const serialized = JSON.stringify(result);\n event.action_result = {\n value:\n serialized.length > MAX_RESULT_SIZE\n ? serialized.slice(0, MAX_RESULT_SIZE)\n : serialized,\n };\n }\n\n if (this._sessionId) {\n event.framework_session_id = this._sessionId;\n }\n\n this._buffer.push(event);\n }\n\n private _pushEvent(\n toolName: string,\n args: unknown[],\n result: unknown,\n error: string | undefined,\n startTime: number\n ): void {\n const latencyMs = performance.now() - startTime;\n\n // Try to convert args array to object\n let argsObj: Record<string, unknown> = {};\n if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n argsObj = args[0] as Record<string, unknown>;\n } else if (args.length > 0) {\n argsObj = { args };\n }\n\n this.onToolCall(toolName, argsObj, result, error, latencyMs);\n }\n}\n","/**\n * canary-agent — API observability for Node.js AI agents.\n *\n * Mirrors sdk/src/canary/__init__.py.\n */\n\nimport { EventBuffer } from \"./buffer.js\";\nimport {\n patchHttp,\n unpatchHttp,\n patchFetch,\n unpatchFetch,\n originalHttpRequest,\n originalHttpsRequest,\n setHttpSdkEndpointHost,\n setFetchSdkEndpointHost,\n} from \"./interceptors/index.js\";\nimport { SessionReporter } from \"./reporter.js\";\nimport {\n runWithSession,\n setFrameworkSession,\n clearFrameworkSession,\n getSessionSignals,\n} from \"./context.js\";\nimport type { CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nimport { detectAgent } from \"./agent-detect.js\";\n\nlet _initialized = false;\nlet _buffer: EventBuffer | null = null;\nlet _autoReport = true;\n\n/**\n * Initialize Canary SDK. Patches http/https and fetch to intercept API calls.\n */\nexport function init(config: CanaryConfig): void {\n if (_initialized) return;\n\n if (!config.apiKey.startsWith(\"cnry_sk_\")) {\n throw new Error(\n \"Invalid API key: must start with 'cnry_sk_'. \" +\n \"Get your key at https://app.canary.dev\"\n );\n }\n\n const endpoint = config.endpoint ?? \"https://canary-production-89d8.up.railway.app\";\n _autoReport = config.autoReport ?? true;\n const autoFlush = config.autoFlush ?? true;\n\n _buffer = new EventBuffer(\n config.apiKey,\n endpoint,\n autoFlush,\n originalHttpsRequest,\n originalHttpRequest\n );\n\n // Tell interceptors to skip the SDK's own endpoint\n try {\n const endpointUrl = new URL(endpoint);\n setHttpSdkEndpointHost(endpointUrl.hostname);\n setFetchSdkEndpointHost(endpointUrl.hostname);\n } catch {\n // Invalid endpoint URL — skip exclusion setup\n }\n\n patchHttp(_buffer);\n patchFetch(_buffer);\n _initialized = true;\n}\n\n/**\n * Shut down the SDK: unpatch interceptors, generate auto-report, flush events.\n */\nexport async function shutdown(): Promise<void> {\n if (!_initialized || !_buffer) return;\n\n unpatchHttp();\n unpatchFetch();\n\n if (_autoReport) {\n const reporter = new SessionReporter();\n const allEvents = _buffer.peek();\n const feedbackEvents = reporter.generateFeedback(allEvents);\n for (const event of feedbackEvents) {\n _buffer.push(event);\n }\n }\n\n await _buffer.shutdown();\n _buffer = null;\n _initialized = false;\n}\n\n/**\n * Submit manual feedback.\n */\nexport function survey(options: {\n worked?: boolean;\n context?: string;\n frictionPoints?: string[];\n provider?: string;\n sessionId?: string;\n}): void {\n if (!_buffer) {\n throw new Error(\"Canary SDK not initialized. Call init() first.\");\n }\n\n const agentInfo = detectAgent();\n const event: FeedbackEvent = {\n event_type: \"feedback\",\n source: \"manual\",\n worked: options.worked ?? true,\n context: options.context ?? \"\",\n ts: Date.now() / 1000,\n agent_name: agentInfo.agent_name ?? undefined,\n agent_version: agentInfo.agent_version ?? undefined,\n };\n\n if (options.frictionPoints && options.frictionPoints.length > 0) {\n event.friction_points = options.frictionPoints;\n }\n if (options.provider) {\n event.provider = options.provider;\n }\n if (options.sessionId) {\n event.framework_session_id = options.sessionId;\n }\n\n _buffer.push(event);\n}\n\n/**\n * Get the global EventBuffer (for advanced usage / adapters).\n */\nexport function getBuffer(): EventBuffer | null {\n return _buffer;\n}\n\n// Re-export session helpers\nexport { runWithSession, setFrameworkSession, clearFrameworkSession, getSessionSignals };\n\n// Re-export types\nexport type { CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nexport type { HttpEvent, ActionEvent } from \"./types.js\";\nexport { EventBuffer } from \"./buffer.js\";\nexport { SessionReporter } from \"./reporter.js\";\nexport { CanaryMCPMiddleware } from \"./adapters/mcp.js\";\nexport { matchProvider, resolveProvider, COMMON_API_DOMAINS } from \"./domains.js\";\nexport { normalizeEndpoint } from \"./normalize.js\";\nexport { extractResponseFields } from \"./extract.js\";\nexport { detectAgent, resetAgentCache } from \"./agent-detect.js\";\n"],"mappings":";AAMA,SAAS,gBAAgB;AACzB,OAAO,WAAW;AAClB,OAAO,UAAU;AAGjB,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,oBAAoB;AAC1B,IAAM,yBAAyB;AAC/B,IAAM,2BAA2B;AACjC,IAAM,iBAAiB;AACvB,IAAM,mBAAmB,CAAC,KAAM,KAAM,GAAI;AAEnC,IAAM,cAAN,MAAkB;AAAA,EACf,UAAyB,CAAC;AAAA,EAC1B,gBAA4C,oBAAI,IAAI;AAAA,EACpD,0BAA0B;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,SAAgD;AAAA,EAChD,WAAW;AAAA;AAAA,EAEX;AAAA;AAAA,EAEA;AAAA,EAER,YACE,QACA,WAAW,yBACX,YAAY,MACZA,uBACAC,sBACA;AACA,SAAK,UAAU;AACf,SAAK,YAAY,SAAS,QAAQ,QAAQ,EAAE;AAC5C,SAAK,wBAAwBD,yBAAwB,MAAM;AAC3D,SAAK,uBAAuBC,wBAAuB,KAAK;AAExD,QAAI,WAAW;AACb,WAAK,SAAS,YAAY,MAAM,KAAK,MAAM,GAAG,iBAAiB;AAC/D,WAAK,OAAO,MAAM;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,OAA0B;AAC7B,QAAI,KAAK,QAAQ,UAAU,SAAS;AAClC,WAAK,QAAQ,MAAM;AAAA,IACrB;AACA,SAAK,QAAQ,KAAK,KAAK;AAGvB,UAAM,YAAa,MAA6C;AAChE,QAAI,OAAO,cAAc,YAAY,WAAW;AAC9C,UAAI,KAAK,0BAA0B,0BAA0B;AAC3D,YAAI,gBAAgB,KAAK,cAAc,IAAI,SAAS;AACpD,YAAI,CAAC,eAAe;AAClB,0BAAgB,CAAC;AACjB,eAAK,cAAc,IAAI,WAAW,aAAa;AAAA,QACjD;AACA,YAAI,cAAc,SAAS,wBAAwB;AACjD,wBAAc,KAAK,KAAK;AACxB,eAAK;AAAA,QACP;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,iBAAiB,WAAkC;AACjD,WAAO,CAAC,GAAI,KAAK,cAAc,IAAI,SAAS,KAAK,CAAC,CAAE;AAAA,EACtD;AAAA;AAAA,EAGA,aAAa,WAAyB;AACpC,UAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,QAAI,QAAQ;AACV,WAAK,0BAA0B,KAAK;AAAA,QAClC;AAAA,QACA,KAAK,0BAA0B,OAAO;AAAA,MACxC;AACA,WAAK,cAAc,OAAO,SAAS;AAAA,IACrC;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,OAA8B;AAClC,UAAM,IAAI,KAAK,IAAI,OAAO,KAAK,QAAQ,MAAM;AAC7C,WAAO,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,EACjC;AAAA;AAAA,EAGA,OAAsB;AACpB,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,SAAiB;AACnB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,UAAM,SAAS,KAAK,MAAM,UAAU;AACpC,QAAI,OAAO,WAAW,EAAG;AAEzB,aAAS,UAAU,GAAG,UAAU,gBAAgB,WAAW;AACzD,UAAI;AACF,cAAM,KAAK,MAAM,MAAM;AACvB;AAAA,MACF,QAAQ;AACN,YAAI,UAAU,iBAAiB,GAAG;AAChC,gBAAM,MAAM,iBAAiB,OAAO,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAAA,EAEF;AAAA;AAAA,EAGA,MAAM,WAA0B;AAC9B,SAAK,WAAW;AAChB,QAAI,KAAK,QAAQ;AACf,oBAAc,KAAK,MAAM;AACzB,WAAK,SAAS;AAAA,IAChB;AAEA,WAAO,KAAK,QAAQ,SAAS,GAAG;AAC9B,YAAM,SAAS,KAAK,MAAM,UAAU;AACpC,UAAI,OAAO,WAAW,EAAG;AAEzB,eAAS,UAAU,GAAG,UAAU,gBAAgB,WAAW;AACzD,YAAI;AACF,gBAAM,KAAK,MAAM,MAAM;AACvB;AAAA,QACF,QAAQ;AACN,cAAI,YAAY,iBAAiB,GAAG;AAAA,UAEpC,OAAO;AACL,kBAAM,MAAM,iBAAiB,OAAO,CAAC;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,MAAM,QAAsC;AAClD,UAAM,UAAU,KAAK,UAAU;AAAA,MAC7B;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AACD,UAAM,aAAa,SAAS,OAAO,KAAK,SAAS,OAAO,CAAC;AAEzD,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,SAAS,YAAY;AACjD,UAAM,UAAU,IAAI,aAAa;AACjC,UAAM,YAAY,UAAU,KAAK,wBAAwB,KAAK;AAE9D,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAM,MAAM;AAAA,QACV;AAAA,UACE,UAAU,IAAI;AAAA,UACd,MAAM,IAAI,SAAS,UAAU,MAAM;AAAA,UACnC,MAAM,IAAI;AAAA,UACV,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,oBAAoB;AAAA,YACpB,eAAe,UAAU,KAAK,OAAO;AAAA,YACrC,kBAAkB,WAAW;AAAA,UAC/B;AAAA,UACA,SAAS;AAAA,QACX;AAAA,QACA,CAAC,QAAQ;AAEP,cAAI,OAAO;AACX,cAAI,IAAI,cAAc,IAAI,cAAc,OAAO,IAAI,aAAa,KAAK;AACnE,oBAAQ;AAAA,UACV,OAAO;AACL,mBAAO,IAAI,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;AAAA,UAC5C;AAAA,QACF;AAAA,MACF;AAEA,UAAI,GAAG,SAAS,MAAM;AACtB,UAAI,GAAG,WAAW,MAAM;AACtB,YAAI,QAAQ;AACZ,eAAO,IAAI,MAAM,iBAAiB,CAAC;AAAA,MACrC,CAAC;AACD,UAAI,MAAM,UAAU;AACpB,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;;ACrMA,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,SAAS,mBAAmB;;;ACFrB,IAAM,qBAA6C;AAAA;AAAA,EAExD,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,4BAA4B;AAAA;AAAA,EAE5B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,qCAAqC;AAAA,EACrC,qBAAqB;AAAA,EACrB,gCAAgC;AAAA,EAChC,kBAAkB;AAAA;AAAA,EAElB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,kBAAkB;AAAA;AAAA,EAElB,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,kBAAkB;AAAA;AAAA,EAElB,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA;AAAA,EAEnB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA;AAAA,EAErB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA;AAAA,EAEtB,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,aAAa;AAAA;AAAA,EAEb,iBAAiB;AAAA,EACjB,qBAAqB;AAAA;AAAA,EAErB,mBAAmB;AAAA;AAAA,EAEnB,uBAAuB;AAAA;AAAA,EAEvB,iBAAiB;AAAA;AAAA,EAEjB,mBAAmB;AAAA;AAAA,EAEnB,mBAAmB;AAAA,EACnB,2BAA2B;AAC7B;AAGA,IAAM,kBAA0C,CAAC;AACjD,WAAW,CAAC,QAAQ,QAAQ,KAAK,OAAO,QAAQ,kBAAkB,GAAG;AACnE,QAAM,QAAQ,OAAO,MAAM,GAAG;AAC9B,MAAI,MAAM,UAAU,GAAG;AACrB,UAAM,cAAc,MAAM,MAAM,EAAE,EAAE,KAAK,GAAG;AAC5C,oBAAgB,WAAW,IAAI;AAAA,EACjC;AACF;AAOO,SAAS,cAAc,MAA6B;AACzD,SAAO,KAAK,YAAY,EAAE,KAAK;AAG/B,MAAI,QAAQ,oBAAoB;AAC9B,WAAO,mBAAmB,IAAI;AAAA,EAChC;AAGA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,UAAU,GAAG;AACrB,UAAM,cAAc,MAAM,MAAM,EAAE,EAAE,KAAK,GAAG;AAC5C,QAAI,eAAe,iBAAiB;AAClC,aAAO,gBAAgB,WAAW;AAAA,IACpC;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,mBAAmB,UAA0B;AACpD,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,MAAI,MAAM,UAAU,GAAG;AAErB,WAAO,MAAM,MAAM,SAAS,CAAC;AAAA,EAC/B;AACA,SAAO;AACT;AAQO,SAAS,gBAAgB,UAAsC;AACpE,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,UAAU,cAAc,QAAQ;AACtC,MAAI,QAAS,QAAO;AACpB,SAAO,mBAAmB,SAAS,YAAY,EAAE,KAAK,CAAC;AACzD;;;ACvHA,IAAM,UACJ;AAGF,IAAM,eAAe;AAGrB,IAAM,aAAa;AASZ,SAAS,kBAAkB,MAAsB;AAEtD,SAAO,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAGtC,SAAO,KAAK,QAAQ,SAAS,MAAM;AAGnC,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAM,aAAuB,CAAC;AAE9B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,MAAM;AACT,iBAAW,KAAK,IAAI;AAAA,IACtB,WAAW,SAAS,QAAQ;AAC1B,iBAAW,KAAK,IAAI;AAAA,IACtB,WAAW,aAAa,KAAK,IAAI,GAAG;AAClC,iBAAW,KAAK,MAAM;AAAA,IACxB,WAAW,WAAW,KAAK,IAAI,GAAG;AAChC,iBAAW,KAAK,MAAM;AAAA,IACxB,OAAO;AACL,iBAAW,KAAK,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,GAAG;AAC5B;;;AC3CA,IAAM,uBAAuB,oBAAI,IAAI;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,gBAAgB;AAQf,SAAS,sBACd,MACqC;AACrC,MAAI,KAAK,SAAS,eAAe;AAC/B,WAAO,KAAK,MAAM,GAAG,aAAa;AAAA,EACpC;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,GAAG;AACpE,WAAO;AAAA,EACT;AAEA,QAAM,MAAM;AACZ,QAAM,SAAkC,CAAC;AAEzC,aAAW,OAAO,sBAAsB;AACtC,QAAI,OAAO,KAAK;AACd,YAAM,MAAM,IAAI,GAAG;AACnB,UAAI,QAAQ,WAAW,OAAO,QAAQ,YAAY,QAAQ,QAAQ,CAAC,MAAM,QAAQ,GAAG,GAAG;AACrF,cAAM,WAAW;AACjB,cAAM,QAAiC,CAAC;AACxC,mBAAW,MAAM,YAAY;AAC3B,cAAI,MAAM,UAAU;AAClB,kBAAM,EAAE,IAAI,SAAS,EAAE;AAAA,UACzB;AAAA,QACF;AACA,YAAI,OAAO,KAAK,KAAK,EAAE,SAAS,GAAG;AACjC,iBAAO,OAAO,IAAI;AAAA,QACpB;AAAA,MACF,OAAO;AACL,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,mBAAmB,WAAW,aAAa,KAAK;AACpD,UAAM,UAAU,IAAI,SAAS;AAC7B,QAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,GAAG;AAChD,YAAM,SAAS,QAAQ,CAAC;AACxB,UAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,mBAAmB,QAAQ;AAC9E,eAAO,eAAe,IAAK,OAAmC,eAAe;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AACnD;;;ACnFA,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;;;ACCpB,IAAM,gBAA0G;AAAA,EACrH,EAAE,QAAQ,qBAAqB,MAAM,IAAI,UAAU,KAAK;AAAA,EACxD,EAAE,QAAQ,eAAe,MAAM,cAAc;AAAA,EAC7C,EAAE,QAAQ,mBAAmB,MAAM,SAAS;AAAA,EAC5C,EAAE,QAAQ,uBAAuB,MAAM,YAAY,YAAY,mBAAmB;AAAA,EAClF,EAAE,QAAQ,uBAAuB,MAAM,WAAW;AAAA,EAClD,EAAE,QAAQ,iBAAiB,MAAM,QAAQ;AAAA,EACzC,EAAE,QAAQ,iBAAiB,MAAM,SAAS,YAAY,gBAAgB;AAAA,EACtE,EAAE,QAAQ,oBAAoB,MAAM,QAAQ;AAAA,EAC5C,EAAE,QAAQ,aAAa,MAAM,QAAQ;AAAA,EACrC,EAAE,QAAQ,kBAAkB,MAAM,iBAAiB;AAAA,EACnD,EAAE,QAAQ,mBAAmB,MAAM,mBAAmB;AACxD;AAEA,IAAI,aAA+B;AAE5B,SAAS,cAAyB;AACvC,MAAI,WAAY,QAAO;AAEvB,aAAW,SAAS,eAAe;AACjC,UAAM,MAAM,QAAQ,IAAI,MAAM,MAAM;AAEpC,QAAI,QAAQ,QAAW;AACrB,mBAAa;AAAA,QACX,YAAY,MAAM,WAAY,OAAO,OAAQ,MAAM;AAAA,QACnD,eAAe,MAAM,aAAc,QAAQ,IAAI,MAAM,UAAU,KAAK,OAAQ;AAAA,MAC9E;AACA,UAAI,WAAW,WAAY,QAAO;AAAA,IACpC;AAAA,EACF;AAEA,eAAa,EAAE,YAAY,MAAM,eAAe,KAAK;AACrD,SAAO;AACT;AAEO,SAAS,kBAAwB;AACtC,eAAa;AACf;;;AD/BA,IAAM,iBAAiB,IAAI,kBAAkC;AAC7D,IAAM,gBAAgB,WAAW,EAAE,QAAQ,MAAM,EAAE;AACnD,IAAI,eAAe;AACnB,IAAI,4BAA4B;AAezB,SAAS,oBAAoC;AAClD;AAEA,QAAM,QAAQ,eAAe,SAAS;AACtC,QAAM,UAA0B;AAAA,IAC9B,iBAAiB;AAAA,IACjB,YAAY,QAAQ;AAAA,IACpB,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AAEA,QAAM,qBACJ,OAAO,sBAAsB;AAC/B,MAAI,oBAAoB;AACtB,YAAQ,uBAAuB;AAAA,EACjC;AAEA,QAAM,YAAY,YAAY;AAC9B,MAAI,UAAU,YAAY;AACxB,YAAQ,aAAa,UAAU;AAAA,EACjC;AACA,MAAI,UAAU,eAAe;AAC3B,YAAQ,gBAAgB,UAAU;AAAA,EACpC;AAEA,SAAO;AACT;AAQO,SAAS,oBAAoB,WAAyB;AAC3D,QAAM,QAAQ,eAAe,SAAS;AACtC,MAAI,OAAO;AACT,UAAM,qBAAqB;AAC3B;AAAA,EACF;AAEA,8BAA4B;AAC9B;AAKO,SAAS,wBAA8B;AAC5C,QAAM,QAAQ,eAAe,SAAS;AACtC,MAAI,OAAO;AACT,UAAM,qBAAqB;AAC3B;AAAA,EACF;AAEA,8BAA4B;AAC9B;AAOO,SAAS,eAAkB,WAAmB,IAAgB;AACnE,SAAO,eAAe,IAAI,EAAE,oBAAoB,UAAU,GAAG,EAAE;AACjE;;;AJlFA,IAAM,mBAAmB;AAIzB,IAAI,mBAAkC;AAE/B,SAAS,mBAAmB,MAAoB;AACrD,qBAAmB;AACrB;AAEA,IAAM,iBAAiB,oBAAI,IAAI,CAAC,aAAa,aAAa,WAAW,KAAK,CAAC;AAE3E,SAAS,WAAW,UAA2B;AAC7C,MAAI,eAAe,IAAI,QAAQ,EAAG,QAAO;AACzC,MAAI,SAAS,SAAS,QAAQ,KAAK,SAAS,SAAS,WAAW,EAAG,QAAO;AAC1E,MAAI,aAAa,iBAAkB,QAAO;AAC1C,SAAO;AACT;AAGO,IAAM,sBAAsBC,MAAK;AACjC,IAAM,kBAAkBA,MAAK;AAC7B,IAAM,uBAAuBC,OAAM;AACnC,IAAM,mBAAmBA,OAAM;AAEtC,IAAI,UAA8B;AAElC,SAAS,YACP,UACA,iBACqB;AACrB,QAAM,UAAU;AAgBhB,SAAO,SAAS,kBAEX,MACiB;AAEpB,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,aAAa,KAAK;AACzD,YAAM,KAAK,CAAC;AACZ,UAAI,OAAO,KAAK,CAAC,MAAM,YAAY;AACjC,mBAAW,KAAK,CAAC;AAAA,MACnB,OAAO;AACL,kBAAU,KAAK,CAAC;AAChB,mBAAW,KAAK,CAAC;AAAA,MACnB;AAAA,IACF,OAAO;AACL,gBAAU,KAAK,CAAC;AAChB,iBAAW,KAAK,CAAC;AACjB,YAAM;AAAA,IACR;AAGA,QAAI;AACJ,QAAI,WAAW;AACf,QAAI,SAAS;AAEb,QAAI,OAAO,QAAQ,YAAY,KAAK;AAClC,UAAI;AACF,cAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,mBAAW,OAAO;AAClB,mBAAW,OAAO;AAAA,MACpB,QAAQ;AACN,mBAAW;AAAA,MACb;AAAA,IACF,WAAW,eAAe,KAAK;AAC7B,iBAAW,IAAI;AACf,iBAAW,IAAI;AAAA,IACjB;AAEA,QAAI,SAAS;AACX,iBAAW,YAAa,QAAQ,YAAwB,QAAQ;AAChE,UAAI,QAAQ,KAAM,YAAW,QAAQ;AACrC,UAAI,QAAQ,OAAQ,UAAS,QAAQ;AAAA,IACvC;AAGA,QAAI,YAAY,SAAS,SAAS,GAAG,GAAG;AACtC,iBAAW,SAAS,MAAM,GAAG,EAAE,CAAC;AAAA,IAClC;AAGA,QAAI,YAAY,WAAW,QAAQ,GAAG;AACpC,aAAO,SAAS,MAAM,MAAM,IAAI;AAAA,IAClC;AAGA,UAAM,WAAW,WAAW,cAAc,QAAQ,IAAI;AACtD,QAAI,CAAC,YAAY,CAAC,SAAS;AACzB,aAAO,SAAS,MAAM,MAAM,IAAI;AAAA,IAClC;AAEA,UAAM,QAAQ,YAAY,IAAI;AAC9B,UAAM,UAAU,kBAAkB;AAGlC,UAAM,kBAAkB,CAAC,QAA8B;AACrD,YAAM,SAAmB,CAAC;AAC1B,UAAI,YAAY;AAEhB,UAAI,GAAG,QAAQ,CAAC,UAAkB;AAChC,YAAI,YAAY,kBAAkB;AAChC,iBAAO,KAAK,KAAK;AACjB,uBAAa,MAAM;AAAA,QACrB;AAAA,MACF,CAAC;AAED,UAAI,GAAG,OAAO,MAAM;AAClB,cAAM,YAAY,YAAY,IAAI,IAAI;AACtC,cAAM,SAAS,IAAI,cAAc;AACjC,cAAM,OAAO,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,EAAE,MAAM,GAAG,gBAAgB;AAE9E,cAAM,QAAiC;AAAA,UACrC,YAAY;AAAA,UACZ;AAAA,UACA,kBAAkB,kBAAkB,QAAQ;AAAA,UAC5C;AAAA,UACA,QAAQ,OAAO,YAAY;AAAA,UAC3B;AAAA,UACA,YAAY,KAAK,MAAM,YAAY,GAAG,IAAI;AAAA,UAC1C,IAAI,KAAK,IAAI,IAAI;AAAA,UACjB,GAAG;AAAA,QACL;AAEA,YAAI,UAAU,KAAK;AACjB,gBAAM,aAAa;AAAA,QACrB,WAAW,UAAU,OAAO,SAAS,KAAK;AACxC,gBAAM,SAAS,sBAAsB,IAAI;AACzC,cAAI,QAAQ;AACV,kBAAM,kBAAkB;AAAA,UAC1B;AAAA,QACF;AAEA,gBAAS,KAAK,KAAY;AAAA,MAC5B,CAAC;AAED,UAAI,SAAU,UAAS,GAAG;AAAA,IAC5B;AAGA,QAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,aAAa,KAAK;AACzD,UAAI,SAAS;AACX,eAAO,QAAQ,KAAK,CAAC,GAAG,SAAS,eAAe;AAAA,MAClD,OAAO;AACL,eAAO,QAAQ,KAAK,CAAC,GAAG,eAAe;AAAA,MACzC;AAAA,IACF,OAAO;AACL,aAAO,QAAQ,WAAW,CAAC,GAAG,eAAe;AAAA,IAC/C;AAAA,EACF;AACF;AAGO,SAAS,UAAU,QAA2B;AACnD,YAAU;AACV,EAAAD,MAAK,UAAU,YAAY,qBAAqB,OAAO;AACvD,EAAAA,MAAK,MAAM,YAAY,iBAAiB,OAAO;AAC/C,EAAAC,OAAM,UAAU,YAAY,sBAAsB,QAAQ;AAC1D,EAAAA,OAAM,MAAM,YAAY,kBAAkB,QAAQ;AACpD;AAGO,SAAS,cAAoB;AAClC,YAAU;AACV,EAAAD,MAAK,UAAU;AACf,EAAAA,MAAK,MAAM;AACX,EAAAC,OAAM,UAAU;AAChB,EAAAA,OAAM,MAAM;AACd;;;AMnMA,SAAS,eAAAC,oBAAmB;AAO5B,IAAMC,oBAAmB;AACzB,IAAM,wBAAwB;AAI9B,IAAIC,oBAAkC;AAE/B,SAASC,oBAAmB,MAAoB;AACrD,EAAAD,oBAAmB;AACrB;AAEA,IAAME,kBAAiB,oBAAI,IAAI,CAAC,aAAa,aAAa,WAAW,KAAK,CAAC;AAE3E,SAASC,YAAW,UAA2B;AAC7C,MAAID,gBAAe,IAAI,QAAQ,EAAG,QAAO;AACzC,MAAI,SAAS,SAAS,QAAQ,KAAK,SAAS,SAAS,WAAW,EAAG,QAAO;AAC1E,MAAI,aAAaF,kBAAkB,QAAO;AAC1C,SAAO;AACT;AAEA,IAAI,iBAAiD;AACrD,IAAII,WAA8B;AAM3B,SAAS,WAAW,QAA2B;AACpD,MAAI,OAAO,WAAW,UAAU,YAAa;AAE7C,mBAAiB,WAAW;AAC5B,EAAAA,WAAU;AAEV,aAAW,QAAQ,eAAe,aAChC,OACAC,OACmB;AAEnB,QAAI;AACJ,QAAI,SAAS;AAEb,QAAI;AACF,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAM,IAAI,IAAI,KAAK;AAAA,MACrB,WAAW,iBAAiB,KAAK;AAC/B,cAAM;AAAA,MACR,WAAW,OAAO,YAAY,eAAe,iBAAiB,SAAS;AACrE,cAAM,IAAI,IAAI,MAAM,GAAG;AACvB,iBAAS,MAAM;AAAA,MACjB,OAAO;AACL,eAAO,eAAgB,OAAOA,KAAI;AAAA,MACpC;AAAA,IACF,QAAQ;AACN,aAAO,eAAgB,OAAOA,KAAI;AAAA,IACpC;AAEA,QAAIA,OAAM,OAAQ,UAASA,MAAK;AAGhC,QAAIF,YAAW,IAAI,QAAQ,GAAG;AAC5B,aAAO,eAAgB,OAAOE,KAAI;AAAA,IACpC;AAEA,UAAM,WAAW,cAAc,IAAI,QAAQ;AAC3C,QAAI,CAAC,YAAY,CAACD,UAAS;AACzB,aAAO,eAAgB,OAAOC,KAAI;AAAA,IACpC;AAEA,UAAM,QAAQC,aAAY,IAAI;AAC9B,UAAM,UAAU,kBAAkB;AAElC,UAAM,WAAW,MAAM,eAAgB,OAAOD,KAAI;AAClD,UAAM,YAAYC,aAAY,IAAI,IAAI;AACtC,UAAM,SAAS,SAAS;AAGxB,QAAI,OAAO;AACX,QAAI;AACF,YAAM,QAAQ,SAAS,MAAM;AAC7B,YAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,QAC9B,MAAM,KAAK;AAAA,QACX,IAAI;AAAA,UAAgB,CAAC,GAAG,WACtB,WAAW,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC,GAAG,qBAAqB;AAAA,QACtE;AAAA,MACF,CAAC;AACD,aAAO,KAAK,MAAM,GAAGP,iBAAgB;AAAA,IACvC,QAAQ;AAAA,IAER;AAEA,UAAM,QAAiC;AAAA,MACrC,YAAY;AAAA,MACZ;AAAA,MACA,kBAAkB,kBAAkB,IAAI,QAAQ;AAAA,MAChD,UAAU,IAAI;AAAA,MACd,QAAQ,OAAO,YAAY;AAAA,MAC3B;AAAA,MACA,YAAY,KAAK,MAAM,YAAY,GAAG,IAAI;AAAA,MAC1C,IAAI,KAAK,IAAI,IAAI;AAAA,MACjB,GAAG;AAAA,IACL;AAEA,QAAI,UAAU,KAAK;AACjB,YAAM,aAAa;AAAA,IACrB,WAAW,UAAU,OAAO,SAAS,OAAO,MAAM;AAChD,YAAM,SAAS,sBAAsB,IAAI;AACzC,UAAI,QAAQ;AACV,cAAM,kBAAkB;AAAA,MAC1B;AAAA,IACF;AAEA,IAAAK,SAAQ,KAAK,KAAY;AACzB,WAAO;AAAA,EACT;AACF;AAGO,SAAS,eAAqB;AACnC,MAAI,gBAAgB;AAClB,eAAW,QAAQ;AACnB,qBAAiB;AAAA,EACnB;AACA,EAAAA,WAAU;AACZ;;;AChIA,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAErB,IAAM,kBAAN,MAAsB;AAAA;AAAA;AAAA;AAAA,EAI3B,iBAAiB,QAAwC;AAEvD,UAAM,WAAW,oBAAI,IAAuC;AAE5D,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,eAAe,WAAY;AACrC,YAAM,MAAO,MAA6C;AAG1D,UAAI,OAAO,SAAS,IAAI,GAAG;AAC3B,UAAI,CAAC,MAAM;AACT,eAAO,CAAC;AACR,iBAAS,IAAI,KAAK,IAAI;AAAA,MACxB;AACA,WAAK,KAAK,KAAK;AAAA,IACjB;AAEA,UAAM,iBAAkC,CAAC;AACzC,eAAW,CAAC,WAAW,aAAa,KAAK,UAAU;AACjD,YAAM,WAAW,KAAK,gBAAgB,WAAW,aAAa;AAC9D,UAAI,SAAU,gBAAe,KAAK,QAAQ;AAAA,IAC5C;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBACN,WACA,QACsB;AACtB,UAAM,aAAa,OAAO,OAAO,CAAC,MAAM,EAAE,eAAe,MAAM;AAG/D,UAAM,eAAe,OAAO;AAAA,MAC1B,CAAC,MAAM,EAAE,eAAe;AAAA,IAC1B;AAEA,QAAI,WAAW,WAAW,KAAK,aAAa,WAAW,EAAG,QAAO;AAEjE,UAAM,cAAc,WAAW;AAAA,MAC7B,CAAC,MAAM,OAAO,EAAE,WAAW,YAAY,EAAE,UAAU;AAAA,IACrD;AACA,UAAM,aAAa,WAAW;AAC9B,UAAM,aAAa,YAAY;AAC/B,UAAM,YAAY,aAAa,IAAI,aAAa,aAAa;AAC7D,UAAM,SAAS,YAAY;AAE3B,UAAM,iBAAiB,KAAK,uBAAuB,WAAW;AAC9D,UAAM,UAAU,KAAK,cAAc,YAAY,cAAc,UAAU;AAEvE,UAAM,iBAAiB;AAAA,MACrB,GAAG,IAAI;AAAA,QACL,YACG,IAAI,CAAC,MAAM,EAAE,QAAQ,EACrB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAAA,MACrD;AAAA,IACF,EAAE,KAAK;AAEP,UAAM,WAA0B;AAAA,MAC9B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,IAAI,KAAK,IAAI,IAAI;AAAA,IACnB;AAEA,QAAI,eAAe,SAAS,GAAG;AAC7B,eAAS,kBAAkB;AAAA,IAC7B;AACA,QAAI,WAAW;AACb,eAAS,uBAAuB;AAAA,IAClC;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,eAAS,WAAW,eAAe,CAAC;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,uBACN,aACU;AACV,UAAM,iBAA2B,CAAC;AAClC,UAAM,OAAO,oBAAI,IAAY;AAE7B,eAAW,SAAS,aAAa;AAC/B,YAAM,WAAW,MAAM,YAAY;AACnC,YAAM,WAAW,MAAM,oBAAoB;AAC3C,YAAM,YAAa,MAA6C,cAAwB;AACxF,YAAM,SAAS,MAAM,UAAU;AAE/B,UAAI;AACJ,UAAI,CAAC,WAAW;AACd,gBAAQ,GAAG,QAAQ,IAAI,QAAQ,UAAU,MAAM;AAAA,MACjD,OAAO;AACL,cAAM,UAAU,KAAK,qBAAqB,SAAS;AACnD,gBAAQ,GAAG,QAAQ,IAAI,QAAQ,KAAK,OAAO;AAAA,MAC7C;AAEA,cAAQ,MAAM,MAAM,GAAG,mBAAmB;AAC1C,UAAI,CAAC,KAAK,IAAI,KAAK,GAAG;AACpB,aAAK,IAAI,KAAK;AACd,uBAAe,KAAK,KAAK;AAAA,MAC3B;AAEA,UAAI,eAAe,UAAU,oBAAqB;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,WAA2B;AACtD,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,SAAS;AACjC,UAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,mBAAW,OAAO,CAAC,WAAW,SAAS,UAAU,eAAe,GAAG;AACjE,gBAAM,MAAM,KAAK,GAAG;AACpB,cAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,cAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,kBAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,gBAAI,IAAK,QAAO,OAAO,GAAG;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,UAAM,YAAY,UAAU,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;AAChD,WAAO,UAAU,MAAM,GAAG,mBAAmB;AAAA,EAC/C;AAAA,EAEQ,cACN,YACA,cACA,YACQ;AACR,UAAM,QAAkB,CAAC;AAEzB,UAAM,YAAY;AAAA,MAChB,GAAG,IAAI;AAAA,QACL,WACG,IAAI,CAAC,MAAM,EAAE,QAAQ,EACrB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAAA,MACrD;AAAA,IACF,EAAE,KAAK;AACP,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,KAAK,SAAS,UAAU,KAAK,IAAI,CAAC,EAAE;AAAA,IAC5C;AAEA,UAAM,QAAQ,aACX,OAAO,CAAC,MAAM,EAAE,gBAAgB,mBAAmB,EAAE,WAAW,EAChE,IAAI,CAAC,MAAO,EAAE,aAAoD,GAAG,EACrE,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS,CAAC;AACnE,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,KAAK,GAAG,MAAM,MAAM,gBAAgB;AAAA,IAC5C;AAEA,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,KAAK,GAAG,WAAW,MAAM,gBAAgB,UAAU,SAAS;AAAA,IACpE;AAEA,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,KAAK,GAAG,aAAa,MAAM,kBAAkB;AAAA,IACrD;AAEA,WAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,EAC/C;AACF;;;ACjLA,SAAS,eAAAG,oBAAmB;AAI5B,IAAM,kBAAkB;AAEjB,IAAM,sBAAN,MAA0B;AAAA,EACvB;AAAA,EACA;AAAA,EAER,YAAY,QAAqB,WAAoB;AACnD,SAAK,UAAU;AACf,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,KACE,SACA,UACG;AACH,UAAM,OAAO;AAEb,UAAM,UAAU,kBAEX,MACqB;AACxB,YAAM,QAAQA,aAAY,IAAI;AAC9B,UAAI;AACJ,UAAI;AAEJ,UAAI;AACF,iBAAS,MAAM,QAAQ,MAAM,MAAM,IAAI;AAAA,MACzC,SAAS,KAAK;AACZ,gBAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACvD,aAAK,WAAW,UAAU,MAAM,QAAW,OAAO,KAAK;AACvD,cAAM;AAAA,MACR;AAEA,WAAK,WAAW,UAAU,MAAM,QAAQ,QAAW,KAAK;AACxD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WACE,UACA,MACA,QACA,OACA,WACM;AACN,UAAM,QAAqB;AAAA,MACzB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,aAAa;AAAA,MACb,YAAY,aAAa;AAAA,MACzB,IAAI,KAAK,IAAI,IAAI;AAAA,IACnB;AAEA,QAAI,OAAO;AACT,YAAM,gBAAgB,EAAE,MAAM;AAC9B,YAAM,SAAS;AAAA,IACjB,WAAW,WAAW,QAAW;AAC/B,YAAM,aAAa,KAAK,UAAU,MAAM;AACxC,YAAM,gBAAgB;AAAA,QACpB,OACE,WAAW,SAAS,kBAChB,WAAW,MAAM,GAAG,eAAe,IACnC;AAAA,MACR;AAAA,IACF;AAEA,QAAI,KAAK,YAAY;AACnB,YAAM,uBAAuB,KAAK;AAAA,IACpC;AAEA,SAAK,QAAQ,KAAK,KAAK;AAAA,EACzB;AAAA,EAEQ,WACN,UACA,MACA,QACA,OACA,WACM;AACN,UAAM,YAAYA,aAAY,IAAI,IAAI;AAGtC,QAAI,UAAmC,CAAC;AACxC,QAAI,KAAK,WAAW,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AACxE,gBAAU,KAAK,CAAC;AAAA,IAClB,WAAW,KAAK,SAAS,GAAG;AAC1B,gBAAU,EAAE,KAAK;AAAA,IACnB;AAEA,SAAK,WAAW,UAAU,SAAS,QAAQ,OAAO,SAAS;AAAA,EAC7D;AACF;;;ACpFA,IAAI,eAAe;AACnB,IAAIC,WAA8B;AAClC,IAAI,cAAc;AAKX,SAAS,KAAK,QAA4B;AAC/C,MAAI,aAAc;AAElB,MAAI,CAAC,OAAO,OAAO,WAAW,UAAU,GAAG;AACzC,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,YAAY;AACpC,gBAAc,OAAO,cAAc;AACnC,QAAM,YAAY,OAAO,aAAa;AAEtC,EAAAA,WAAU,IAAI;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,MAAI;AACF,UAAM,cAAc,IAAI,IAAI,QAAQ;AACpC,uBAAuB,YAAY,QAAQ;AAC3C,IAAAC,oBAAwB,YAAY,QAAQ;AAAA,EAC9C,QAAQ;AAAA,EAER;AAEA,YAAUD,QAAO;AACjB,aAAWA,QAAO;AAClB,iBAAe;AACjB;AAKA,eAAsB,WAA0B;AAC9C,MAAI,CAAC,gBAAgB,CAACA,SAAS;AAE/B,cAAY;AACZ,eAAa;AAEb,MAAI,aAAa;AACf,UAAM,WAAW,IAAI,gBAAgB;AACrC,UAAM,YAAYA,SAAQ,KAAK;AAC/B,UAAM,iBAAiB,SAAS,iBAAiB,SAAS;AAC1D,eAAW,SAAS,gBAAgB;AAClC,MAAAA,SAAQ,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAMA,SAAQ,SAAS;AACvB,EAAAA,WAAU;AACV,iBAAe;AACjB;AAKO,SAAS,OAAO,SAMd;AACP,MAAI,CAACA,UAAS;AACZ,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AAEA,QAAM,YAAY,YAAY;AAC9B,QAAM,QAAuB;AAAA,IAC3B,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,QAAQ,QAAQ,UAAU;AAAA,IAC1B,SAAS,QAAQ,WAAW;AAAA,IAC5B,IAAI,KAAK,IAAI,IAAI;AAAA,IACjB,YAAY,UAAU,cAAc;AAAA,IACpC,eAAe,UAAU,iBAAiB;AAAA,EAC5C;AAEA,MAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,UAAM,kBAAkB,QAAQ;AAAA,EAClC;AACA,MAAI,QAAQ,UAAU;AACpB,UAAM,WAAW,QAAQ;AAAA,EAC3B;AACA,MAAI,QAAQ,WAAW;AACrB,UAAM,uBAAuB,QAAQ;AAAA,EACvC;AAEA,EAAAA,SAAQ,KAAK,KAAK;AACpB;AAKO,SAAS,YAAgC;AAC9C,SAAOA;AACT;","names":["originalHttpsRequest","originalHttpRequest","http","https","http","https","performance","MAX_BODY_CAPTURE","_sdkEndpointHost","setSdkEndpointHost","EXCLUDED_HOSTS","isExcluded","_buffer","init","performance","performance","_buffer","setSdkEndpointHost"]}
|
|
@@ -882,7 +882,7 @@ function init(config) {
|
|
|
882
882
|
"Invalid API key: must start with 'cnry_sk_'. Get your key at https://app.canary.dev"
|
|
883
883
|
);
|
|
884
884
|
}
|
|
885
|
-
const endpoint = _nullishCoalesce(config.endpoint, () => ( "
|
|
885
|
+
const endpoint = _nullishCoalesce(config.endpoint, () => ( "https://canary-production-89d8.up.railway.app"));
|
|
886
886
|
_autoReport = _nullishCoalesce(config.autoReport, () => ( true));
|
|
887
887
|
const autoFlush = _nullishCoalesce(config.autoFlush, () => ( true));
|
|
888
888
|
_buffer3 = new EventBuffer(
|
|
@@ -967,4 +967,4 @@ function getBuffer() {
|
|
|
967
967
|
|
|
968
968
|
|
|
969
969
|
exports.EventBuffer = EventBuffer; exports.COMMON_API_DOMAINS = COMMON_API_DOMAINS; exports.matchProvider = matchProvider; exports.resolveProvider = resolveProvider; exports.normalizeEndpoint = normalizeEndpoint; exports.extractResponseFields = extractResponseFields; exports.detectAgent = detectAgent; exports.resetAgentCache = resetAgentCache; exports.getSessionSignals = getSessionSignals; exports.setFrameworkSession = setFrameworkSession; exports.clearFrameworkSession = clearFrameworkSession; exports.runWithSession = runWithSession; exports.SessionReporter = SessionReporter; exports.CanaryMCPMiddleware = CanaryMCPMiddleware; exports.init = init; exports.shutdown = shutdown; exports.survey = survey; exports.getBuffer = getBuffer;
|
|
970
|
-
//# sourceMappingURL=chunk-
|
|
970
|
+
//# sourceMappingURL=chunk-DRKEGQ7U.cjs.map
|