@morsehq-dev/sdk 0.4.0-rc.2 → 0.4.0

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.
@@ -22,7 +22,7 @@ chunkO4HG3OSK_cjs.init_otel_provider_browser();
22
22
  chunkO4HG3OSK_cjs.init_redaction();
23
23
 
24
24
  // src/_internal/transport.ts
25
- var SDK_VERSION = "0.4.0-rc.2" ;
25
+ var SDK_VERSION = "0.4.0" ;
26
26
  var RETRY_AFTER_CAP_SECONDS = 30;
27
27
  function parseRetryAfter(value) {
28
28
  if (!value) return null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/_internal/auto-detect.browser.ts","../../src/index.ts","../../src/_internal/db-capture.browser.ts","../../src/_internal/transport.ts"],"names":["init_env","init_otel_provider_browser","init_redaction","report","init_tracing","init_spans","init_propagation","getEndpoint","DEFAULT_ENDPOINT","isDisabled","getApiKey","getFlushIntervalSeconds","setTransport","setRedactionConfig","isValidInternalSource","getInternalSource","setSource","getEngine","getSampleRate","getMaxQueueSize","getMaxBatchSize","getMaxFieldBytes","buildRedactionConfig","_setEnsureInitialized","absorbErrorsSync","redactValue","run","generateTraceId","nowIso","span","generateSpanId","SpanType","buildTraceEntry","sendTrace","runSync","runAsync","setContext","startSpan","spanSync","spanAsync","absorbErrorsAsync","quotaState","_resetForTesting","getLastTrace","getActiveTraceState"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Browser replacement for `auto-detect.ts` (MHQ-482 follow-up).\n *\n * `auto-detect.ts` already no-ops at RUNTIME outside Node — its own\n * docstring says so — but it reaches for `node:module`'s `createRequire` /\n * `Module` in a static top-level import, so a bundler still had to resolve\n * `module` for the browser graph. The runtime guard was never the problem;\n * the import was.\n *\n * Nothing here is a behaviour change: zero-config adapter auto-detect works\n * by hooking `require()` to patch LLM SDKs as they load, and a browser has\n * no `require` to hook. See `otel-provider.browser.ts` for the full\n * rationale and the build wiring.\n */\n\n/** No-op: there is no `require` to hook in a browser. */\nexport async function installAdapters(): Promise<void> {}\n\n/** No state to reset in the browser build. */\nexport function _resetForTesting(): void {}\n","/**\n * `@morsehq-dev/sdk` — TypeScript SDK for Morse observability.\n *\n * V2 module-level singleton API:\n *\n * ```ts\n * import * as morse from \"@morsehq-dev/sdk\";\n *\n * // Three-step zero-setup integration:\n * // 1. Generate an API key in the Morse dashboard.\n * // 2. npm install @morsehq-dev/sdk\n * // 3. Set MORSE_API_KEY in your environment.\n *\n * morse.track({ agentId: \"lead-qualifier\", runData: { success: true } });\n *\n * // Or call init() explicitly to override env-var resolution:\n * morse.init({\n * apiKey: \"mhq_xxx\", // pragma: allowlist secret — placeholder, not a key\n * endpoint: \"https://api.morsehq.dev/api/v1/traces/batch\",\n * flushIntervalSeconds: 2,\n * });\n * ```\n *\n * Non-invasive guarantee: every public function is wrapped with\n * `absorbErrors`. Telemetry never throws back at the caller; internal\n * errors are reported out-of-band to Morse's Sentry instance.\n *\n * The internal `BatchTransport` and `TraceState` types back the singleton\n * but are NOT part of the public API. Customers should never reach into\n * the `_internal` directory.\n */\n\nimport {\n installAdapters as _installAdapters,\n _resetForTesting as _resetAutoDetectForTesting,\n} from \"./_internal/auto-detect\";\nimport { absorbErrorsAsync, absorbErrorsSync } from \"./_internal/errors\";\nimport {\n DEFAULT_ENDPOINT,\n getApiKey as readApiKeyFromEnv,\n getEndpoint as readEndpointFromEnv,\n getFlushIntervalSeconds,\n getInternalSource as readInternalSourceFromEnv,\n getMaxBatchSize,\n getMaxFieldBytes,\n getMaxQueueSize,\n getSampleRate,\n isDisabled as readIsDisabled,\n isValidInternalSource,\n} from \"./_internal/env\";\nimport { maybeEnableDbCaptureFromEnv } from \"./_internal/db-capture\";\nimport { getEngine as getOtelEngine } from \"./_internal/otel-provider\";\nimport {\n buildRedactionConfig,\n type RedactionConfig,\n type RedactionOptions,\n redactValue,\n} from \"./_internal/redaction\";\nimport {\n BatchTransport,\n SDK_VERSION,\n type TelemetryEvent,\n getQuotaState as _getQuotaState,\n _resetQuotaStateForTesting,\n type QuotaState,\n} from \"./_internal/transport\";\nimport {\n getActiveTraceState as _getActiveTraceState,\n getLastTrace as _getLastTrace,\n runAsync as _runAsync,\n runSync as _runSync,\n sendTrace as _sendTrace,\n setContext as _setContext,\n setRedactionConfig,\n setSource as _setSource,\n setTransport,\n startSpan as _startSpan,\n type RunHandle,\n type RunOptions,\n type SpanHandle,\n type SpanOptions,\n spanAsync as _spanAsync,\n spanSync as _spanSync,\n _resetForTesting as _resetTracingForTesting,\n} from \"./_internal/tracing\";\nimport { report, _resetForTesting as _resetSentryForTesting } from \"./_internal/sentry-reporter\";\nimport { _setEnsureInitialized as _setLogEnsureInitialized } from \"./log\";\nimport {\n buildTraceEntry,\n generateSpanId,\n generateTraceId,\n nowIso,\n SpanType,\n type SpanData,\n type TraceFields,\n} from \"./_internal/spans\";\n\nexport type { SpanData, SpanType, SpanStatus, ContextSegment } from \"./_internal/spans\";\nexport type { RunHandle, RunOptions, SpanHandle, SpanOptions };\nexport type { QuotaState };\nexport type { RedactionOptions, RedactionConfig } from \"./_internal/redaction\";\nexport {\n extractTraceparent,\n formatTraceparent,\n injectTraceparent,\n injectIntoRequestInit,\n W3C_TRACEPARENT_HEADER,\n} from \"./propagation\";\nexport {\n extractAnthropicSegments,\n instrumentAnthropic,\n isAnthropicWrapped,\n uninstallAnthropic,\n wrapAnthropic,\n MAX_SEGMENT_CONTENT_BYTES,\n} from \"./anthropic\";\nexport type {\n AnthropicClientLike,\n AnthropicContentBlockParam,\n AnthropicMessageCreateParams,\n AnthropicMessageParam,\n AnthropicMessageResponse,\n AnthropicTextBlockParam,\n AnthropicToolParam,\n AnthropicToolResultBlockParam,\n AnthropicToolUseBlockParam,\n AnthropicUsage,\n InstrumentAnthropicOptions,\n} from \"./anthropic\";\nexport { log } from \"./log\";\nexport type { LogOptions, LogSeverity } from \"./log\";\n\nexport const version = SDK_VERSION;\n\n/* ------------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------------ */\n\n/** Options accepted by `init()`. Mirrors Python's `init(...)` kwargs. */\nexport interface InitOptions {\n /** API key (starts with \"mhq_\"). Required if no `MORSE_API_KEY` env var. */\n apiKey?: string;\n /** Full ingestion URL. Defaults to the production Railway endpoint. */\n endpoint?: string;\n /** Seconds between batch flushes. Default: 2. */\n flushIntervalSeconds?: number;\n /**\n * Client-side PII redaction. Default-on with the canonical eight-pattern\n * catalogue. Set `enabled: false` to opt out (the server-side mandatory\n * floor still applies). Mandatory patterns (`api_key_prefixed`, `jwt`,\n * `credit_card`, `ssn_us`) cannot be added to `disabledPatterns` —\n * `init()` throws on that.\n */\n redaction?: RedactionOptions;\n /**\n * INTERNAL USE ONLY — resource-level source label for Morse's own\n * dogfood emitters. Mirrors the Python SDK's `init(source=...)` kwarg\n * (PR #90). Accepted values: `\"copilot\" | \"investigator\" | \"refagent\"\n * | \"ui-bot\"`. Any other value (including misspellings) is silently\n * dropped — falls back to the `MORSE_INTERNAL_SOURCE` env var.\n * Customers should not set this.\n */\n source?: string;\n}\n\n/** Single agent-run payload accepted by `track()`. */\nexport interface TrackOptions {\n /** User-defined agent name (e.g., \"lead-qualifier\"). */\n agentId: string;\n /** Run-data dict. Shape mirrors backend Run model. */\n runData: Record<string, unknown>;\n}\n\n/** High-level outcome shape accepted by `record()` (V1 ergonomic API). */\nexport interface RunRecord {\n agent: string;\n success?: boolean;\n outcome?: string;\n cost?: number;\n model?: string;\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n durationMs?: number;\n errorMessage?: string;\n metadata?: Record<string, unknown>;\n status?: \"completed\" | \"failed\" | \"timed_out\";\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level singleton state */\n/* ------------------------------------------------------------------------ */\n\ninterface SingletonState {\n apiKey: string;\n endpoint: string;\n transport: BatchTransport;\n redaction: RedactionConfig;\n}\n\nlet singleton: SingletonState | null = null;\nlet transportFactory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n}) => BatchTransport = (opts) => new BatchTransport(opts);\n\nlet noApiKeyWarningEmitted = false;\nlet lastLoggedEndpoint: string | null = null;\n\nfunction logger(level: \"info\" | \"warn\", message: string): void {\n const out = level === \"warn\" ? console.warn : console.info;\n out.call(console, message);\n}\n\nfunction logDestinationOnce(endpoint: string): void {\n if (endpoint === lastLoggedEndpoint) return;\n lastLoggedEndpoint = endpoint;\n logger(\n \"info\",\n `morse: Morse SDK initialized — sending to ${endpoint} (sdk version ${SDK_VERSION})`,\n );\n}\n\nfunction resolveEndpoint(explicit?: string): string {\n if (explicit && explicit.length > 0) return explicit;\n const fromEnv = readEndpointFromEnv();\n if (fromEnv) return fromEnv;\n return DEFAULT_ENDPOINT;\n}\n\nconst OTLP_TRACES_PATH = \"/api/v1/otlp/v1/traces\";\n\n/**\n * Derive the OTLP ingest URL from the legacy JSON traces batch URL. Keeps\n * host/scheme; replaces the path — mirrors the Python SDK's\n * `client.py::_derive_otlp_endpoint` and this file's own\n * `transport.ts::deriveLogsEndpoint`.\n */\nfunction deriveOtlpEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = OTLP_TRACES_PATH;\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, OTLP_TRACES_PATH);\n }\n}\n\nfunction buildSingleton(opts: InitOptions, redaction: RedactionConfig): SingletonState | null {\n if (readIsDisabled()) {\n return null;\n }\n const apiKey = opts.apiKey ?? readApiKeyFromEnv();\n if (!apiKey) {\n if (!noApiKeyWarningEmitted) {\n logger(\n \"warn\",\n \"morse: not initialized — set MORSE_API_KEY or call morse.init({ apiKey }) (this warning prints once per process; SDK calls are silent no-ops)\",\n );\n noApiKeyWarningEmitted = true;\n }\n return null;\n }\n const endpoint = resolveEndpoint(opts.endpoint);\n const flushIntervalSeconds = opts.flushIntervalSeconds ?? getFlushIntervalSeconds();\n const transport = transportFactory({ apiKey, endpoint, flushIntervalSeconds });\n setTransport(transport);\n setRedactionConfig(redaction);\n // Resolve the internal source label: explicit kwarg wins, then env.\n // Invalid values are silently dropped so a typo never tags customer\n // data as dogfood.\n const explicitSource = isValidInternalSource(opts.source) ? opts.source! : undefined;\n const resolvedSource = explicitSource ?? readInternalSourceFromEnv() ?? null;\n _setSource(resolvedSource);\n logDestinationOnce(endpoint);\n\n // MHQ-482: run()/span()/runTraced()'s real emission pipeline — real OTel\n // spans over OTLP protobuf+gzip. Configured unconditionally (even under\n // MORSE_LEGACY_TRANSPORT=1) so the engine is ready if the flag flips at\n // runtime; the flag only affects which pipeline `tracing.ts::sendTrace()`\n // feeds. Mirrors the Python SDK's `_Client.__init__`.\n try {\n getOtelEngine().configure({\n endpoint: deriveOtlpEndpoint(endpoint),\n apiKey,\n sampleRate: getSampleRate(),\n scheduleDelayMillis: flushIntervalSeconds * 1000,\n maxQueueSize: getMaxQueueSize(),\n maxExportBatchSize: getMaxBatchSize(),\n maxFieldBytes: getMaxFieldBytes(),\n // 10s (not the exporter's un-overridden 30s ceiling): a genuine\n // network blip still gets several retries with backoff; a fully dead\n // endpoint fails within a bounded, shutdown-friendly window instead\n // of the library's full 30s ceiling. Mirrors the Python SDK.\n timeoutMillis: 10_000,\n redactionConfig: redaction,\n });\n } catch (err) {\n report(err, { fingerprint: \"morse.otel_provider.configure\" });\n }\n\n // MHQ-482: zero-config adapter auto-detect (mirrors Python's\n // `auto_detect.install_adapters()` call in `_ensure_initialized` /\n // `_init_impl`). Fire-and-forget — telemetry setup must never block or\n // throw back into the customer's init() / first SDK call. Runs AFTER the\n // OTel engine above is configured so any span an auto-installed adapter\n // emits immediately has a working pipeline to flow through.\n void _installAdapters().catch(() => {\n // installAdapters() already absorbs adapter-level failures; this is a\n // defensive backstop so a totally unexpected rejection never surfaces.\n });\n\n // MHQ-482: DB auto-capture (opt-in — MORSE_CAPTURE_DB / MORSE_CAPTURE_INFRA,\n // mirrors the Python SDK's `infra_capture.py::maybe_enable_from_env`).\n // MUST run after `getOtelEngine().configure()` above — instrumentation\n // registration needs the `NodeTracerProvider` that call just built.\n try {\n maybeEnableDbCaptureFromEnv();\n } catch (err) {\n report(err, { fingerprint: \"morse.db_capture.register\" });\n }\n\n return { apiKey, endpoint, transport, redaction };\n}\n\nfunction ensureInitialized(): SingletonState | null {\n if (readIsDisabled()) return null;\n if (singleton !== null) return singleton;\n // Lazy default-init: defaults are safe (default-on redaction).\n singleton = buildSingleton({}, buildRedactionConfig());\n return singleton;\n}\n\n// Wire the lazy-init hook into log.ts so `log()` participates in the\n// same env-driven auto-init flow as `track()` / `record()`. Discards\n// the return value — log.ts only needs the side-effect of populating\n// the module-level singleton + transport.\n_setLogEnsureInitialized(() => {\n ensureInitialized();\n});\n\n/* ------------------------------------------------------------------------ */\n/* Public API — wrapped at the module surface */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Initialize the Morse SDK singleton. Call once at application\n * startup. After this call, `track()`, `batchTrack()`, `run()`, and\n * `flush()` are wired up to the configured endpoint.\n *\n * `endpoint` is optional — when omitted, the SDK resolves in this order:\n * 1. `MORSE_ENDPOINT` env var (if set)\n * 2. The default Railway production URL.\n *\n * `MORSE_DISABLED=1` overrides everything: `init()` is a no-op.\n *\n * Calling `init()` a second time replaces the singleton — the previous\n * transport is shut down cleanly and a new one is constructed.\n *\n * Validation errors (e.g., disabling a mandatory redaction pattern, an\n * uncompilable extra pattern) are deliberately NOT absorbed — they\n * propagate as `Error`s so the developer sees the misconfiguration at\n * application startup. All other errors raised from internal SDK paths\n * are absorbed via the per-call wrappers downstream.\n */\nexport function init(options: InitOptions = {}): void {\n if (readIsDisabled()) {\n logger(\"info\", \"morse: MORSE_DISABLED=1 — SDK is a no-op\");\n return;\n }\n // Validate redaction options FIRST — must throw before we mutate any\n // singleton state, so a misconfigured init leaves the previous client\n // intact and the spec scenario \"no Client is constructed\" holds.\n const redaction = buildRedactionConfig(options.redaction);\n\n try {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n }\n singleton = buildSingleton(options, redaction);\n } catch (err) {\n report(err, { fingerprint: \"morse.init\" });\n }\n}\n\n/**\n * Submit a single agent run to the ingestion endpoint. Non-blocking;\n * returns immediately. Silently no-ops if the SDK isn't initialised.\n */\nexport const track = absorbErrorsSync(\n (options: TrackOptions): void => {\n const state = ensureInitialized();\n if (!state) return;\n const payload = {\n agent_id: options.agentId,\n ...options.runData,\n };\n const redacted = redactValue(payload, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n },\n { fingerprint: \"morse.track\" },\n);\n\n/** Submit multiple agent runs in bulk. Silently no-ops if uninitialised. */\nexport const batchTrack = absorbErrorsSync(\n (runs: Array<Record<string, unknown>>): void => {\n const state = ensureInitialized();\n if (!state) return;\n for (const run of runs) {\n const redacted = redactValue(run, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n }\n },\n { fingerprint: \"morse.batch_track\" },\n);\n\n/**\n * High-level convenience: record an agent outcome with named fields.\n *\n * Builds a synthetic single-span `agent`-type trace and submits it through\n * the same `traces`-shaped emission path `run()`/`span()` use (MHQ-746) —\n * mirrors Python's `client.py::record()`. The live dev-api backend has no\n * `runs` ingest route; sending through `state.transport.track()` (the\n * shape `track()`/`batchTrack()` still use, matching Python's equally\n * unmigrated `track()`/`batch_track()`) 422s silently. See MHQ-746.\n */\nexport const record = absorbErrorsSync(\n (rec: RunRecord): void => {\n const state = ensureInitialized();\n if (!state) return;\n\n const traceId = generateTraceId();\n const now = nowIso();\n const resolvedStatus = rec.errorMessage ? \"failed\" : (rec.status ?? \"completed\");\n\n const span: SpanData = {\n trace_id: traceId,\n span_id: generateSpanId(),\n type: SpanType.AGENT,\n name: rec.agent,\n started_at: now,\n ended_at: now,\n status: resolvedStatus,\n duration_ms: rec.durationMs,\n model: rec.model,\n input_tokens: rec.inputTokens,\n output_tokens: rec.outputTokens,\n total_tokens: rec.totalTokens,\n cost_usd: rec.cost,\n error_message: rec.errorMessage,\n metadata: rec.metadata,\n };\n\n const traceFields: TraceFields = {\n trace_id: traceId,\n agent: rec.agent,\n status: resolvedStatus,\n started_at: now,\n ended_at: now,\n duration_ms: rec.durationMs,\n error_message: rec.errorMessage,\n success: rec.success,\n outcome: rec.outcome,\n };\n\n const entry = buildTraceEntry(traceFields, [span]);\n const redacted = redactValue(entry, state.redaction) as {\n trace: Record<string, unknown>;\n spans: Record<string, unknown>[];\n };\n _sendTrace(redacted);\n },\n { fingerprint: \"morse.record\" },\n);\n\n/**\n * Open a traced agent run, executing `fn` inside the trace context.\n * Mirrors Python's `morse.run(...)` context manager.\n *\n * Two overloads are supported:\n * - `run(name, fn)` — quick form with default options.\n * - `run({ agentName, incomingTraceparent }, fn)` — full options.\n *\n * Async callers receive a Promise; sync callers receive the value\n * directly. The function's return value is forwarded to the caller.\n */\nexport function run<T>(name: string, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(options: RunOptions, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(\n nameOrOpts: string | RunOptions,\n fn: (handle: RunHandle) => T,\n): T | undefined {\n const opts = typeof nameOrOpts === \"string\" ? { agentName: nameOrOpts } : nameOrOpts;\n // Make sure the SDK is initialised so spans get sent. Failure to init\n // is fine — `_runSync` records to `lastTrace` regardless, useful for tests.\n ensureInitialized();\n // We can't reliably detect async vs sync up front in TypeScript, so we\n // route through `_runSync` and, if the user's `fn` returns a promise,\n // the wrapping trace ends synchronously after the promise is created.\n // For correct async lifecycle, callers should use `runTraced` instead\n // (it awaits the promise inside the `als.run` scope so the trace\n // closes when the promise resolves).\n return _runSync(opts, fn);\n}\n\n/** Async variant of `run` — exported for callers who want explicit typing. */\nexport function runTraced<T>(\n options: RunOptions,\n fn: (handle: RunHandle) => Promise<T>,\n): Promise<T | undefined> {\n ensureInitialized();\n return _runAsync(options, fn);\n}\n\n/**\n * Attach cost-attribution dimensions to the active trace (MHQ-751). Call\n * once per request (e.g. in middleware) to propagate `customerId`,\n * `feature`, `env`, `team`, etc. to all child spans downstream. Values are\n * coerced to strings. No-op outside an active trace — never throws.\n * Mirrors Python's `morse_ai.set_context(**dims)`.\n */\nexport function setContext(dims: Record<string, unknown>): void {\n _setContext(dims);\n}\n\n/** Start a child span under the active trace. Returns null if no trace. */\nexport function startSpan(options: SpanOptions): SpanHandle | null {\n return _startSpan(options);\n}\n\n/** Run `fn` inside a child span. No-op if no trace is active. */\nexport function span<T>(options: SpanOptions, fn: (handle: SpanHandle | null) => T): T {\n return _spanSync(options, fn);\n}\n\n/** Async variant of `span`. */\nexport function spanAsync<T>(\n options: SpanOptions,\n fn: (handle: SpanHandle | null) => Promise<T>,\n): Promise<T> {\n return _spanAsync(options, fn);\n}\n\n/** Block until all queued events have been sent. Silent on uninit / errors. */\nexport const flush = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.flush();\n await getOtelEngine().flush();\n }\n },\n { fingerprint: \"morse.flush\" },\n);\n\n/**\n * Latest ingest quota / rate-limit state observed by the SDK, or `null`.\n *\n * TQE-04 (MHQ-676/MHQ-749): when the Morse backend answers a telemetry\n * batch with HTTP 429 — the monthly event limit (`event_limit_exceeded`)\n * or the per-second ingest rate limit (`RATE_LIMIT_INGEST`) — the\n * transport parses the response and stores it. This function exposes\n * that state so host applications can alert on quota exhaustion instead\n * of discovering it from missing telemetry. Mirrors Python's\n * `morse_ai.quota_state()`.\n *\n * Returns `null` when the SDK is uninitialized or no 429 has been\n * observed. The object includes at least `kind` (`\"event_limit\"`,\n * `\"rate_limit\"`, or `\"unknown\"`), `status`, and `receivedAt` (epoch\n * seconds); event-limit states add `currentCount` / `limitCount` /\n * `planId` / `periodStart` / `upgradeUrl`.\n */\nexport const quotaState = absorbErrorsSync(\n (): QuotaState | null => {\n if (singleton === null) return null;\n return _getQuotaState();\n },\n { fingerprint: \"morse.quota_state\" },\n);\n\n/** Flush and stop the background transport. Call on app exit. */\nexport const shutdown = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.shutdown();\n await getOtelEngine().flush();\n getOtelEngine().shutdown();\n singleton = null;\n setTransport(null);\n }\n },\n { fingerprint: \"morse.shutdown\" },\n);\n\n/* ------------------------------------------------------------------------ */\n/* Test-only hooks (private) */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Reset the module-level singleton for tests. Customer code must NOT\n * call this — the singleton is meant to live for the process lifetime.\n *\n * @internal\n */\nexport function _resetForTesting(): void {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n setTransport(null);\n }\n getOtelEngine().resetForTesting();\n noApiKeyWarningEmitted = false;\n lastLoggedEndpoint = null;\n _setSource(null);\n _resetTracingForTesting();\n _resetSentryForTesting();\n _resetAutoDetectForTesting();\n _resetQuotaStateForTesting();\n // Reset the transport factory back to the default.\n transportFactory = (opts) => new BatchTransport(opts);\n}\n\n/**\n * Inject an alternate transport factory for tests (e.g., a mock that\n * captures sent payloads instead of opening a fetch).\n *\n * @internal\n */\nexport function _setTransportFactoryForTesting(\n factory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n }) => BatchTransport,\n): void {\n transportFactory = factory;\n}\n\n/**\n * Read the most recently completed trace entry, for test assertions.\n *\n * @internal\n */\nexport function _getLastTraceForTesting(): { trace: unknown; spans: unknown[] } | null {\n return _getLastTrace();\n}\n\n/**\n * Read the active trace state, for test assertions.\n *\n * @internal\n */\nexport function _getActiveTraceStateForTesting(): unknown {\n return _getActiveTraceState();\n}\n","/**\n * Browser replacement for `db-capture.ts` (MHQ-482 follow-up).\n *\n * `db-capture.ts` statically imports `@opentelemetry/instrumentation-pg`,\n * `-mysql2` and `@opentelemetry/instrumentation` (which reaches for node's\n * `module` via require-in-the-middle). A browser has no pg/mysql2 driver to\n * patch, so the whole module is inert there — but the static imports still\n * dragged those packages, and node `module`, into the browser graph.\n *\n * Swapped in by the esbuild plugin in `tsup.config.ts`; see\n * `otel-provider.browser.ts` for the full rationale. Only the symbols\n * `index.ts` imports need to exist here.\n */\n\n/**\n * No-op: there is no database driver to instrument in a browser.\n *\n * Deliberately silent. `otel-provider.browser.ts::noteBrowserRuntime()`\n * already states that DB capture is Node-only, and it fires on\n * `configure()` — which `index.ts` always calls before this. A second\n * message would be noise, and this function runs even when the customer\n * never asked for DB capture.\n */\nexport function maybeEnableDbCaptureFromEnv(): void {}\n","/**\n * Background batched HTTP transport.\n *\n * Mirrors the Python SDK's `transport.py` semantics:\n *\n * - Async batching: 2-second flush interval (configurable), 100-event\n * max batch size (configurable). Whichever fires first triggers a\n * flush.\n * - Network failures are silently dropped with a debug log — never\n * thrown to the caller.\n * - The caller's thread / event loop is never blocked: `track()`\n * enqueues and returns immediately.\n *\n * The transport speaks two payload shapes (matching Python):\n *\n * - V2 trace events: `{ traces: [{ trace, spans }, ...] }`\n * - V1 legacy runs: `{ runs: [{ ... }, ...] }`\n *\n * Wire-format routing checks for a `traces` key on each enqueued event.\n *\n * Browser vs Node:\n * - Both runtimes ship `fetch` (Node 18+ / all evergreens).\n * - In Node we can run a `setInterval` and call it a day.\n * - In the browser the same approach works; we don't currently flush\n * on `pagehide` / `beforeunload` — Phase 6c spec doesn't require it.\n * A future change can add `navigator.sendBeacon` for last-gasp delivery.\n */\n\nimport { report } from \"./sentry-reporter\";\n\n// Injected at build time by tsup from package.json — see tsup.config.ts.\n// The version is declared in exactly one place (package.json); a copy here\n// would drift the moment a release bumped one and not the other, and the\n// stale value rides out on the User-Agent and on every span's `sdk_version`.\n// `__SDK_VERSION__` is undefined when running straight from source (vitest,\n// ts-node), hence the fallback.\ndeclare const __SDK_VERSION__: string | undefined;\n\nexport const SDK_VERSION = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\nexport interface BatchTransportOptions {\n endpoint: string;\n apiKey: string;\n /** Max wait between flushes, in seconds. Default: 2.0. */\n flushIntervalSeconds?: number;\n /** Max events per batch. Default: 100. */\n maxBatchSize?: number;\n /** Max queued events before drop. Default: 10_000. */\n maxQueueSize?: number;\n /** Timeout for the underlying fetch, in ms. Default: 5_000. */\n fetchTimeoutMs?: number;\n /**\n * Pluggable fetch implementation. Defaults to `globalThis.fetch`.\n * Useful for tests.\n */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Wire shape for a single log record. Mirrors the Python SDK payload at\n * `apps/sdk/python/src/morse/client.py:191-211` and the backend\n * `SimpleLogsIngestController.ingest` contract\n * (`apps/api/apps/logs/controllers.py:66-134`).\n */\nexport interface LogRecordPayload {\n timestamp: string;\n severity: string;\n service_name: string;\n body: string;\n attributes: Record<string, unknown>;\n trace_id: string | null;\n span_id: string | null;\n}\n\nexport type TelemetryEvent =\n | { traces: unknown[] }\n | { logs: LogRecordPayload[] }\n | Record<string, unknown>;\n\n/**\n * Quota / rate-limit state parsed from a 429 batch-POST response.\n *\n * Mirrors Python's `transport.py::BatchTransport._record_quota_state` /\n * `quota_state()` (TQE-04 / MHQ-676) field-for-field, camelCased per this\n * codebase's convention (`received_at` -> `receivedAt`, etc.). Two known\n * body shapes:\n *\n * - `event_limit_exceeded` (traces controllers) — flat body with\n * `current_count` / `limit_count` / `plan_id` / `period_start` /\n * `upgrade_url` / `sample_rate_url`.\n * - `RATE_LIMIT_INGEST` (ingest rate-limit middleware) — nested\n * `{ error: { code, message, details: { limit, plan } } }`.\n *\n * Anything else is recorded as `kind: \"unknown\"` so the state surface\n * still reflects that ingest is throttled even when the body doesn't\n * parse into a known shape.\n */\nexport interface QuotaState {\n kind: \"event_limit\" | \"rate_limit\" | \"unknown\";\n status: number;\n /** Epoch seconds, matching Python's `time.time()`. */\n receivedAt: number;\n message?: string;\n currentCount?: number;\n limitCount?: number;\n planId?: string;\n periodStart?: string;\n upgradeUrl?: string;\n sampleRateUrl?: string;\n quotaLimit?: number;\n quotaRemaining?: number;\n quotaReset?: number;\n retryAfterSeconds?: number;\n}\n\n// `Retry-After` values above this are clamped, mirroring Python's\n// `_RETRY_AFTER_CAP_SECONDS` — a background flush loop must never commit\n// to reporting minutes-long waits from one batch.\nconst RETRY_AFTER_CAP_SECONDS = 30.0;\n\n/**\n * Parse a `Retry-After` header (seconds form only), capped.\n *\n * Mirrors Python's `transport.py::_parse_retry_after`. HTTP-date form\n * isn't worth supporting — Morse's own 429s always use the seconds form.\n * Unparseable / negative values return `null`.\n */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number(value);\n if (!Number.isFinite(seconds) || seconds < 0) return null;\n return Math.min(seconds, RETRY_AFTER_CAP_SECONDS);\n}\n\n// Module-level, matching the `lastTrace` test-introspection pattern in\n// `_internal/tracing.ts` — the latest quota/rate-limit state observed on\n// a 429 response, exposed via `quotaState()` / `morse.quotaState()`. Not\n// cleared on a subsequent successful POST (mirrors Python: it persists\n// until overwritten by a newer 429, so a host app can see \"quota was\n// exceeded recently\" even after the queue drains — see\n// `test_429_recovers_when_retry_succeeds` in the Python test suite,\n// which only asserts no drop-warning, never that state was cleared).\nlet quotaState: QuotaState | null = null;\n\n/**\n * Latest quota/rate-limit state observed on a 429, or `null` if none has\n * been observed yet. Returns a shallow copy — callers can't mutate\n * transport-internal state. Mirrors Python's `BatchTransport.quota_state()`.\n *\n * @internal\n */\nexport function getQuotaState(): QuotaState | null {\n return quotaState ? { ...quotaState } : null;\n}\n\n/**\n * Reset the module-level quota state between test files/cases.\n *\n * @internal\n */\nexport function _resetQuotaStateForTesting(): void {\n quotaState = null;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction numberFromHeader(raw: string | null): number | undefined {\n if (raw === null) return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Parse a 429 response into the module-level quota state. Never throws —\n * defensively parses two known body shapes (see `QuotaState`), falling\n * back to `kind: \"unknown\"` for anything else. Mirrors Python's\n * `_record_quota_state`.\n */\nasync function recordQuotaState(resp: Response): Promise<void> {\n try {\n const state: QuotaState = {\n kind: \"unknown\",\n status: 429,\n receivedAt: Date.now() / 1000,\n };\n\n let data: unknown = null;\n try {\n data = await resp.clone().json();\n } catch {\n data = null;\n }\n\n if (data !== null && typeof data === \"object\") {\n const body = data as Record<string, unknown>;\n const err = body.error;\n if (err === \"event_limit_exceeded\") {\n state.kind = \"event_limit\";\n state.message = asString(body.message);\n state.currentCount = asNumber(body.current_count);\n state.limitCount = asNumber(body.limit_count);\n state.planId = asString(body.plan_id);\n state.periodStart = asString(body.period_start);\n state.upgradeUrl = asString(body.upgrade_url);\n state.sampleRateUrl = asString(body.sample_rate_url);\n } else if (\n err !== null &&\n typeof err === \"object\" &&\n (err as Record<string, unknown>).code === \"RATE_LIMIT_INGEST\"\n ) {\n const errObj = err as Record<string, unknown>;\n const details =\n errObj.details !== null && typeof errObj.details === \"object\"\n ? (errObj.details as Record<string, unknown>)\n : {};\n state.kind = \"rate_limit\";\n state.message = asString(errObj.message);\n state.limitCount = asNumber(details.limit);\n state.planId = asString(details.plan);\n }\n }\n\n // TQE-05 headers, emitted by the server on every ingest response.\n const quotaLimit = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Limit\"));\n if (quotaLimit !== undefined) state.quotaLimit = quotaLimit;\n const quotaRemaining = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Remaining\"));\n if (quotaRemaining !== undefined) state.quotaRemaining = quotaRemaining;\n const quotaReset = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Reset\"));\n if (quotaReset !== undefined) state.quotaReset = quotaReset;\n const retryAfter = parseRetryAfter(resp.headers.get(\"Retry-After\"));\n if (retryAfter !== null) state.retryAfterSeconds = retryAfter;\n\n quotaState = state;\n } catch {\n // Never let state capture break the send path.\n }\n}\n\nexport class BatchTransport {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly flushIntervalSeconds: number;\n private readonly maxBatchSize: number;\n private readonly maxQueueSize: number;\n private readonly fetchTimeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n private queue: TelemetryEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private closed = false;\n private inflight: Promise<void> | null = null;\n\n constructor(opts: BatchTransportOptions) {\n this.endpoint = opts.endpoint;\n this.apiKey = opts.apiKey;\n this.flushIntervalSeconds = opts.flushIntervalSeconds ?? 2.0;\n this.maxBatchSize = opts.maxBatchSize ?? 100;\n this.maxQueueSize = opts.maxQueueSize ?? 10_000;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 5_000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);\n\n // setInterval keeps the Node event loop alive. We mark it `unref`\n // when available so a process-exit isn't blocked by a quiet flusher.\n const intervalMs = Math.max(50, Math.round(this.flushIntervalSeconds * 1000));\n this.timer = setInterval(() => {\n void this.tick();\n }, intervalMs);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const t = this.timer as any;\n if (typeof t?.unref === \"function\") {\n t.unref();\n }\n }\n\n /** Enqueue an event. Non-blocking. Returns false if the queue is full. */\n track(event: TelemetryEvent): boolean {\n if (this.closed) return false;\n if (this.queue.length >= this.maxQueueSize) return false;\n this.queue.push(event);\n if (this.queue.length >= this.maxBatchSize) {\n void this.tick();\n }\n return true;\n }\n\n /** Block until all currently-queued events have been sent. */\n async flush(): Promise<void> {\n // Drain in one-batch chunks until empty, then await the in-flight send.\n while (this.queue.length > 0) {\n await this.tick();\n }\n if (this.inflight) {\n try {\n await this.inflight;\n } catch {\n // Swallow — caller observes nothing.\n }\n }\n }\n\n /** Stop the background timer. After this call, `track()` is a no-op. */\n async shutdown(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private async tick(): Promise<void> {\n if (this.flushing) return;\n if (this.queue.length === 0) return;\n this.flushing = true;\n try {\n const batch = this.queue.splice(0, this.maxBatchSize);\n this.inflight = this.sendBatch(batch);\n await this.inflight;\n this.inflight = null;\n } finally {\n this.flushing = false;\n }\n }\n\n private async sendBatch(batch: TelemetryEvent[]): Promise<void> {\n const traceEntries: unknown[] = [];\n const runEntries: Record<string, unknown>[] = [];\n const logEntries: LogRecordPayload[] = [];\n for (const event of batch) {\n if (typeof event === \"object\" && event !== null && \"traces\" in event) {\n const e = event as { traces: unknown[] };\n if (Array.isArray(e.traces)) {\n traceEntries.push(...e.traces);\n }\n } else if (typeof event === \"object\" && event !== null && \"logs\" in event) {\n const e = event as { logs: LogRecordPayload[] };\n if (Array.isArray(e.logs)) {\n logEntries.push(...e.logs);\n }\n } else {\n runEntries.push(event as Record<string, unknown>);\n }\n }\n if (traceEntries.length > 0) {\n await this.post(this.endpoint, { traces: traceEntries });\n }\n if (runEntries.length > 0) {\n await this.post(this.endpoint, { runs: runEntries });\n }\n if (logEntries.length > 0) {\n // Phase 12.5 Tier 1.2 — logs go to a derived `/api/v1/logs/ingest`\n // endpoint on the same host as the traces batch URL. Mirrors the\n // Python SDK's URL derivation in\n // `apps/sdk/python/src/morse/client.py:187-190`.\n await this.post(deriveLogsEndpoint(this.endpoint), { logs: logEntries });\n }\n }\n\n private async post(url: string, payload: Record<string, unknown>): Promise<void> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.fetchTimeoutMs);\n try {\n const resp = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `morse-typescript/${SDK_VERSION}`,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n if (resp.status === 429) {\n // TQE-04 (MHQ-676) parity: surface quota/rate-limit state instead\n // of silently dropping telemetry. `resp.clone()` inside so the\n // body-snippet read below still works off the original response.\n await recordQuotaState(resp);\n }\n if (!resp.ok) {\n // Drain a few hundred chars of the body for diagnostics.\n let bodySnippet = \"\";\n try {\n const text = await resp.text();\n bodySnippet = text.slice(0, 200);\n } catch {\n // ignore\n }\n report(new Error(`morse: batch POST returned ${resp.status} ${bodySnippet}`), {\n fingerprint: \"morse.transport.post.non_2xx\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n }\n } catch (err) {\n // Network errors / aborts / timeouts — silent drop, out-of-band report.\n report(err, {\n fingerprint: \"morse.transport.post.network\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n\nfunction hostnameOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return \"<invalid>\";\n }\n}\n\n/**\n * Derive the logs ingest URL from the traces batch endpoint. Mirrors the\n * Python SDK helper: replace the path with `/api/v1/logs/ingest` and drop\n * any query/fragment.\n *\n * Exported for tests. Customer code should not call this directly.\n *\n * @internal\n */\nexport function deriveLogsEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = \"/api/v1/logs/ingest\";\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n // Fall back to a string replace on the most common shape.\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, \"/api/v1/logs/ingest\");\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/_internal/auto-detect.browser.ts","../../src/index.ts","../../src/_internal/db-capture.browser.ts","../../src/_internal/transport.ts"],"names":["init_env","init_otel_provider_browser","init_redaction","report","init_tracing","init_spans","init_propagation","getEndpoint","DEFAULT_ENDPOINT","isDisabled","getApiKey","getFlushIntervalSeconds","setTransport","setRedactionConfig","isValidInternalSource","getInternalSource","setSource","getEngine","getSampleRate","getMaxQueueSize","getMaxBatchSize","getMaxFieldBytes","buildRedactionConfig","_setEnsureInitialized","absorbErrorsSync","redactValue","run","generateTraceId","nowIso","span","generateSpanId","SpanType","buildTraceEntry","sendTrace","runSync","runAsync","setContext","startSpan","spanSync","spanAsync","absorbErrorsAsync","quotaState","_resetForTesting","getLastTrace","getActiveTraceState"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Browser replacement for `auto-detect.ts` (MHQ-482 follow-up).\n *\n * `auto-detect.ts` already no-ops at RUNTIME outside Node — its own\n * docstring says so — but it reaches for `node:module`'s `createRequire` /\n * `Module` in a static top-level import, so a bundler still had to resolve\n * `module` for the browser graph. The runtime guard was never the problem;\n * the import was.\n *\n * Nothing here is a behaviour change: zero-config adapter auto-detect works\n * by hooking `require()` to patch LLM SDKs as they load, and a browser has\n * no `require` to hook. See `otel-provider.browser.ts` for the full\n * rationale and the build wiring.\n */\n\n/** No-op: there is no `require` to hook in a browser. */\nexport async function installAdapters(): Promise<void> {}\n\n/** No state to reset in the browser build. */\nexport function _resetForTesting(): void {}\n","/**\n * `@morsehq-dev/sdk` — TypeScript SDK for Morse observability.\n *\n * V2 module-level singleton API:\n *\n * ```ts\n * import * as morse from \"@morsehq-dev/sdk\";\n *\n * // Three-step zero-setup integration:\n * // 1. Generate an API key in the Morse dashboard.\n * // 2. npm install @morsehq-dev/sdk\n * // 3. Set MORSE_API_KEY in your environment.\n *\n * morse.track({ agentId: \"lead-qualifier\", runData: { success: true } });\n *\n * // Or call init() explicitly to override env-var resolution:\n * morse.init({\n * apiKey: \"mhq_xxx\", // pragma: allowlist secret — placeholder, not a key\n * endpoint: \"https://api.morsehq.dev/api/v1/traces/batch\",\n * flushIntervalSeconds: 2,\n * });\n * ```\n *\n * Non-invasive guarantee: every public function is wrapped with\n * `absorbErrors`. Telemetry never throws back at the caller; internal\n * errors are reported out-of-band to Morse's Sentry instance.\n *\n * The internal `BatchTransport` and `TraceState` types back the singleton\n * but are NOT part of the public API. Customers should never reach into\n * the `_internal` directory.\n */\n\nimport {\n installAdapters as _installAdapters,\n _resetForTesting as _resetAutoDetectForTesting,\n} from \"./_internal/auto-detect\";\nimport { absorbErrorsAsync, absorbErrorsSync } from \"./_internal/errors\";\nimport {\n DEFAULT_ENDPOINT,\n getApiKey as readApiKeyFromEnv,\n getEndpoint as readEndpointFromEnv,\n getFlushIntervalSeconds,\n getInternalSource as readInternalSourceFromEnv,\n getMaxBatchSize,\n getMaxFieldBytes,\n getMaxQueueSize,\n getSampleRate,\n isDisabled as readIsDisabled,\n isValidInternalSource,\n} from \"./_internal/env\";\nimport { maybeEnableDbCaptureFromEnv } from \"./_internal/db-capture\";\nimport { getEngine as getOtelEngine } from \"./_internal/otel-provider\";\nimport {\n buildRedactionConfig,\n type RedactionConfig,\n type RedactionOptions,\n redactValue,\n} from \"./_internal/redaction\";\nimport {\n BatchTransport,\n SDK_VERSION,\n type TelemetryEvent,\n getQuotaState as _getQuotaState,\n _resetQuotaStateForTesting,\n type QuotaState,\n} from \"./_internal/transport\";\nimport {\n getActiveTraceState as _getActiveTraceState,\n getLastTrace as _getLastTrace,\n runAsync as _runAsync,\n runSync as _runSync,\n sendTrace as _sendTrace,\n setContext as _setContext,\n setRedactionConfig,\n setSource as _setSource,\n setTransport,\n startSpan as _startSpan,\n type RunHandle,\n type RunOptions,\n type SpanHandle,\n type SpanOptions,\n spanAsync as _spanAsync,\n spanSync as _spanSync,\n _resetForTesting as _resetTracingForTesting,\n} from \"./_internal/tracing\";\nimport { report, _resetForTesting as _resetSentryForTesting } from \"./_internal/sentry-reporter\";\nimport { _setEnsureInitialized as _setLogEnsureInitialized } from \"./log\";\nimport {\n buildTraceEntry,\n generateSpanId,\n generateTraceId,\n nowIso,\n SpanType,\n type SpanData,\n type TraceFields,\n} from \"./_internal/spans\";\n\nexport type { SpanData, SpanType, SpanStatus, ContextSegment } from \"./_internal/spans\";\nexport type { RunHandle, RunOptions, SpanHandle, SpanOptions };\nexport type { QuotaState };\nexport type { RedactionOptions, RedactionConfig } from \"./_internal/redaction\";\nexport {\n extractTraceparent,\n formatTraceparent,\n injectTraceparent,\n injectIntoRequestInit,\n W3C_TRACEPARENT_HEADER,\n} from \"./propagation\";\nexport {\n extractAnthropicSegments,\n instrumentAnthropic,\n isAnthropicWrapped,\n uninstallAnthropic,\n wrapAnthropic,\n MAX_SEGMENT_CONTENT_BYTES,\n} from \"./anthropic\";\nexport type {\n AnthropicClientLike,\n AnthropicContentBlockParam,\n AnthropicMessageCreateParams,\n AnthropicMessageParam,\n AnthropicMessageResponse,\n AnthropicTextBlockParam,\n AnthropicToolParam,\n AnthropicToolResultBlockParam,\n AnthropicToolUseBlockParam,\n AnthropicUsage,\n InstrumentAnthropicOptions,\n} from \"./anthropic\";\nexport { log } from \"./log\";\nexport type { LogOptions, LogSeverity } from \"./log\";\n\nexport const version = SDK_VERSION;\n\n/* ------------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------------ */\n\n/** Options accepted by `init()`. Mirrors Python's `init(...)` kwargs. */\nexport interface InitOptions {\n /** API key (starts with \"mhq_\"). Required if no `MORSE_API_KEY` env var. */\n apiKey?: string;\n /** Full ingestion URL. Defaults to the production Railway endpoint. */\n endpoint?: string;\n /** Seconds between batch flushes. Default: 2. */\n flushIntervalSeconds?: number;\n /**\n * Client-side PII redaction. Default-on with the canonical eight-pattern\n * catalogue. Set `enabled: false` to opt out (the server-side mandatory\n * floor still applies). Mandatory patterns (`api_key_prefixed`, `jwt`,\n * `credit_card`, `ssn_us`) cannot be added to `disabledPatterns` —\n * `init()` throws on that.\n */\n redaction?: RedactionOptions;\n /**\n * INTERNAL USE ONLY — resource-level source label for Morse's own\n * dogfood emitters. Mirrors the Python SDK's `init(source=...)` kwarg\n * (PR #90). Accepted values: `\"copilot\" | \"investigator\" | \"refagent\"\n * | \"ui-bot\"`. Any other value (including misspellings) is silently\n * dropped — falls back to the `MORSE_INTERNAL_SOURCE` env var.\n * Customers should not set this.\n */\n source?: string;\n}\n\n/** Single agent-run payload accepted by `track()`. */\nexport interface TrackOptions {\n /** User-defined agent name (e.g., \"lead-qualifier\"). */\n agentId: string;\n /** Run-data dict. Shape mirrors backend Run model. */\n runData: Record<string, unknown>;\n}\n\n/** High-level outcome shape accepted by `record()` (V1 ergonomic API). */\nexport interface RunRecord {\n agent: string;\n success?: boolean;\n outcome?: string;\n cost?: number;\n model?: string;\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n durationMs?: number;\n errorMessage?: string;\n metadata?: Record<string, unknown>;\n status?: \"completed\" | \"failed\" | \"timed_out\";\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level singleton state */\n/* ------------------------------------------------------------------------ */\n\ninterface SingletonState {\n apiKey: string;\n endpoint: string;\n transport: BatchTransport;\n redaction: RedactionConfig;\n}\n\nlet singleton: SingletonState | null = null;\nlet transportFactory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n}) => BatchTransport = (opts) => new BatchTransport(opts);\n\nlet noApiKeyWarningEmitted = false;\nlet lastLoggedEndpoint: string | null = null;\n\nfunction logger(level: \"info\" | \"warn\", message: string): void {\n const out = level === \"warn\" ? console.warn : console.info;\n out.call(console, message);\n}\n\nfunction logDestinationOnce(endpoint: string): void {\n if (endpoint === lastLoggedEndpoint) return;\n lastLoggedEndpoint = endpoint;\n logger(\n \"info\",\n `morse: Morse SDK initialized — sending to ${endpoint} (sdk version ${SDK_VERSION})`,\n );\n}\n\nfunction resolveEndpoint(explicit?: string): string {\n if (explicit && explicit.length > 0) return explicit;\n const fromEnv = readEndpointFromEnv();\n if (fromEnv) return fromEnv;\n return DEFAULT_ENDPOINT;\n}\n\nconst OTLP_TRACES_PATH = \"/api/v1/otlp/v1/traces\";\n\n/**\n * Derive the OTLP ingest URL from the legacy JSON traces batch URL. Keeps\n * host/scheme; replaces the path — mirrors the Python SDK's\n * `client.py::_derive_otlp_endpoint` and this file's own\n * `transport.ts::deriveLogsEndpoint`.\n */\nfunction deriveOtlpEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = OTLP_TRACES_PATH;\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, OTLP_TRACES_PATH);\n }\n}\n\nfunction buildSingleton(opts: InitOptions, redaction: RedactionConfig): SingletonState | null {\n if (readIsDisabled()) {\n return null;\n }\n const apiKey = opts.apiKey ?? readApiKeyFromEnv();\n if (!apiKey) {\n if (!noApiKeyWarningEmitted) {\n logger(\n \"warn\",\n \"morse: not initialized — set MORSE_API_KEY or call morse.init({ apiKey }) (this warning prints once per process; SDK calls are silent no-ops)\",\n );\n noApiKeyWarningEmitted = true;\n }\n return null;\n }\n const endpoint = resolveEndpoint(opts.endpoint);\n const flushIntervalSeconds = opts.flushIntervalSeconds ?? getFlushIntervalSeconds();\n const transport = transportFactory({ apiKey, endpoint, flushIntervalSeconds });\n setTransport(transport);\n setRedactionConfig(redaction);\n // Resolve the internal source label: explicit kwarg wins, then env.\n // Invalid values are silently dropped so a typo never tags customer\n // data as dogfood.\n const explicitSource = isValidInternalSource(opts.source) ? opts.source! : undefined;\n const resolvedSource = explicitSource ?? readInternalSourceFromEnv() ?? null;\n _setSource(resolvedSource);\n logDestinationOnce(endpoint);\n\n // MHQ-482: run()/span()/runTraced()'s real emission pipeline — real OTel\n // spans over OTLP protobuf+gzip. Configured unconditionally (even under\n // MORSE_LEGACY_TRANSPORT=1) so the engine is ready if the flag flips at\n // runtime; the flag only affects which pipeline `tracing.ts::sendTrace()`\n // feeds. Mirrors the Python SDK's `_Client.__init__`.\n try {\n getOtelEngine().configure({\n endpoint: deriveOtlpEndpoint(endpoint),\n apiKey,\n sampleRate: getSampleRate(),\n scheduleDelayMillis: flushIntervalSeconds * 1000,\n maxQueueSize: getMaxQueueSize(),\n maxExportBatchSize: getMaxBatchSize(),\n maxFieldBytes: getMaxFieldBytes(),\n // 10s (not the exporter's un-overridden 30s ceiling): a genuine\n // network blip still gets several retries with backoff; a fully dead\n // endpoint fails within a bounded, shutdown-friendly window instead\n // of the library's full 30s ceiling. Mirrors the Python SDK.\n timeoutMillis: 10_000,\n redactionConfig: redaction,\n });\n } catch (err) {\n report(err, { fingerprint: \"morse.otel_provider.configure\" });\n }\n\n // MHQ-482: zero-config adapter auto-detect (mirrors Python's\n // `auto_detect.install_adapters()` call in `_ensure_initialized` /\n // `_init_impl`). Fire-and-forget — telemetry setup must never block or\n // throw back into the customer's init() / first SDK call. Runs AFTER the\n // OTel engine above is configured so any span an auto-installed adapter\n // emits immediately has a working pipeline to flow through.\n void _installAdapters().catch(() => {\n // installAdapters() already absorbs adapter-level failures; this is a\n // defensive backstop so a totally unexpected rejection never surfaces.\n });\n\n // MHQ-482: DB auto-capture (opt-in — MORSE_CAPTURE_DB / MORSE_CAPTURE_INFRA,\n // mirrors the Python SDK's `infra_capture.py::maybe_enable_from_env`).\n // MUST run after `getOtelEngine().configure()` above — instrumentation\n // registration needs the `NodeTracerProvider` that call just built.\n try {\n maybeEnableDbCaptureFromEnv();\n } catch (err) {\n report(err, { fingerprint: \"morse.db_capture.register\" });\n }\n\n return { apiKey, endpoint, transport, redaction };\n}\n\nfunction ensureInitialized(): SingletonState | null {\n if (readIsDisabled()) return null;\n if (singleton !== null) return singleton;\n // Lazy default-init: defaults are safe (default-on redaction).\n singleton = buildSingleton({}, buildRedactionConfig());\n return singleton;\n}\n\n// Wire the lazy-init hook into log.ts so `log()` participates in the\n// same env-driven auto-init flow as `track()` / `record()`. Discards\n// the return value — log.ts only needs the side-effect of populating\n// the module-level singleton + transport.\n_setLogEnsureInitialized(() => {\n ensureInitialized();\n});\n\n/* ------------------------------------------------------------------------ */\n/* Public API — wrapped at the module surface */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Initialize the Morse SDK singleton. Call once at application\n * startup. After this call, `track()`, `batchTrack()`, `run()`, and\n * `flush()` are wired up to the configured endpoint.\n *\n * `endpoint` is optional — when omitted, the SDK resolves in this order:\n * 1. `MORSE_ENDPOINT` env var (if set)\n * 2. The default Railway production URL.\n *\n * `MORSE_DISABLED=1` overrides everything: `init()` is a no-op.\n *\n * Calling `init()` a second time replaces the singleton — the previous\n * transport is shut down cleanly and a new one is constructed.\n *\n * Validation errors (e.g., disabling a mandatory redaction pattern, an\n * uncompilable extra pattern) are deliberately NOT absorbed — they\n * propagate as `Error`s so the developer sees the misconfiguration at\n * application startup. All other errors raised from internal SDK paths\n * are absorbed via the per-call wrappers downstream.\n */\nexport function init(options: InitOptions = {}): void {\n if (readIsDisabled()) {\n logger(\"info\", \"morse: MORSE_DISABLED=1 — SDK is a no-op\");\n return;\n }\n // Validate redaction options FIRST — must throw before we mutate any\n // singleton state, so a misconfigured init leaves the previous client\n // intact and the spec scenario \"no Client is constructed\" holds.\n const redaction = buildRedactionConfig(options.redaction);\n\n try {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n }\n singleton = buildSingleton(options, redaction);\n } catch (err) {\n report(err, { fingerprint: \"morse.init\" });\n }\n}\n\n/**\n * Submit a single agent run to the ingestion endpoint. Non-blocking;\n * returns immediately. Silently no-ops if the SDK isn't initialised.\n */\nexport const track = absorbErrorsSync(\n (options: TrackOptions): void => {\n const state = ensureInitialized();\n if (!state) return;\n const payload = {\n agent_id: options.agentId,\n ...options.runData,\n };\n const redacted = redactValue(payload, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n },\n { fingerprint: \"morse.track\" },\n);\n\n/** Submit multiple agent runs in bulk. Silently no-ops if uninitialised. */\nexport const batchTrack = absorbErrorsSync(\n (runs: Array<Record<string, unknown>>): void => {\n const state = ensureInitialized();\n if (!state) return;\n for (const run of runs) {\n const redacted = redactValue(run, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n }\n },\n { fingerprint: \"morse.batch_track\" },\n);\n\n/**\n * High-level convenience: record an agent outcome with named fields.\n *\n * Builds a synthetic single-span `agent`-type trace and submits it through\n * the same `traces`-shaped emission path `run()`/`span()` use (MHQ-746) —\n * mirrors Python's `client.py::record()`. The live dev-api backend has no\n * `runs` ingest route; sending through `state.transport.track()` (the\n * shape `track()`/`batchTrack()` still use, matching Python's equally\n * unmigrated `track()`/`batch_track()`) 422s silently. See MHQ-746.\n */\nexport const record = absorbErrorsSync(\n (rec: RunRecord): void => {\n const state = ensureInitialized();\n if (!state) return;\n\n const traceId = generateTraceId();\n const now = nowIso();\n const resolvedStatus = rec.errorMessage ? \"failed\" : (rec.status ?? \"completed\");\n\n const span: SpanData = {\n trace_id: traceId,\n span_id: generateSpanId(),\n type: SpanType.AGENT,\n name: rec.agent,\n started_at: now,\n ended_at: now,\n status: resolvedStatus,\n duration_ms: rec.durationMs,\n model: rec.model,\n input_tokens: rec.inputTokens,\n output_tokens: rec.outputTokens,\n total_tokens: rec.totalTokens,\n cost_usd: rec.cost,\n error_message: rec.errorMessage,\n metadata: rec.metadata,\n };\n\n const traceFields: TraceFields = {\n trace_id: traceId,\n agent: rec.agent,\n status: resolvedStatus,\n started_at: now,\n ended_at: now,\n duration_ms: rec.durationMs,\n error_message: rec.errorMessage,\n success: rec.success,\n outcome: rec.outcome,\n };\n\n const entry = buildTraceEntry(traceFields, [span]);\n const redacted = redactValue(entry, state.redaction) as {\n trace: Record<string, unknown>;\n spans: Record<string, unknown>[];\n };\n _sendTrace(redacted);\n },\n { fingerprint: \"morse.record\" },\n);\n\n/**\n * Open a traced agent run, executing `fn` inside the trace context.\n * Mirrors Python's `morse.run(...)` context manager.\n *\n * Two overloads are supported:\n * - `run(name, fn)` — quick form with default options.\n * - `run({ agentName, incomingTraceparent }, fn)` — full options.\n *\n * Async callers receive a Promise; sync callers receive the value\n * directly. The function's return value is forwarded to the caller.\n */\nexport function run<T>(name: string, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(options: RunOptions, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(\n nameOrOpts: string | RunOptions,\n fn: (handle: RunHandle) => T,\n): T | undefined {\n const opts = typeof nameOrOpts === \"string\" ? { agentName: nameOrOpts } : nameOrOpts;\n // Make sure the SDK is initialised so spans get sent. Failure to init\n // is fine — `_runSync` records to `lastTrace` regardless, useful for tests.\n ensureInitialized();\n // We can't reliably detect async vs sync up front in TypeScript, so we\n // route through `_runSync` and, if the user's `fn` returns a promise,\n // the wrapping trace ends synchronously after the promise is created.\n // For correct async lifecycle, callers should use `runTraced` instead\n // (it awaits the promise inside the `als.run` scope so the trace\n // closes when the promise resolves).\n return _runSync(opts, fn);\n}\n\n/** Async variant of `run` — exported for callers who want explicit typing. */\nexport function runTraced<T>(\n options: RunOptions,\n fn: (handle: RunHandle) => Promise<T>,\n): Promise<T | undefined> {\n ensureInitialized();\n return _runAsync(options, fn);\n}\n\n/**\n * Attach cost-attribution dimensions to the active trace (MHQ-751). Call\n * once per request (e.g. in middleware) to propagate `customerId`,\n * `feature`, `env`, `team`, etc. to all child spans downstream. Values are\n * coerced to strings. No-op outside an active trace — never throws.\n * Mirrors Python's `morse_ai.set_context(**dims)`.\n */\nexport function setContext(dims: Record<string, unknown>): void {\n _setContext(dims);\n}\n\n/** Start a child span under the active trace. Returns null if no trace. */\nexport function startSpan(options: SpanOptions): SpanHandle | null {\n return _startSpan(options);\n}\n\n/** Run `fn` inside a child span. No-op if no trace is active. */\nexport function span<T>(options: SpanOptions, fn: (handle: SpanHandle | null) => T): T {\n return _spanSync(options, fn);\n}\n\n/** Async variant of `span`. */\nexport function spanAsync<T>(\n options: SpanOptions,\n fn: (handle: SpanHandle | null) => Promise<T>,\n): Promise<T> {\n return _spanAsync(options, fn);\n}\n\n/** Block until all queued events have been sent. Silent on uninit / errors. */\nexport const flush = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.flush();\n await getOtelEngine().flush();\n }\n },\n { fingerprint: \"morse.flush\" },\n);\n\n/**\n * Latest ingest quota / rate-limit state observed by the SDK, or `null`.\n *\n * TQE-04 (MHQ-676/MHQ-749): when the Morse backend answers a telemetry\n * batch with HTTP 429 — the monthly event limit (`event_limit_exceeded`)\n * or the per-second ingest rate limit (`RATE_LIMIT_INGEST`) — the\n * transport parses the response and stores it. This function exposes\n * that state so host applications can alert on quota exhaustion instead\n * of discovering it from missing telemetry. Mirrors Python's\n * `morse_ai.quota_state()`.\n *\n * Returns `null` when the SDK is uninitialized or no 429 has been\n * observed. The object includes at least `kind` (`\"event_limit\"`,\n * `\"rate_limit\"`, or `\"unknown\"`), `status`, and `receivedAt` (epoch\n * seconds); event-limit states add `currentCount` / `limitCount` /\n * `planId` / `periodStart` / `upgradeUrl`.\n */\nexport const quotaState = absorbErrorsSync(\n (): QuotaState | null => {\n if (singleton === null) return null;\n return _getQuotaState();\n },\n { fingerprint: \"morse.quota_state\" },\n);\n\n/** Flush and stop the background transport. Call on app exit. */\nexport const shutdown = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.shutdown();\n await getOtelEngine().flush();\n getOtelEngine().shutdown();\n singleton = null;\n setTransport(null);\n }\n },\n { fingerprint: \"morse.shutdown\" },\n);\n\n/* ------------------------------------------------------------------------ */\n/* Test-only hooks (private) */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Reset the module-level singleton for tests. Customer code must NOT\n * call this — the singleton is meant to live for the process lifetime.\n *\n * @internal\n */\nexport function _resetForTesting(): void {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n setTransport(null);\n }\n getOtelEngine().resetForTesting();\n noApiKeyWarningEmitted = false;\n lastLoggedEndpoint = null;\n _setSource(null);\n _resetTracingForTesting();\n _resetSentryForTesting();\n _resetAutoDetectForTesting();\n _resetQuotaStateForTesting();\n // Reset the transport factory back to the default.\n transportFactory = (opts) => new BatchTransport(opts);\n}\n\n/**\n * Inject an alternate transport factory for tests (e.g., a mock that\n * captures sent payloads instead of opening a fetch).\n *\n * @internal\n */\nexport function _setTransportFactoryForTesting(\n factory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n }) => BatchTransport,\n): void {\n transportFactory = factory;\n}\n\n/**\n * Read the most recently completed trace entry, for test assertions.\n *\n * @internal\n */\nexport function _getLastTraceForTesting(): { trace: unknown; spans: unknown[] } | null {\n return _getLastTrace();\n}\n\n/**\n * Read the active trace state, for test assertions.\n *\n * @internal\n */\nexport function _getActiveTraceStateForTesting(): unknown {\n return _getActiveTraceState();\n}\n","/**\n * Browser replacement for `db-capture.ts` (MHQ-482 follow-up).\n *\n * `db-capture.ts` statically imports `@opentelemetry/instrumentation-pg`,\n * `-mysql2` and `@opentelemetry/instrumentation` (which reaches for node's\n * `module` via require-in-the-middle). A browser has no pg/mysql2 driver to\n * patch, so the whole module is inert there — but the static imports still\n * dragged those packages, and node `module`, into the browser graph.\n *\n * Swapped in by the esbuild plugin in `tsup.config.ts`; see\n * `otel-provider.browser.ts` for the full rationale. Only the symbols\n * `index.ts` imports need to exist here.\n */\n\n/**\n * No-op: there is no database driver to instrument in a browser.\n *\n * Deliberately silent. `otel-provider.browser.ts::noteBrowserRuntime()`\n * already states that DB capture is Node-only, and it fires on\n * `configure()` — which `index.ts` always calls before this. A second\n * message would be noise, and this function runs even when the customer\n * never asked for DB capture.\n */\nexport function maybeEnableDbCaptureFromEnv(): void {}\n","/**\n * Background batched HTTP transport.\n *\n * Mirrors the Python SDK's `transport.py` semantics:\n *\n * - Async batching: 2-second flush interval (configurable), 100-event\n * max batch size (configurable). Whichever fires first triggers a\n * flush.\n * - Network failures are silently dropped with a debug log — never\n * thrown to the caller.\n * - The caller's thread / event loop is never blocked: `track()`\n * enqueues and returns immediately.\n *\n * The transport speaks two payload shapes (matching Python):\n *\n * - V2 trace events: `{ traces: [{ trace, spans }, ...] }`\n * - V1 legacy runs: `{ runs: [{ ... }, ...] }`\n *\n * Wire-format routing checks for a `traces` key on each enqueued event.\n *\n * Browser vs Node:\n * - Both runtimes ship `fetch` (Node 18+ / all evergreens).\n * - In Node we can run a `setInterval` and call it a day.\n * - In the browser the same approach works; we don't currently flush\n * on `pagehide` / `beforeunload` — Phase 6c spec doesn't require it.\n * A future change can add `navigator.sendBeacon` for last-gasp delivery.\n */\n\nimport { report } from \"./sentry-reporter\";\n\n// Injected at build time by tsup from package.json — see tsup.config.ts.\n// The version is declared in exactly one place (package.json); a copy here\n// would drift the moment a release bumped one and not the other, and the\n// stale value rides out on the User-Agent and on every span's `sdk_version`.\n// `__SDK_VERSION__` is undefined when running straight from source (vitest,\n// ts-node), hence the fallback.\ndeclare const __SDK_VERSION__: string | undefined;\n\nexport const SDK_VERSION = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\nexport interface BatchTransportOptions {\n endpoint: string;\n apiKey: string;\n /** Max wait between flushes, in seconds. Default: 2.0. */\n flushIntervalSeconds?: number;\n /** Max events per batch. Default: 100. */\n maxBatchSize?: number;\n /** Max queued events before drop. Default: 10_000. */\n maxQueueSize?: number;\n /** Timeout for the underlying fetch, in ms. Default: 5_000. */\n fetchTimeoutMs?: number;\n /**\n * Pluggable fetch implementation. Defaults to `globalThis.fetch`.\n * Useful for tests.\n */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Wire shape for a single log record. Mirrors the Python SDK payload at\n * `apps/sdk/python/src/morse/client.py:191-211` and the backend\n * `SimpleLogsIngestController.ingest` contract\n * (`apps/api/apps/logs/controllers.py:66-134`).\n */\nexport interface LogRecordPayload {\n timestamp: string;\n severity: string;\n service_name: string;\n body: string;\n attributes: Record<string, unknown>;\n trace_id: string | null;\n span_id: string | null;\n}\n\nexport type TelemetryEvent =\n | { traces: unknown[] }\n | { logs: LogRecordPayload[] }\n | Record<string, unknown>;\n\n/**\n * Quota / rate-limit state parsed from a 429 batch-POST response.\n *\n * Mirrors Python's `transport.py::BatchTransport._record_quota_state` /\n * `quota_state()` (TQE-04 / MHQ-676) field-for-field, camelCased per this\n * codebase's convention (`received_at` -> `receivedAt`, etc.). Two known\n * body shapes:\n *\n * - `event_limit_exceeded` (traces controllers) — flat body with\n * `current_count` / `limit_count` / `plan_id` / `period_start` /\n * `upgrade_url` / `sample_rate_url`.\n * - `RATE_LIMIT_INGEST` (ingest rate-limit middleware) — nested\n * `{ error: { code, message, details: { limit, plan } } }`.\n *\n * Anything else is recorded as `kind: \"unknown\"` so the state surface\n * still reflects that ingest is throttled even when the body doesn't\n * parse into a known shape.\n */\nexport interface QuotaState {\n kind: \"event_limit\" | \"rate_limit\" | \"unknown\";\n status: number;\n /** Epoch seconds, matching Python's `time.time()`. */\n receivedAt: number;\n message?: string;\n currentCount?: number;\n limitCount?: number;\n planId?: string;\n periodStart?: string;\n upgradeUrl?: string;\n sampleRateUrl?: string;\n quotaLimit?: number;\n quotaRemaining?: number;\n quotaReset?: number;\n retryAfterSeconds?: number;\n}\n\n// `Retry-After` values above this are clamped, mirroring Python's\n// `_RETRY_AFTER_CAP_SECONDS` — a background flush loop must never commit\n// to reporting minutes-long waits from one batch.\nconst RETRY_AFTER_CAP_SECONDS = 30.0;\n\n/**\n * Parse a `Retry-After` header (seconds form only), capped.\n *\n * Mirrors Python's `transport.py::_parse_retry_after`. HTTP-date form\n * isn't worth supporting — Morse's own 429s always use the seconds form.\n * Unparseable / negative values return `null`.\n */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number(value);\n if (!Number.isFinite(seconds) || seconds < 0) return null;\n return Math.min(seconds, RETRY_AFTER_CAP_SECONDS);\n}\n\n// Module-level, matching the `lastTrace` test-introspection pattern in\n// `_internal/tracing.ts` — the latest quota/rate-limit state observed on\n// a 429 response, exposed via `quotaState()` / `morse.quotaState()`. Not\n// cleared on a subsequent successful POST (mirrors Python: it persists\n// until overwritten by a newer 429, so a host app can see \"quota was\n// exceeded recently\" even after the queue drains — see\n// `test_429_recovers_when_retry_succeeds` in the Python test suite,\n// which only asserts no drop-warning, never that state was cleared).\nlet quotaState: QuotaState | null = null;\n\n/**\n * Latest quota/rate-limit state observed on a 429, or `null` if none has\n * been observed yet. Returns a shallow copy — callers can't mutate\n * transport-internal state. Mirrors Python's `BatchTransport.quota_state()`.\n *\n * @internal\n */\nexport function getQuotaState(): QuotaState | null {\n return quotaState ? { ...quotaState } : null;\n}\n\n/**\n * Reset the module-level quota state between test files/cases.\n *\n * @internal\n */\nexport function _resetQuotaStateForTesting(): void {\n quotaState = null;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction numberFromHeader(raw: string | null): number | undefined {\n if (raw === null) return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Parse a 429 response into the module-level quota state. Never throws —\n * defensively parses two known body shapes (see `QuotaState`), falling\n * back to `kind: \"unknown\"` for anything else. Mirrors Python's\n * `_record_quota_state`.\n */\nasync function recordQuotaState(resp: Response): Promise<void> {\n try {\n const state: QuotaState = {\n kind: \"unknown\",\n status: 429,\n receivedAt: Date.now() / 1000,\n };\n\n let data: unknown = null;\n try {\n data = await resp.clone().json();\n } catch {\n data = null;\n }\n\n if (data !== null && typeof data === \"object\") {\n const body = data as Record<string, unknown>;\n const err = body.error;\n if (err === \"event_limit_exceeded\") {\n state.kind = \"event_limit\";\n state.message = asString(body.message);\n state.currentCount = asNumber(body.current_count);\n state.limitCount = asNumber(body.limit_count);\n state.planId = asString(body.plan_id);\n state.periodStart = asString(body.period_start);\n state.upgradeUrl = asString(body.upgrade_url);\n state.sampleRateUrl = asString(body.sample_rate_url);\n } else if (\n err !== null &&\n typeof err === \"object\" &&\n (err as Record<string, unknown>).code === \"RATE_LIMIT_INGEST\"\n ) {\n const errObj = err as Record<string, unknown>;\n const details =\n errObj.details !== null && typeof errObj.details === \"object\"\n ? (errObj.details as Record<string, unknown>)\n : {};\n state.kind = \"rate_limit\";\n state.message = asString(errObj.message);\n state.limitCount = asNumber(details.limit);\n state.planId = asString(details.plan);\n }\n }\n\n // TQE-05 headers, emitted by the server on every ingest response.\n const quotaLimit = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Limit\"));\n if (quotaLimit !== undefined) state.quotaLimit = quotaLimit;\n const quotaRemaining = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Remaining\"));\n if (quotaRemaining !== undefined) state.quotaRemaining = quotaRemaining;\n const quotaReset = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Reset\"));\n if (quotaReset !== undefined) state.quotaReset = quotaReset;\n const retryAfter = parseRetryAfter(resp.headers.get(\"Retry-After\"));\n if (retryAfter !== null) state.retryAfterSeconds = retryAfter;\n\n quotaState = state;\n } catch {\n // Never let state capture break the send path.\n }\n}\n\nexport class BatchTransport {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly flushIntervalSeconds: number;\n private readonly maxBatchSize: number;\n private readonly maxQueueSize: number;\n private readonly fetchTimeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n private queue: TelemetryEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private closed = false;\n private inflight: Promise<void> | null = null;\n\n constructor(opts: BatchTransportOptions) {\n this.endpoint = opts.endpoint;\n this.apiKey = opts.apiKey;\n this.flushIntervalSeconds = opts.flushIntervalSeconds ?? 2.0;\n this.maxBatchSize = opts.maxBatchSize ?? 100;\n this.maxQueueSize = opts.maxQueueSize ?? 10_000;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 5_000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);\n\n // setInterval keeps the Node event loop alive. We mark it `unref`\n // when available so a process-exit isn't blocked by a quiet flusher.\n const intervalMs = Math.max(50, Math.round(this.flushIntervalSeconds * 1000));\n this.timer = setInterval(() => {\n void this.tick();\n }, intervalMs);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const t = this.timer as any;\n if (typeof t?.unref === \"function\") {\n t.unref();\n }\n }\n\n /** Enqueue an event. Non-blocking. Returns false if the queue is full. */\n track(event: TelemetryEvent): boolean {\n if (this.closed) return false;\n if (this.queue.length >= this.maxQueueSize) return false;\n this.queue.push(event);\n if (this.queue.length >= this.maxBatchSize) {\n void this.tick();\n }\n return true;\n }\n\n /** Block until all currently-queued events have been sent. */\n async flush(): Promise<void> {\n // Drain in one-batch chunks until empty, then await the in-flight send.\n while (this.queue.length > 0) {\n await this.tick();\n }\n if (this.inflight) {\n try {\n await this.inflight;\n } catch {\n // Swallow — caller observes nothing.\n }\n }\n }\n\n /** Stop the background timer. After this call, `track()` is a no-op. */\n async shutdown(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private async tick(): Promise<void> {\n if (this.flushing) return;\n if (this.queue.length === 0) return;\n this.flushing = true;\n try {\n const batch = this.queue.splice(0, this.maxBatchSize);\n this.inflight = this.sendBatch(batch);\n await this.inflight;\n this.inflight = null;\n } finally {\n this.flushing = false;\n }\n }\n\n private async sendBatch(batch: TelemetryEvent[]): Promise<void> {\n const traceEntries: unknown[] = [];\n const runEntries: Record<string, unknown>[] = [];\n const logEntries: LogRecordPayload[] = [];\n for (const event of batch) {\n if (typeof event === \"object\" && event !== null && \"traces\" in event) {\n const e = event as { traces: unknown[] };\n if (Array.isArray(e.traces)) {\n traceEntries.push(...e.traces);\n }\n } else if (typeof event === \"object\" && event !== null && \"logs\" in event) {\n const e = event as { logs: LogRecordPayload[] };\n if (Array.isArray(e.logs)) {\n logEntries.push(...e.logs);\n }\n } else {\n runEntries.push(event as Record<string, unknown>);\n }\n }\n if (traceEntries.length > 0) {\n await this.post(this.endpoint, { traces: traceEntries });\n }\n if (runEntries.length > 0) {\n await this.post(this.endpoint, { runs: runEntries });\n }\n if (logEntries.length > 0) {\n // Phase 12.5 Tier 1.2 — logs go to a derived `/api/v1/logs/ingest`\n // endpoint on the same host as the traces batch URL. Mirrors the\n // Python SDK's URL derivation in\n // `apps/sdk/python/src/morse/client.py:187-190`.\n await this.post(deriveLogsEndpoint(this.endpoint), { logs: logEntries });\n }\n }\n\n private async post(url: string, payload: Record<string, unknown>): Promise<void> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.fetchTimeoutMs);\n try {\n const resp = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `morse-typescript/${SDK_VERSION}`,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n if (resp.status === 429) {\n // TQE-04 (MHQ-676) parity: surface quota/rate-limit state instead\n // of silently dropping telemetry. `resp.clone()` inside so the\n // body-snippet read below still works off the original response.\n await recordQuotaState(resp);\n }\n if (!resp.ok) {\n // Drain a few hundred chars of the body for diagnostics.\n let bodySnippet = \"\";\n try {\n const text = await resp.text();\n bodySnippet = text.slice(0, 200);\n } catch {\n // ignore\n }\n report(new Error(`morse: batch POST returned ${resp.status} ${bodySnippet}`), {\n fingerprint: \"morse.transport.post.non_2xx\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n }\n } catch (err) {\n // Network errors / aborts / timeouts — silent drop, out-of-band report.\n report(err, {\n fingerprint: \"morse.transport.post.network\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n\nfunction hostnameOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return \"<invalid>\";\n }\n}\n\n/**\n * Derive the logs ingest URL from the traces batch endpoint. Mirrors the\n * Python SDK helper: replace the path with `/api/v1/logs/ingest` and drop\n * any query/fragment.\n *\n * Exported for tests. Customer code should not call this directly.\n *\n * @internal\n */\nexport function deriveLogsEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = \"/api/v1/logs/ingest\";\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n // Fall back to a string replace on the most common shape.\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, \"/api/v1/logs/ingest\");\n }\n}\n"]}
@@ -22,7 +22,7 @@ init_otel_provider_browser();
22
22
  init_redaction();
23
23
 
24
24
  // src/_internal/transport.ts
25
- var SDK_VERSION = "0.4.0-rc.2" ;
25
+ var SDK_VERSION = "0.4.0" ;
26
26
  var RETRY_AFTER_CAP_SECONDS = 30;
27
27
  function parseRetryAfter(value) {
28
28
  if (!value) return null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/_internal/auto-detect.browser.ts","../../src/index.ts","../../src/_internal/db-capture.browser.ts","../../src/_internal/transport.ts"],"names":["run","span","setContext","startSpan","spanAsync","quotaState","_resetForTesting"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Browser replacement for `auto-detect.ts` (MHQ-482 follow-up).\n *\n * `auto-detect.ts` already no-ops at RUNTIME outside Node — its own\n * docstring says so — but it reaches for `node:module`'s `createRequire` /\n * `Module` in a static top-level import, so a bundler still had to resolve\n * `module` for the browser graph. The runtime guard was never the problem;\n * the import was.\n *\n * Nothing here is a behaviour change: zero-config adapter auto-detect works\n * by hooking `require()` to patch LLM SDKs as they load, and a browser has\n * no `require` to hook. See `otel-provider.browser.ts` for the full\n * rationale and the build wiring.\n */\n\n/** No-op: there is no `require` to hook in a browser. */\nexport async function installAdapters(): Promise<void> {}\n\n/** No state to reset in the browser build. */\nexport function _resetForTesting(): void {}\n","/**\n * `@morsehq-dev/sdk` — TypeScript SDK for Morse observability.\n *\n * V2 module-level singleton API:\n *\n * ```ts\n * import * as morse from \"@morsehq-dev/sdk\";\n *\n * // Three-step zero-setup integration:\n * // 1. Generate an API key in the Morse dashboard.\n * // 2. npm install @morsehq-dev/sdk\n * // 3. Set MORSE_API_KEY in your environment.\n *\n * morse.track({ agentId: \"lead-qualifier\", runData: { success: true } });\n *\n * // Or call init() explicitly to override env-var resolution:\n * morse.init({\n * apiKey: \"mhq_xxx\", // pragma: allowlist secret — placeholder, not a key\n * endpoint: \"https://api.morsehq.dev/api/v1/traces/batch\",\n * flushIntervalSeconds: 2,\n * });\n * ```\n *\n * Non-invasive guarantee: every public function is wrapped with\n * `absorbErrors`. Telemetry never throws back at the caller; internal\n * errors are reported out-of-band to Morse's Sentry instance.\n *\n * The internal `BatchTransport` and `TraceState` types back the singleton\n * but are NOT part of the public API. Customers should never reach into\n * the `_internal` directory.\n */\n\nimport {\n installAdapters as _installAdapters,\n _resetForTesting as _resetAutoDetectForTesting,\n} from \"./_internal/auto-detect\";\nimport { absorbErrorsAsync, absorbErrorsSync } from \"./_internal/errors\";\nimport {\n DEFAULT_ENDPOINT,\n getApiKey as readApiKeyFromEnv,\n getEndpoint as readEndpointFromEnv,\n getFlushIntervalSeconds,\n getInternalSource as readInternalSourceFromEnv,\n getMaxBatchSize,\n getMaxFieldBytes,\n getMaxQueueSize,\n getSampleRate,\n isDisabled as readIsDisabled,\n isValidInternalSource,\n} from \"./_internal/env\";\nimport { maybeEnableDbCaptureFromEnv } from \"./_internal/db-capture\";\nimport { getEngine as getOtelEngine } from \"./_internal/otel-provider\";\nimport {\n buildRedactionConfig,\n type RedactionConfig,\n type RedactionOptions,\n redactValue,\n} from \"./_internal/redaction\";\nimport {\n BatchTransport,\n SDK_VERSION,\n type TelemetryEvent,\n getQuotaState as _getQuotaState,\n _resetQuotaStateForTesting,\n type QuotaState,\n} from \"./_internal/transport\";\nimport {\n getActiveTraceState as _getActiveTraceState,\n getLastTrace as _getLastTrace,\n runAsync as _runAsync,\n runSync as _runSync,\n sendTrace as _sendTrace,\n setContext as _setContext,\n setRedactionConfig,\n setSource as _setSource,\n setTransport,\n startSpan as _startSpan,\n type RunHandle,\n type RunOptions,\n type SpanHandle,\n type SpanOptions,\n spanAsync as _spanAsync,\n spanSync as _spanSync,\n _resetForTesting as _resetTracingForTesting,\n} from \"./_internal/tracing\";\nimport { report, _resetForTesting as _resetSentryForTesting } from \"./_internal/sentry-reporter\";\nimport { _setEnsureInitialized as _setLogEnsureInitialized } from \"./log\";\nimport {\n buildTraceEntry,\n generateSpanId,\n generateTraceId,\n nowIso,\n SpanType,\n type SpanData,\n type TraceFields,\n} from \"./_internal/spans\";\n\nexport type { SpanData, SpanType, SpanStatus, ContextSegment } from \"./_internal/spans\";\nexport type { RunHandle, RunOptions, SpanHandle, SpanOptions };\nexport type { QuotaState };\nexport type { RedactionOptions, RedactionConfig } from \"./_internal/redaction\";\nexport {\n extractTraceparent,\n formatTraceparent,\n injectTraceparent,\n injectIntoRequestInit,\n W3C_TRACEPARENT_HEADER,\n} from \"./propagation\";\nexport {\n extractAnthropicSegments,\n instrumentAnthropic,\n isAnthropicWrapped,\n uninstallAnthropic,\n wrapAnthropic,\n MAX_SEGMENT_CONTENT_BYTES,\n} from \"./anthropic\";\nexport type {\n AnthropicClientLike,\n AnthropicContentBlockParam,\n AnthropicMessageCreateParams,\n AnthropicMessageParam,\n AnthropicMessageResponse,\n AnthropicTextBlockParam,\n AnthropicToolParam,\n AnthropicToolResultBlockParam,\n AnthropicToolUseBlockParam,\n AnthropicUsage,\n InstrumentAnthropicOptions,\n} from \"./anthropic\";\nexport { log } from \"./log\";\nexport type { LogOptions, LogSeverity } from \"./log\";\n\nexport const version = SDK_VERSION;\n\n/* ------------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------------ */\n\n/** Options accepted by `init()`. Mirrors Python's `init(...)` kwargs. */\nexport interface InitOptions {\n /** API key (starts with \"mhq_\"). Required if no `MORSE_API_KEY` env var. */\n apiKey?: string;\n /** Full ingestion URL. Defaults to the production Railway endpoint. */\n endpoint?: string;\n /** Seconds between batch flushes. Default: 2. */\n flushIntervalSeconds?: number;\n /**\n * Client-side PII redaction. Default-on with the canonical eight-pattern\n * catalogue. Set `enabled: false` to opt out (the server-side mandatory\n * floor still applies). Mandatory patterns (`api_key_prefixed`, `jwt`,\n * `credit_card`, `ssn_us`) cannot be added to `disabledPatterns` —\n * `init()` throws on that.\n */\n redaction?: RedactionOptions;\n /**\n * INTERNAL USE ONLY — resource-level source label for Morse's own\n * dogfood emitters. Mirrors the Python SDK's `init(source=...)` kwarg\n * (PR #90). Accepted values: `\"copilot\" | \"investigator\" | \"refagent\"\n * | \"ui-bot\"`. Any other value (including misspellings) is silently\n * dropped — falls back to the `MORSE_INTERNAL_SOURCE` env var.\n * Customers should not set this.\n */\n source?: string;\n}\n\n/** Single agent-run payload accepted by `track()`. */\nexport interface TrackOptions {\n /** User-defined agent name (e.g., \"lead-qualifier\"). */\n agentId: string;\n /** Run-data dict. Shape mirrors backend Run model. */\n runData: Record<string, unknown>;\n}\n\n/** High-level outcome shape accepted by `record()` (V1 ergonomic API). */\nexport interface RunRecord {\n agent: string;\n success?: boolean;\n outcome?: string;\n cost?: number;\n model?: string;\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n durationMs?: number;\n errorMessage?: string;\n metadata?: Record<string, unknown>;\n status?: \"completed\" | \"failed\" | \"timed_out\";\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level singleton state */\n/* ------------------------------------------------------------------------ */\n\ninterface SingletonState {\n apiKey: string;\n endpoint: string;\n transport: BatchTransport;\n redaction: RedactionConfig;\n}\n\nlet singleton: SingletonState | null = null;\nlet transportFactory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n}) => BatchTransport = (opts) => new BatchTransport(opts);\n\nlet noApiKeyWarningEmitted = false;\nlet lastLoggedEndpoint: string | null = null;\n\nfunction logger(level: \"info\" | \"warn\", message: string): void {\n const out = level === \"warn\" ? console.warn : console.info;\n out.call(console, message);\n}\n\nfunction logDestinationOnce(endpoint: string): void {\n if (endpoint === lastLoggedEndpoint) return;\n lastLoggedEndpoint = endpoint;\n logger(\n \"info\",\n `morse: Morse SDK initialized — sending to ${endpoint} (sdk version ${SDK_VERSION})`,\n );\n}\n\nfunction resolveEndpoint(explicit?: string): string {\n if (explicit && explicit.length > 0) return explicit;\n const fromEnv = readEndpointFromEnv();\n if (fromEnv) return fromEnv;\n return DEFAULT_ENDPOINT;\n}\n\nconst OTLP_TRACES_PATH = \"/api/v1/otlp/v1/traces\";\n\n/**\n * Derive the OTLP ingest URL from the legacy JSON traces batch URL. Keeps\n * host/scheme; replaces the path — mirrors the Python SDK's\n * `client.py::_derive_otlp_endpoint` and this file's own\n * `transport.ts::deriveLogsEndpoint`.\n */\nfunction deriveOtlpEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = OTLP_TRACES_PATH;\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, OTLP_TRACES_PATH);\n }\n}\n\nfunction buildSingleton(opts: InitOptions, redaction: RedactionConfig): SingletonState | null {\n if (readIsDisabled()) {\n return null;\n }\n const apiKey = opts.apiKey ?? readApiKeyFromEnv();\n if (!apiKey) {\n if (!noApiKeyWarningEmitted) {\n logger(\n \"warn\",\n \"morse: not initialized — set MORSE_API_KEY or call morse.init({ apiKey }) (this warning prints once per process; SDK calls are silent no-ops)\",\n );\n noApiKeyWarningEmitted = true;\n }\n return null;\n }\n const endpoint = resolveEndpoint(opts.endpoint);\n const flushIntervalSeconds = opts.flushIntervalSeconds ?? getFlushIntervalSeconds();\n const transport = transportFactory({ apiKey, endpoint, flushIntervalSeconds });\n setTransport(transport);\n setRedactionConfig(redaction);\n // Resolve the internal source label: explicit kwarg wins, then env.\n // Invalid values are silently dropped so a typo never tags customer\n // data as dogfood.\n const explicitSource = isValidInternalSource(opts.source) ? opts.source! : undefined;\n const resolvedSource = explicitSource ?? readInternalSourceFromEnv() ?? null;\n _setSource(resolvedSource);\n logDestinationOnce(endpoint);\n\n // MHQ-482: run()/span()/runTraced()'s real emission pipeline — real OTel\n // spans over OTLP protobuf+gzip. Configured unconditionally (even under\n // MORSE_LEGACY_TRANSPORT=1) so the engine is ready if the flag flips at\n // runtime; the flag only affects which pipeline `tracing.ts::sendTrace()`\n // feeds. Mirrors the Python SDK's `_Client.__init__`.\n try {\n getOtelEngine().configure({\n endpoint: deriveOtlpEndpoint(endpoint),\n apiKey,\n sampleRate: getSampleRate(),\n scheduleDelayMillis: flushIntervalSeconds * 1000,\n maxQueueSize: getMaxQueueSize(),\n maxExportBatchSize: getMaxBatchSize(),\n maxFieldBytes: getMaxFieldBytes(),\n // 10s (not the exporter's un-overridden 30s ceiling): a genuine\n // network blip still gets several retries with backoff; a fully dead\n // endpoint fails within a bounded, shutdown-friendly window instead\n // of the library's full 30s ceiling. Mirrors the Python SDK.\n timeoutMillis: 10_000,\n redactionConfig: redaction,\n });\n } catch (err) {\n report(err, { fingerprint: \"morse.otel_provider.configure\" });\n }\n\n // MHQ-482: zero-config adapter auto-detect (mirrors Python's\n // `auto_detect.install_adapters()` call in `_ensure_initialized` /\n // `_init_impl`). Fire-and-forget — telemetry setup must never block or\n // throw back into the customer's init() / first SDK call. Runs AFTER the\n // OTel engine above is configured so any span an auto-installed adapter\n // emits immediately has a working pipeline to flow through.\n void _installAdapters().catch(() => {\n // installAdapters() already absorbs adapter-level failures; this is a\n // defensive backstop so a totally unexpected rejection never surfaces.\n });\n\n // MHQ-482: DB auto-capture (opt-in — MORSE_CAPTURE_DB / MORSE_CAPTURE_INFRA,\n // mirrors the Python SDK's `infra_capture.py::maybe_enable_from_env`).\n // MUST run after `getOtelEngine().configure()` above — instrumentation\n // registration needs the `NodeTracerProvider` that call just built.\n try {\n maybeEnableDbCaptureFromEnv();\n } catch (err) {\n report(err, { fingerprint: \"morse.db_capture.register\" });\n }\n\n return { apiKey, endpoint, transport, redaction };\n}\n\nfunction ensureInitialized(): SingletonState | null {\n if (readIsDisabled()) return null;\n if (singleton !== null) return singleton;\n // Lazy default-init: defaults are safe (default-on redaction).\n singleton = buildSingleton({}, buildRedactionConfig());\n return singleton;\n}\n\n// Wire the lazy-init hook into log.ts so `log()` participates in the\n// same env-driven auto-init flow as `track()` / `record()`. Discards\n// the return value — log.ts only needs the side-effect of populating\n// the module-level singleton + transport.\n_setLogEnsureInitialized(() => {\n ensureInitialized();\n});\n\n/* ------------------------------------------------------------------------ */\n/* Public API — wrapped at the module surface */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Initialize the Morse SDK singleton. Call once at application\n * startup. After this call, `track()`, `batchTrack()`, `run()`, and\n * `flush()` are wired up to the configured endpoint.\n *\n * `endpoint` is optional — when omitted, the SDK resolves in this order:\n * 1. `MORSE_ENDPOINT` env var (if set)\n * 2. The default Railway production URL.\n *\n * `MORSE_DISABLED=1` overrides everything: `init()` is a no-op.\n *\n * Calling `init()` a second time replaces the singleton — the previous\n * transport is shut down cleanly and a new one is constructed.\n *\n * Validation errors (e.g., disabling a mandatory redaction pattern, an\n * uncompilable extra pattern) are deliberately NOT absorbed — they\n * propagate as `Error`s so the developer sees the misconfiguration at\n * application startup. All other errors raised from internal SDK paths\n * are absorbed via the per-call wrappers downstream.\n */\nexport function init(options: InitOptions = {}): void {\n if (readIsDisabled()) {\n logger(\"info\", \"morse: MORSE_DISABLED=1 — SDK is a no-op\");\n return;\n }\n // Validate redaction options FIRST — must throw before we mutate any\n // singleton state, so a misconfigured init leaves the previous client\n // intact and the spec scenario \"no Client is constructed\" holds.\n const redaction = buildRedactionConfig(options.redaction);\n\n try {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n }\n singleton = buildSingleton(options, redaction);\n } catch (err) {\n report(err, { fingerprint: \"morse.init\" });\n }\n}\n\n/**\n * Submit a single agent run to the ingestion endpoint. Non-blocking;\n * returns immediately. Silently no-ops if the SDK isn't initialised.\n */\nexport const track = absorbErrorsSync(\n (options: TrackOptions): void => {\n const state = ensureInitialized();\n if (!state) return;\n const payload = {\n agent_id: options.agentId,\n ...options.runData,\n };\n const redacted = redactValue(payload, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n },\n { fingerprint: \"morse.track\" },\n);\n\n/** Submit multiple agent runs in bulk. Silently no-ops if uninitialised. */\nexport const batchTrack = absorbErrorsSync(\n (runs: Array<Record<string, unknown>>): void => {\n const state = ensureInitialized();\n if (!state) return;\n for (const run of runs) {\n const redacted = redactValue(run, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n }\n },\n { fingerprint: \"morse.batch_track\" },\n);\n\n/**\n * High-level convenience: record an agent outcome with named fields.\n *\n * Builds a synthetic single-span `agent`-type trace and submits it through\n * the same `traces`-shaped emission path `run()`/`span()` use (MHQ-746) —\n * mirrors Python's `client.py::record()`. The live dev-api backend has no\n * `runs` ingest route; sending through `state.transport.track()` (the\n * shape `track()`/`batchTrack()` still use, matching Python's equally\n * unmigrated `track()`/`batch_track()`) 422s silently. See MHQ-746.\n */\nexport const record = absorbErrorsSync(\n (rec: RunRecord): void => {\n const state = ensureInitialized();\n if (!state) return;\n\n const traceId = generateTraceId();\n const now = nowIso();\n const resolvedStatus = rec.errorMessage ? \"failed\" : (rec.status ?? \"completed\");\n\n const span: SpanData = {\n trace_id: traceId,\n span_id: generateSpanId(),\n type: SpanType.AGENT,\n name: rec.agent,\n started_at: now,\n ended_at: now,\n status: resolvedStatus,\n duration_ms: rec.durationMs,\n model: rec.model,\n input_tokens: rec.inputTokens,\n output_tokens: rec.outputTokens,\n total_tokens: rec.totalTokens,\n cost_usd: rec.cost,\n error_message: rec.errorMessage,\n metadata: rec.metadata,\n };\n\n const traceFields: TraceFields = {\n trace_id: traceId,\n agent: rec.agent,\n status: resolvedStatus,\n started_at: now,\n ended_at: now,\n duration_ms: rec.durationMs,\n error_message: rec.errorMessage,\n success: rec.success,\n outcome: rec.outcome,\n };\n\n const entry = buildTraceEntry(traceFields, [span]);\n const redacted = redactValue(entry, state.redaction) as {\n trace: Record<string, unknown>;\n spans: Record<string, unknown>[];\n };\n _sendTrace(redacted);\n },\n { fingerprint: \"morse.record\" },\n);\n\n/**\n * Open a traced agent run, executing `fn` inside the trace context.\n * Mirrors Python's `morse.run(...)` context manager.\n *\n * Two overloads are supported:\n * - `run(name, fn)` — quick form with default options.\n * - `run({ agentName, incomingTraceparent }, fn)` — full options.\n *\n * Async callers receive a Promise; sync callers receive the value\n * directly. The function's return value is forwarded to the caller.\n */\nexport function run<T>(name: string, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(options: RunOptions, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(\n nameOrOpts: string | RunOptions,\n fn: (handle: RunHandle) => T,\n): T | undefined {\n const opts = typeof nameOrOpts === \"string\" ? { agentName: nameOrOpts } : nameOrOpts;\n // Make sure the SDK is initialised so spans get sent. Failure to init\n // is fine — `_runSync` records to `lastTrace` regardless, useful for tests.\n ensureInitialized();\n // We can't reliably detect async vs sync up front in TypeScript, so we\n // route through `_runSync` and, if the user's `fn` returns a promise,\n // the wrapping trace ends synchronously after the promise is created.\n // For correct async lifecycle, callers should use `runTraced` instead\n // (it awaits the promise inside the `als.run` scope so the trace\n // closes when the promise resolves).\n return _runSync(opts, fn);\n}\n\n/** Async variant of `run` — exported for callers who want explicit typing. */\nexport function runTraced<T>(\n options: RunOptions,\n fn: (handle: RunHandle) => Promise<T>,\n): Promise<T | undefined> {\n ensureInitialized();\n return _runAsync(options, fn);\n}\n\n/**\n * Attach cost-attribution dimensions to the active trace (MHQ-751). Call\n * once per request (e.g. in middleware) to propagate `customerId`,\n * `feature`, `env`, `team`, etc. to all child spans downstream. Values are\n * coerced to strings. No-op outside an active trace — never throws.\n * Mirrors Python's `morse_ai.set_context(**dims)`.\n */\nexport function setContext(dims: Record<string, unknown>): void {\n _setContext(dims);\n}\n\n/** Start a child span under the active trace. Returns null if no trace. */\nexport function startSpan(options: SpanOptions): SpanHandle | null {\n return _startSpan(options);\n}\n\n/** Run `fn` inside a child span. No-op if no trace is active. */\nexport function span<T>(options: SpanOptions, fn: (handle: SpanHandle | null) => T): T {\n return _spanSync(options, fn);\n}\n\n/** Async variant of `span`. */\nexport function spanAsync<T>(\n options: SpanOptions,\n fn: (handle: SpanHandle | null) => Promise<T>,\n): Promise<T> {\n return _spanAsync(options, fn);\n}\n\n/** Block until all queued events have been sent. Silent on uninit / errors. */\nexport const flush = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.flush();\n await getOtelEngine().flush();\n }\n },\n { fingerprint: \"morse.flush\" },\n);\n\n/**\n * Latest ingest quota / rate-limit state observed by the SDK, or `null`.\n *\n * TQE-04 (MHQ-676/MHQ-749): when the Morse backend answers a telemetry\n * batch with HTTP 429 — the monthly event limit (`event_limit_exceeded`)\n * or the per-second ingest rate limit (`RATE_LIMIT_INGEST`) — the\n * transport parses the response and stores it. This function exposes\n * that state so host applications can alert on quota exhaustion instead\n * of discovering it from missing telemetry. Mirrors Python's\n * `morse_ai.quota_state()`.\n *\n * Returns `null` when the SDK is uninitialized or no 429 has been\n * observed. The object includes at least `kind` (`\"event_limit\"`,\n * `\"rate_limit\"`, or `\"unknown\"`), `status`, and `receivedAt` (epoch\n * seconds); event-limit states add `currentCount` / `limitCount` /\n * `planId` / `periodStart` / `upgradeUrl`.\n */\nexport const quotaState = absorbErrorsSync(\n (): QuotaState | null => {\n if (singleton === null) return null;\n return _getQuotaState();\n },\n { fingerprint: \"morse.quota_state\" },\n);\n\n/** Flush and stop the background transport. Call on app exit. */\nexport const shutdown = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.shutdown();\n await getOtelEngine().flush();\n getOtelEngine().shutdown();\n singleton = null;\n setTransport(null);\n }\n },\n { fingerprint: \"morse.shutdown\" },\n);\n\n/* ------------------------------------------------------------------------ */\n/* Test-only hooks (private) */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Reset the module-level singleton for tests. Customer code must NOT\n * call this — the singleton is meant to live for the process lifetime.\n *\n * @internal\n */\nexport function _resetForTesting(): void {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n setTransport(null);\n }\n getOtelEngine().resetForTesting();\n noApiKeyWarningEmitted = false;\n lastLoggedEndpoint = null;\n _setSource(null);\n _resetTracingForTesting();\n _resetSentryForTesting();\n _resetAutoDetectForTesting();\n _resetQuotaStateForTesting();\n // Reset the transport factory back to the default.\n transportFactory = (opts) => new BatchTransport(opts);\n}\n\n/**\n * Inject an alternate transport factory for tests (e.g., a mock that\n * captures sent payloads instead of opening a fetch).\n *\n * @internal\n */\nexport function _setTransportFactoryForTesting(\n factory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n }) => BatchTransport,\n): void {\n transportFactory = factory;\n}\n\n/**\n * Read the most recently completed trace entry, for test assertions.\n *\n * @internal\n */\nexport function _getLastTraceForTesting(): { trace: unknown; spans: unknown[] } | null {\n return _getLastTrace();\n}\n\n/**\n * Read the active trace state, for test assertions.\n *\n * @internal\n */\nexport function _getActiveTraceStateForTesting(): unknown {\n return _getActiveTraceState();\n}\n","/**\n * Browser replacement for `db-capture.ts` (MHQ-482 follow-up).\n *\n * `db-capture.ts` statically imports `@opentelemetry/instrumentation-pg`,\n * `-mysql2` and `@opentelemetry/instrumentation` (which reaches for node's\n * `module` via require-in-the-middle). A browser has no pg/mysql2 driver to\n * patch, so the whole module is inert there — but the static imports still\n * dragged those packages, and node `module`, into the browser graph.\n *\n * Swapped in by the esbuild plugin in `tsup.config.ts`; see\n * `otel-provider.browser.ts` for the full rationale. Only the symbols\n * `index.ts` imports need to exist here.\n */\n\n/**\n * No-op: there is no database driver to instrument in a browser.\n *\n * Deliberately silent. `otel-provider.browser.ts::noteBrowserRuntime()`\n * already states that DB capture is Node-only, and it fires on\n * `configure()` — which `index.ts` always calls before this. A second\n * message would be noise, and this function runs even when the customer\n * never asked for DB capture.\n */\nexport function maybeEnableDbCaptureFromEnv(): void {}\n","/**\n * Background batched HTTP transport.\n *\n * Mirrors the Python SDK's `transport.py` semantics:\n *\n * - Async batching: 2-second flush interval (configurable), 100-event\n * max batch size (configurable). Whichever fires first triggers a\n * flush.\n * - Network failures are silently dropped with a debug log — never\n * thrown to the caller.\n * - The caller's thread / event loop is never blocked: `track()`\n * enqueues and returns immediately.\n *\n * The transport speaks two payload shapes (matching Python):\n *\n * - V2 trace events: `{ traces: [{ trace, spans }, ...] }`\n * - V1 legacy runs: `{ runs: [{ ... }, ...] }`\n *\n * Wire-format routing checks for a `traces` key on each enqueued event.\n *\n * Browser vs Node:\n * - Both runtimes ship `fetch` (Node 18+ / all evergreens).\n * - In Node we can run a `setInterval` and call it a day.\n * - In the browser the same approach works; we don't currently flush\n * on `pagehide` / `beforeunload` — Phase 6c spec doesn't require it.\n * A future change can add `navigator.sendBeacon` for last-gasp delivery.\n */\n\nimport { report } from \"./sentry-reporter\";\n\n// Injected at build time by tsup from package.json — see tsup.config.ts.\n// The version is declared in exactly one place (package.json); a copy here\n// would drift the moment a release bumped one and not the other, and the\n// stale value rides out on the User-Agent and on every span's `sdk_version`.\n// `__SDK_VERSION__` is undefined when running straight from source (vitest,\n// ts-node), hence the fallback.\ndeclare const __SDK_VERSION__: string | undefined;\n\nexport const SDK_VERSION = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\nexport interface BatchTransportOptions {\n endpoint: string;\n apiKey: string;\n /** Max wait between flushes, in seconds. Default: 2.0. */\n flushIntervalSeconds?: number;\n /** Max events per batch. Default: 100. */\n maxBatchSize?: number;\n /** Max queued events before drop. Default: 10_000. */\n maxQueueSize?: number;\n /** Timeout for the underlying fetch, in ms. Default: 5_000. */\n fetchTimeoutMs?: number;\n /**\n * Pluggable fetch implementation. Defaults to `globalThis.fetch`.\n * Useful for tests.\n */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Wire shape for a single log record. Mirrors the Python SDK payload at\n * `apps/sdk/python/src/morse/client.py:191-211` and the backend\n * `SimpleLogsIngestController.ingest` contract\n * (`apps/api/apps/logs/controllers.py:66-134`).\n */\nexport interface LogRecordPayload {\n timestamp: string;\n severity: string;\n service_name: string;\n body: string;\n attributes: Record<string, unknown>;\n trace_id: string | null;\n span_id: string | null;\n}\n\nexport type TelemetryEvent =\n | { traces: unknown[] }\n | { logs: LogRecordPayload[] }\n | Record<string, unknown>;\n\n/**\n * Quota / rate-limit state parsed from a 429 batch-POST response.\n *\n * Mirrors Python's `transport.py::BatchTransport._record_quota_state` /\n * `quota_state()` (TQE-04 / MHQ-676) field-for-field, camelCased per this\n * codebase's convention (`received_at` -> `receivedAt`, etc.). Two known\n * body shapes:\n *\n * - `event_limit_exceeded` (traces controllers) — flat body with\n * `current_count` / `limit_count` / `plan_id` / `period_start` /\n * `upgrade_url` / `sample_rate_url`.\n * - `RATE_LIMIT_INGEST` (ingest rate-limit middleware) — nested\n * `{ error: { code, message, details: { limit, plan } } }`.\n *\n * Anything else is recorded as `kind: \"unknown\"` so the state surface\n * still reflects that ingest is throttled even when the body doesn't\n * parse into a known shape.\n */\nexport interface QuotaState {\n kind: \"event_limit\" | \"rate_limit\" | \"unknown\";\n status: number;\n /** Epoch seconds, matching Python's `time.time()`. */\n receivedAt: number;\n message?: string;\n currentCount?: number;\n limitCount?: number;\n planId?: string;\n periodStart?: string;\n upgradeUrl?: string;\n sampleRateUrl?: string;\n quotaLimit?: number;\n quotaRemaining?: number;\n quotaReset?: number;\n retryAfterSeconds?: number;\n}\n\n// `Retry-After` values above this are clamped, mirroring Python's\n// `_RETRY_AFTER_CAP_SECONDS` — a background flush loop must never commit\n// to reporting minutes-long waits from one batch.\nconst RETRY_AFTER_CAP_SECONDS = 30.0;\n\n/**\n * Parse a `Retry-After` header (seconds form only), capped.\n *\n * Mirrors Python's `transport.py::_parse_retry_after`. HTTP-date form\n * isn't worth supporting — Morse's own 429s always use the seconds form.\n * Unparseable / negative values return `null`.\n */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number(value);\n if (!Number.isFinite(seconds) || seconds < 0) return null;\n return Math.min(seconds, RETRY_AFTER_CAP_SECONDS);\n}\n\n// Module-level, matching the `lastTrace` test-introspection pattern in\n// `_internal/tracing.ts` — the latest quota/rate-limit state observed on\n// a 429 response, exposed via `quotaState()` / `morse.quotaState()`. Not\n// cleared on a subsequent successful POST (mirrors Python: it persists\n// until overwritten by a newer 429, so a host app can see \"quota was\n// exceeded recently\" even after the queue drains — see\n// `test_429_recovers_when_retry_succeeds` in the Python test suite,\n// which only asserts no drop-warning, never that state was cleared).\nlet quotaState: QuotaState | null = null;\n\n/**\n * Latest quota/rate-limit state observed on a 429, or `null` if none has\n * been observed yet. Returns a shallow copy — callers can't mutate\n * transport-internal state. Mirrors Python's `BatchTransport.quota_state()`.\n *\n * @internal\n */\nexport function getQuotaState(): QuotaState | null {\n return quotaState ? { ...quotaState } : null;\n}\n\n/**\n * Reset the module-level quota state between test files/cases.\n *\n * @internal\n */\nexport function _resetQuotaStateForTesting(): void {\n quotaState = null;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction numberFromHeader(raw: string | null): number | undefined {\n if (raw === null) return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Parse a 429 response into the module-level quota state. Never throws —\n * defensively parses two known body shapes (see `QuotaState`), falling\n * back to `kind: \"unknown\"` for anything else. Mirrors Python's\n * `_record_quota_state`.\n */\nasync function recordQuotaState(resp: Response): Promise<void> {\n try {\n const state: QuotaState = {\n kind: \"unknown\",\n status: 429,\n receivedAt: Date.now() / 1000,\n };\n\n let data: unknown = null;\n try {\n data = await resp.clone().json();\n } catch {\n data = null;\n }\n\n if (data !== null && typeof data === \"object\") {\n const body = data as Record<string, unknown>;\n const err = body.error;\n if (err === \"event_limit_exceeded\") {\n state.kind = \"event_limit\";\n state.message = asString(body.message);\n state.currentCount = asNumber(body.current_count);\n state.limitCount = asNumber(body.limit_count);\n state.planId = asString(body.plan_id);\n state.periodStart = asString(body.period_start);\n state.upgradeUrl = asString(body.upgrade_url);\n state.sampleRateUrl = asString(body.sample_rate_url);\n } else if (\n err !== null &&\n typeof err === \"object\" &&\n (err as Record<string, unknown>).code === \"RATE_LIMIT_INGEST\"\n ) {\n const errObj = err as Record<string, unknown>;\n const details =\n errObj.details !== null && typeof errObj.details === \"object\"\n ? (errObj.details as Record<string, unknown>)\n : {};\n state.kind = \"rate_limit\";\n state.message = asString(errObj.message);\n state.limitCount = asNumber(details.limit);\n state.planId = asString(details.plan);\n }\n }\n\n // TQE-05 headers, emitted by the server on every ingest response.\n const quotaLimit = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Limit\"));\n if (quotaLimit !== undefined) state.quotaLimit = quotaLimit;\n const quotaRemaining = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Remaining\"));\n if (quotaRemaining !== undefined) state.quotaRemaining = quotaRemaining;\n const quotaReset = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Reset\"));\n if (quotaReset !== undefined) state.quotaReset = quotaReset;\n const retryAfter = parseRetryAfter(resp.headers.get(\"Retry-After\"));\n if (retryAfter !== null) state.retryAfterSeconds = retryAfter;\n\n quotaState = state;\n } catch {\n // Never let state capture break the send path.\n }\n}\n\nexport class BatchTransport {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly flushIntervalSeconds: number;\n private readonly maxBatchSize: number;\n private readonly maxQueueSize: number;\n private readonly fetchTimeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n private queue: TelemetryEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private closed = false;\n private inflight: Promise<void> | null = null;\n\n constructor(opts: BatchTransportOptions) {\n this.endpoint = opts.endpoint;\n this.apiKey = opts.apiKey;\n this.flushIntervalSeconds = opts.flushIntervalSeconds ?? 2.0;\n this.maxBatchSize = opts.maxBatchSize ?? 100;\n this.maxQueueSize = opts.maxQueueSize ?? 10_000;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 5_000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);\n\n // setInterval keeps the Node event loop alive. We mark it `unref`\n // when available so a process-exit isn't blocked by a quiet flusher.\n const intervalMs = Math.max(50, Math.round(this.flushIntervalSeconds * 1000));\n this.timer = setInterval(() => {\n void this.tick();\n }, intervalMs);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const t = this.timer as any;\n if (typeof t?.unref === \"function\") {\n t.unref();\n }\n }\n\n /** Enqueue an event. Non-blocking. Returns false if the queue is full. */\n track(event: TelemetryEvent): boolean {\n if (this.closed) return false;\n if (this.queue.length >= this.maxQueueSize) return false;\n this.queue.push(event);\n if (this.queue.length >= this.maxBatchSize) {\n void this.tick();\n }\n return true;\n }\n\n /** Block until all currently-queued events have been sent. */\n async flush(): Promise<void> {\n // Drain in one-batch chunks until empty, then await the in-flight send.\n while (this.queue.length > 0) {\n await this.tick();\n }\n if (this.inflight) {\n try {\n await this.inflight;\n } catch {\n // Swallow — caller observes nothing.\n }\n }\n }\n\n /** Stop the background timer. After this call, `track()` is a no-op. */\n async shutdown(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private async tick(): Promise<void> {\n if (this.flushing) return;\n if (this.queue.length === 0) return;\n this.flushing = true;\n try {\n const batch = this.queue.splice(0, this.maxBatchSize);\n this.inflight = this.sendBatch(batch);\n await this.inflight;\n this.inflight = null;\n } finally {\n this.flushing = false;\n }\n }\n\n private async sendBatch(batch: TelemetryEvent[]): Promise<void> {\n const traceEntries: unknown[] = [];\n const runEntries: Record<string, unknown>[] = [];\n const logEntries: LogRecordPayload[] = [];\n for (const event of batch) {\n if (typeof event === \"object\" && event !== null && \"traces\" in event) {\n const e = event as { traces: unknown[] };\n if (Array.isArray(e.traces)) {\n traceEntries.push(...e.traces);\n }\n } else if (typeof event === \"object\" && event !== null && \"logs\" in event) {\n const e = event as { logs: LogRecordPayload[] };\n if (Array.isArray(e.logs)) {\n logEntries.push(...e.logs);\n }\n } else {\n runEntries.push(event as Record<string, unknown>);\n }\n }\n if (traceEntries.length > 0) {\n await this.post(this.endpoint, { traces: traceEntries });\n }\n if (runEntries.length > 0) {\n await this.post(this.endpoint, { runs: runEntries });\n }\n if (logEntries.length > 0) {\n // Phase 12.5 Tier 1.2 — logs go to a derived `/api/v1/logs/ingest`\n // endpoint on the same host as the traces batch URL. Mirrors the\n // Python SDK's URL derivation in\n // `apps/sdk/python/src/morse/client.py:187-190`.\n await this.post(deriveLogsEndpoint(this.endpoint), { logs: logEntries });\n }\n }\n\n private async post(url: string, payload: Record<string, unknown>): Promise<void> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.fetchTimeoutMs);\n try {\n const resp = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `morse-typescript/${SDK_VERSION}`,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n if (resp.status === 429) {\n // TQE-04 (MHQ-676) parity: surface quota/rate-limit state instead\n // of silently dropping telemetry. `resp.clone()` inside so the\n // body-snippet read below still works off the original response.\n await recordQuotaState(resp);\n }\n if (!resp.ok) {\n // Drain a few hundred chars of the body for diagnostics.\n let bodySnippet = \"\";\n try {\n const text = await resp.text();\n bodySnippet = text.slice(0, 200);\n } catch {\n // ignore\n }\n report(new Error(`morse: batch POST returned ${resp.status} ${bodySnippet}`), {\n fingerprint: \"morse.transport.post.non_2xx\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n }\n } catch (err) {\n // Network errors / aborts / timeouts — silent drop, out-of-band report.\n report(err, {\n fingerprint: \"morse.transport.post.network\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n\nfunction hostnameOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return \"<invalid>\";\n }\n}\n\n/**\n * Derive the logs ingest URL from the traces batch endpoint. Mirrors the\n * Python SDK helper: replace the path with `/api/v1/logs/ingest` and drop\n * any query/fragment.\n *\n * Exported for tests. Customer code should not call this directly.\n *\n * @internal\n */\nexport function deriveLogsEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = \"/api/v1/logs/ingest\";\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n // Fall back to a string replace on the most common shape.\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, \"/api/v1/logs/ingest\");\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/_internal/auto-detect.browser.ts","../../src/index.ts","../../src/_internal/db-capture.browser.ts","../../src/_internal/transport.ts"],"names":["run","span","setContext","startSpan","spanAsync","quotaState","_resetForTesting"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Browser replacement for `auto-detect.ts` (MHQ-482 follow-up).\n *\n * `auto-detect.ts` already no-ops at RUNTIME outside Node — its own\n * docstring says so — but it reaches for `node:module`'s `createRequire` /\n * `Module` in a static top-level import, so a bundler still had to resolve\n * `module` for the browser graph. The runtime guard was never the problem;\n * the import was.\n *\n * Nothing here is a behaviour change: zero-config adapter auto-detect works\n * by hooking `require()` to patch LLM SDKs as they load, and a browser has\n * no `require` to hook. See `otel-provider.browser.ts` for the full\n * rationale and the build wiring.\n */\n\n/** No-op: there is no `require` to hook in a browser. */\nexport async function installAdapters(): Promise<void> {}\n\n/** No state to reset in the browser build. */\nexport function _resetForTesting(): void {}\n","/**\n * `@morsehq-dev/sdk` — TypeScript SDK for Morse observability.\n *\n * V2 module-level singleton API:\n *\n * ```ts\n * import * as morse from \"@morsehq-dev/sdk\";\n *\n * // Three-step zero-setup integration:\n * // 1. Generate an API key in the Morse dashboard.\n * // 2. npm install @morsehq-dev/sdk\n * // 3. Set MORSE_API_KEY in your environment.\n *\n * morse.track({ agentId: \"lead-qualifier\", runData: { success: true } });\n *\n * // Or call init() explicitly to override env-var resolution:\n * morse.init({\n * apiKey: \"mhq_xxx\", // pragma: allowlist secret — placeholder, not a key\n * endpoint: \"https://api.morsehq.dev/api/v1/traces/batch\",\n * flushIntervalSeconds: 2,\n * });\n * ```\n *\n * Non-invasive guarantee: every public function is wrapped with\n * `absorbErrors`. Telemetry never throws back at the caller; internal\n * errors are reported out-of-band to Morse's Sentry instance.\n *\n * The internal `BatchTransport` and `TraceState` types back the singleton\n * but are NOT part of the public API. Customers should never reach into\n * the `_internal` directory.\n */\n\nimport {\n installAdapters as _installAdapters,\n _resetForTesting as _resetAutoDetectForTesting,\n} from \"./_internal/auto-detect\";\nimport { absorbErrorsAsync, absorbErrorsSync } from \"./_internal/errors\";\nimport {\n DEFAULT_ENDPOINT,\n getApiKey as readApiKeyFromEnv,\n getEndpoint as readEndpointFromEnv,\n getFlushIntervalSeconds,\n getInternalSource as readInternalSourceFromEnv,\n getMaxBatchSize,\n getMaxFieldBytes,\n getMaxQueueSize,\n getSampleRate,\n isDisabled as readIsDisabled,\n isValidInternalSource,\n} from \"./_internal/env\";\nimport { maybeEnableDbCaptureFromEnv } from \"./_internal/db-capture\";\nimport { getEngine as getOtelEngine } from \"./_internal/otel-provider\";\nimport {\n buildRedactionConfig,\n type RedactionConfig,\n type RedactionOptions,\n redactValue,\n} from \"./_internal/redaction\";\nimport {\n BatchTransport,\n SDK_VERSION,\n type TelemetryEvent,\n getQuotaState as _getQuotaState,\n _resetQuotaStateForTesting,\n type QuotaState,\n} from \"./_internal/transport\";\nimport {\n getActiveTraceState as _getActiveTraceState,\n getLastTrace as _getLastTrace,\n runAsync as _runAsync,\n runSync as _runSync,\n sendTrace as _sendTrace,\n setContext as _setContext,\n setRedactionConfig,\n setSource as _setSource,\n setTransport,\n startSpan as _startSpan,\n type RunHandle,\n type RunOptions,\n type SpanHandle,\n type SpanOptions,\n spanAsync as _spanAsync,\n spanSync as _spanSync,\n _resetForTesting as _resetTracingForTesting,\n} from \"./_internal/tracing\";\nimport { report, _resetForTesting as _resetSentryForTesting } from \"./_internal/sentry-reporter\";\nimport { _setEnsureInitialized as _setLogEnsureInitialized } from \"./log\";\nimport {\n buildTraceEntry,\n generateSpanId,\n generateTraceId,\n nowIso,\n SpanType,\n type SpanData,\n type TraceFields,\n} from \"./_internal/spans\";\n\nexport type { SpanData, SpanType, SpanStatus, ContextSegment } from \"./_internal/spans\";\nexport type { RunHandle, RunOptions, SpanHandle, SpanOptions };\nexport type { QuotaState };\nexport type { RedactionOptions, RedactionConfig } from \"./_internal/redaction\";\nexport {\n extractTraceparent,\n formatTraceparent,\n injectTraceparent,\n injectIntoRequestInit,\n W3C_TRACEPARENT_HEADER,\n} from \"./propagation\";\nexport {\n extractAnthropicSegments,\n instrumentAnthropic,\n isAnthropicWrapped,\n uninstallAnthropic,\n wrapAnthropic,\n MAX_SEGMENT_CONTENT_BYTES,\n} from \"./anthropic\";\nexport type {\n AnthropicClientLike,\n AnthropicContentBlockParam,\n AnthropicMessageCreateParams,\n AnthropicMessageParam,\n AnthropicMessageResponse,\n AnthropicTextBlockParam,\n AnthropicToolParam,\n AnthropicToolResultBlockParam,\n AnthropicToolUseBlockParam,\n AnthropicUsage,\n InstrumentAnthropicOptions,\n} from \"./anthropic\";\nexport { log } from \"./log\";\nexport type { LogOptions, LogSeverity } from \"./log\";\n\nexport const version = SDK_VERSION;\n\n/* ------------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------------ */\n\n/** Options accepted by `init()`. Mirrors Python's `init(...)` kwargs. */\nexport interface InitOptions {\n /** API key (starts with \"mhq_\"). Required if no `MORSE_API_KEY` env var. */\n apiKey?: string;\n /** Full ingestion URL. Defaults to the production Railway endpoint. */\n endpoint?: string;\n /** Seconds between batch flushes. Default: 2. */\n flushIntervalSeconds?: number;\n /**\n * Client-side PII redaction. Default-on with the canonical eight-pattern\n * catalogue. Set `enabled: false` to opt out (the server-side mandatory\n * floor still applies). Mandatory patterns (`api_key_prefixed`, `jwt`,\n * `credit_card`, `ssn_us`) cannot be added to `disabledPatterns` —\n * `init()` throws on that.\n */\n redaction?: RedactionOptions;\n /**\n * INTERNAL USE ONLY — resource-level source label for Morse's own\n * dogfood emitters. Mirrors the Python SDK's `init(source=...)` kwarg\n * (PR #90). Accepted values: `\"copilot\" | \"investigator\" | \"refagent\"\n * | \"ui-bot\"`. Any other value (including misspellings) is silently\n * dropped — falls back to the `MORSE_INTERNAL_SOURCE` env var.\n * Customers should not set this.\n */\n source?: string;\n}\n\n/** Single agent-run payload accepted by `track()`. */\nexport interface TrackOptions {\n /** User-defined agent name (e.g., \"lead-qualifier\"). */\n agentId: string;\n /** Run-data dict. Shape mirrors backend Run model. */\n runData: Record<string, unknown>;\n}\n\n/** High-level outcome shape accepted by `record()` (V1 ergonomic API). */\nexport interface RunRecord {\n agent: string;\n success?: boolean;\n outcome?: string;\n cost?: number;\n model?: string;\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n durationMs?: number;\n errorMessage?: string;\n metadata?: Record<string, unknown>;\n status?: \"completed\" | \"failed\" | \"timed_out\";\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level singleton state */\n/* ------------------------------------------------------------------------ */\n\ninterface SingletonState {\n apiKey: string;\n endpoint: string;\n transport: BatchTransport;\n redaction: RedactionConfig;\n}\n\nlet singleton: SingletonState | null = null;\nlet transportFactory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n}) => BatchTransport = (opts) => new BatchTransport(opts);\n\nlet noApiKeyWarningEmitted = false;\nlet lastLoggedEndpoint: string | null = null;\n\nfunction logger(level: \"info\" | \"warn\", message: string): void {\n const out = level === \"warn\" ? console.warn : console.info;\n out.call(console, message);\n}\n\nfunction logDestinationOnce(endpoint: string): void {\n if (endpoint === lastLoggedEndpoint) return;\n lastLoggedEndpoint = endpoint;\n logger(\n \"info\",\n `morse: Morse SDK initialized — sending to ${endpoint} (sdk version ${SDK_VERSION})`,\n );\n}\n\nfunction resolveEndpoint(explicit?: string): string {\n if (explicit && explicit.length > 0) return explicit;\n const fromEnv = readEndpointFromEnv();\n if (fromEnv) return fromEnv;\n return DEFAULT_ENDPOINT;\n}\n\nconst OTLP_TRACES_PATH = \"/api/v1/otlp/v1/traces\";\n\n/**\n * Derive the OTLP ingest URL from the legacy JSON traces batch URL. Keeps\n * host/scheme; replaces the path — mirrors the Python SDK's\n * `client.py::_derive_otlp_endpoint` and this file's own\n * `transport.ts::deriveLogsEndpoint`.\n */\nfunction deriveOtlpEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = OTLP_TRACES_PATH;\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, OTLP_TRACES_PATH);\n }\n}\n\nfunction buildSingleton(opts: InitOptions, redaction: RedactionConfig): SingletonState | null {\n if (readIsDisabled()) {\n return null;\n }\n const apiKey = opts.apiKey ?? readApiKeyFromEnv();\n if (!apiKey) {\n if (!noApiKeyWarningEmitted) {\n logger(\n \"warn\",\n \"morse: not initialized — set MORSE_API_KEY or call morse.init({ apiKey }) (this warning prints once per process; SDK calls are silent no-ops)\",\n );\n noApiKeyWarningEmitted = true;\n }\n return null;\n }\n const endpoint = resolveEndpoint(opts.endpoint);\n const flushIntervalSeconds = opts.flushIntervalSeconds ?? getFlushIntervalSeconds();\n const transport = transportFactory({ apiKey, endpoint, flushIntervalSeconds });\n setTransport(transport);\n setRedactionConfig(redaction);\n // Resolve the internal source label: explicit kwarg wins, then env.\n // Invalid values are silently dropped so a typo never tags customer\n // data as dogfood.\n const explicitSource = isValidInternalSource(opts.source) ? opts.source! : undefined;\n const resolvedSource = explicitSource ?? readInternalSourceFromEnv() ?? null;\n _setSource(resolvedSource);\n logDestinationOnce(endpoint);\n\n // MHQ-482: run()/span()/runTraced()'s real emission pipeline — real OTel\n // spans over OTLP protobuf+gzip. Configured unconditionally (even under\n // MORSE_LEGACY_TRANSPORT=1) so the engine is ready if the flag flips at\n // runtime; the flag only affects which pipeline `tracing.ts::sendTrace()`\n // feeds. Mirrors the Python SDK's `_Client.__init__`.\n try {\n getOtelEngine().configure({\n endpoint: deriveOtlpEndpoint(endpoint),\n apiKey,\n sampleRate: getSampleRate(),\n scheduleDelayMillis: flushIntervalSeconds * 1000,\n maxQueueSize: getMaxQueueSize(),\n maxExportBatchSize: getMaxBatchSize(),\n maxFieldBytes: getMaxFieldBytes(),\n // 10s (not the exporter's un-overridden 30s ceiling): a genuine\n // network blip still gets several retries with backoff; a fully dead\n // endpoint fails within a bounded, shutdown-friendly window instead\n // of the library's full 30s ceiling. Mirrors the Python SDK.\n timeoutMillis: 10_000,\n redactionConfig: redaction,\n });\n } catch (err) {\n report(err, { fingerprint: \"morse.otel_provider.configure\" });\n }\n\n // MHQ-482: zero-config adapter auto-detect (mirrors Python's\n // `auto_detect.install_adapters()` call in `_ensure_initialized` /\n // `_init_impl`). Fire-and-forget — telemetry setup must never block or\n // throw back into the customer's init() / first SDK call. Runs AFTER the\n // OTel engine above is configured so any span an auto-installed adapter\n // emits immediately has a working pipeline to flow through.\n void _installAdapters().catch(() => {\n // installAdapters() already absorbs adapter-level failures; this is a\n // defensive backstop so a totally unexpected rejection never surfaces.\n });\n\n // MHQ-482: DB auto-capture (opt-in — MORSE_CAPTURE_DB / MORSE_CAPTURE_INFRA,\n // mirrors the Python SDK's `infra_capture.py::maybe_enable_from_env`).\n // MUST run after `getOtelEngine().configure()` above — instrumentation\n // registration needs the `NodeTracerProvider` that call just built.\n try {\n maybeEnableDbCaptureFromEnv();\n } catch (err) {\n report(err, { fingerprint: \"morse.db_capture.register\" });\n }\n\n return { apiKey, endpoint, transport, redaction };\n}\n\nfunction ensureInitialized(): SingletonState | null {\n if (readIsDisabled()) return null;\n if (singleton !== null) return singleton;\n // Lazy default-init: defaults are safe (default-on redaction).\n singleton = buildSingleton({}, buildRedactionConfig());\n return singleton;\n}\n\n// Wire the lazy-init hook into log.ts so `log()` participates in the\n// same env-driven auto-init flow as `track()` / `record()`. Discards\n// the return value — log.ts only needs the side-effect of populating\n// the module-level singleton + transport.\n_setLogEnsureInitialized(() => {\n ensureInitialized();\n});\n\n/* ------------------------------------------------------------------------ */\n/* Public API — wrapped at the module surface */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Initialize the Morse SDK singleton. Call once at application\n * startup. After this call, `track()`, `batchTrack()`, `run()`, and\n * `flush()` are wired up to the configured endpoint.\n *\n * `endpoint` is optional — when omitted, the SDK resolves in this order:\n * 1. `MORSE_ENDPOINT` env var (if set)\n * 2. The default Railway production URL.\n *\n * `MORSE_DISABLED=1` overrides everything: `init()` is a no-op.\n *\n * Calling `init()` a second time replaces the singleton — the previous\n * transport is shut down cleanly and a new one is constructed.\n *\n * Validation errors (e.g., disabling a mandatory redaction pattern, an\n * uncompilable extra pattern) are deliberately NOT absorbed — they\n * propagate as `Error`s so the developer sees the misconfiguration at\n * application startup. All other errors raised from internal SDK paths\n * are absorbed via the per-call wrappers downstream.\n */\nexport function init(options: InitOptions = {}): void {\n if (readIsDisabled()) {\n logger(\"info\", \"morse: MORSE_DISABLED=1 — SDK is a no-op\");\n return;\n }\n // Validate redaction options FIRST — must throw before we mutate any\n // singleton state, so a misconfigured init leaves the previous client\n // intact and the spec scenario \"no Client is constructed\" holds.\n const redaction = buildRedactionConfig(options.redaction);\n\n try {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n }\n singleton = buildSingleton(options, redaction);\n } catch (err) {\n report(err, { fingerprint: \"morse.init\" });\n }\n}\n\n/**\n * Submit a single agent run to the ingestion endpoint. Non-blocking;\n * returns immediately. Silently no-ops if the SDK isn't initialised.\n */\nexport const track = absorbErrorsSync(\n (options: TrackOptions): void => {\n const state = ensureInitialized();\n if (!state) return;\n const payload = {\n agent_id: options.agentId,\n ...options.runData,\n };\n const redacted = redactValue(payload, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n },\n { fingerprint: \"morse.track\" },\n);\n\n/** Submit multiple agent runs in bulk. Silently no-ops if uninitialised. */\nexport const batchTrack = absorbErrorsSync(\n (runs: Array<Record<string, unknown>>): void => {\n const state = ensureInitialized();\n if (!state) return;\n for (const run of runs) {\n const redacted = redactValue(run, state.redaction) as TelemetryEvent;\n state.transport.track(redacted);\n }\n },\n { fingerprint: \"morse.batch_track\" },\n);\n\n/**\n * High-level convenience: record an agent outcome with named fields.\n *\n * Builds a synthetic single-span `agent`-type trace and submits it through\n * the same `traces`-shaped emission path `run()`/`span()` use (MHQ-746) —\n * mirrors Python's `client.py::record()`. The live dev-api backend has no\n * `runs` ingest route; sending through `state.transport.track()` (the\n * shape `track()`/`batchTrack()` still use, matching Python's equally\n * unmigrated `track()`/`batch_track()`) 422s silently. See MHQ-746.\n */\nexport const record = absorbErrorsSync(\n (rec: RunRecord): void => {\n const state = ensureInitialized();\n if (!state) return;\n\n const traceId = generateTraceId();\n const now = nowIso();\n const resolvedStatus = rec.errorMessage ? \"failed\" : (rec.status ?? \"completed\");\n\n const span: SpanData = {\n trace_id: traceId,\n span_id: generateSpanId(),\n type: SpanType.AGENT,\n name: rec.agent,\n started_at: now,\n ended_at: now,\n status: resolvedStatus,\n duration_ms: rec.durationMs,\n model: rec.model,\n input_tokens: rec.inputTokens,\n output_tokens: rec.outputTokens,\n total_tokens: rec.totalTokens,\n cost_usd: rec.cost,\n error_message: rec.errorMessage,\n metadata: rec.metadata,\n };\n\n const traceFields: TraceFields = {\n trace_id: traceId,\n agent: rec.agent,\n status: resolvedStatus,\n started_at: now,\n ended_at: now,\n duration_ms: rec.durationMs,\n error_message: rec.errorMessage,\n success: rec.success,\n outcome: rec.outcome,\n };\n\n const entry = buildTraceEntry(traceFields, [span]);\n const redacted = redactValue(entry, state.redaction) as {\n trace: Record<string, unknown>;\n spans: Record<string, unknown>[];\n };\n _sendTrace(redacted);\n },\n { fingerprint: \"morse.record\" },\n);\n\n/**\n * Open a traced agent run, executing `fn` inside the trace context.\n * Mirrors Python's `morse.run(...)` context manager.\n *\n * Two overloads are supported:\n * - `run(name, fn)` — quick form with default options.\n * - `run({ agentName, incomingTraceparent }, fn)` — full options.\n *\n * Async callers receive a Promise; sync callers receive the value\n * directly. The function's return value is forwarded to the caller.\n */\nexport function run<T>(name: string, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(options: RunOptions, fn: (handle: RunHandle) => T): T | undefined;\nexport function run<T>(\n nameOrOpts: string | RunOptions,\n fn: (handle: RunHandle) => T,\n): T | undefined {\n const opts = typeof nameOrOpts === \"string\" ? { agentName: nameOrOpts } : nameOrOpts;\n // Make sure the SDK is initialised so spans get sent. Failure to init\n // is fine — `_runSync` records to `lastTrace` regardless, useful for tests.\n ensureInitialized();\n // We can't reliably detect async vs sync up front in TypeScript, so we\n // route through `_runSync` and, if the user's `fn` returns a promise,\n // the wrapping trace ends synchronously after the promise is created.\n // For correct async lifecycle, callers should use `runTraced` instead\n // (it awaits the promise inside the `als.run` scope so the trace\n // closes when the promise resolves).\n return _runSync(opts, fn);\n}\n\n/** Async variant of `run` — exported for callers who want explicit typing. */\nexport function runTraced<T>(\n options: RunOptions,\n fn: (handle: RunHandle) => Promise<T>,\n): Promise<T | undefined> {\n ensureInitialized();\n return _runAsync(options, fn);\n}\n\n/**\n * Attach cost-attribution dimensions to the active trace (MHQ-751). Call\n * once per request (e.g. in middleware) to propagate `customerId`,\n * `feature`, `env`, `team`, etc. to all child spans downstream. Values are\n * coerced to strings. No-op outside an active trace — never throws.\n * Mirrors Python's `morse_ai.set_context(**dims)`.\n */\nexport function setContext(dims: Record<string, unknown>): void {\n _setContext(dims);\n}\n\n/** Start a child span under the active trace. Returns null if no trace. */\nexport function startSpan(options: SpanOptions): SpanHandle | null {\n return _startSpan(options);\n}\n\n/** Run `fn` inside a child span. No-op if no trace is active. */\nexport function span<T>(options: SpanOptions, fn: (handle: SpanHandle | null) => T): T {\n return _spanSync(options, fn);\n}\n\n/** Async variant of `span`. */\nexport function spanAsync<T>(\n options: SpanOptions,\n fn: (handle: SpanHandle | null) => Promise<T>,\n): Promise<T> {\n return _spanAsync(options, fn);\n}\n\n/** Block until all queued events have been sent. Silent on uninit / errors. */\nexport const flush = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.flush();\n await getOtelEngine().flush();\n }\n },\n { fingerprint: \"morse.flush\" },\n);\n\n/**\n * Latest ingest quota / rate-limit state observed by the SDK, or `null`.\n *\n * TQE-04 (MHQ-676/MHQ-749): when the Morse backend answers a telemetry\n * batch with HTTP 429 — the monthly event limit (`event_limit_exceeded`)\n * or the per-second ingest rate limit (`RATE_LIMIT_INGEST`) — the\n * transport parses the response and stores it. This function exposes\n * that state so host applications can alert on quota exhaustion instead\n * of discovering it from missing telemetry. Mirrors Python's\n * `morse_ai.quota_state()`.\n *\n * Returns `null` when the SDK is uninitialized or no 429 has been\n * observed. The object includes at least `kind` (`\"event_limit\"`,\n * `\"rate_limit\"`, or `\"unknown\"`), `status`, and `receivedAt` (epoch\n * seconds); event-limit states add `currentCount` / `limitCount` /\n * `planId` / `periodStart` / `upgradeUrl`.\n */\nexport const quotaState = absorbErrorsSync(\n (): QuotaState | null => {\n if (singleton === null) return null;\n return _getQuotaState();\n },\n { fingerprint: \"morse.quota_state\" },\n);\n\n/** Flush and stop the background transport. Call on app exit. */\nexport const shutdown = absorbErrorsAsync(\n async (): Promise<void> => {\n if (singleton !== null) {\n await singleton.transport.shutdown();\n await getOtelEngine().flush();\n getOtelEngine().shutdown();\n singleton = null;\n setTransport(null);\n }\n },\n { fingerprint: \"morse.shutdown\" },\n);\n\n/* ------------------------------------------------------------------------ */\n/* Test-only hooks (private) */\n/* ------------------------------------------------------------------------ */\n\n/**\n * Reset the module-level singleton for tests. Customer code must NOT\n * call this — the singleton is meant to live for the process lifetime.\n *\n * @internal\n */\nexport function _resetForTesting(): void {\n if (singleton !== null) {\n void singleton.transport.shutdown();\n singleton = null;\n setTransport(null);\n }\n getOtelEngine().resetForTesting();\n noApiKeyWarningEmitted = false;\n lastLoggedEndpoint = null;\n _setSource(null);\n _resetTracingForTesting();\n _resetSentryForTesting();\n _resetAutoDetectForTesting();\n _resetQuotaStateForTesting();\n // Reset the transport factory back to the default.\n transportFactory = (opts) => new BatchTransport(opts);\n}\n\n/**\n * Inject an alternate transport factory for tests (e.g., a mock that\n * captures sent payloads instead of opening a fetch).\n *\n * @internal\n */\nexport function _setTransportFactoryForTesting(\n factory: (opts: {\n apiKey: string;\n endpoint: string;\n flushIntervalSeconds: number;\n }) => BatchTransport,\n): void {\n transportFactory = factory;\n}\n\n/**\n * Read the most recently completed trace entry, for test assertions.\n *\n * @internal\n */\nexport function _getLastTraceForTesting(): { trace: unknown; spans: unknown[] } | null {\n return _getLastTrace();\n}\n\n/**\n * Read the active trace state, for test assertions.\n *\n * @internal\n */\nexport function _getActiveTraceStateForTesting(): unknown {\n return _getActiveTraceState();\n}\n","/**\n * Browser replacement for `db-capture.ts` (MHQ-482 follow-up).\n *\n * `db-capture.ts` statically imports `@opentelemetry/instrumentation-pg`,\n * `-mysql2` and `@opentelemetry/instrumentation` (which reaches for node's\n * `module` via require-in-the-middle). A browser has no pg/mysql2 driver to\n * patch, so the whole module is inert there — but the static imports still\n * dragged those packages, and node `module`, into the browser graph.\n *\n * Swapped in by the esbuild plugin in `tsup.config.ts`; see\n * `otel-provider.browser.ts` for the full rationale. Only the symbols\n * `index.ts` imports need to exist here.\n */\n\n/**\n * No-op: there is no database driver to instrument in a browser.\n *\n * Deliberately silent. `otel-provider.browser.ts::noteBrowserRuntime()`\n * already states that DB capture is Node-only, and it fires on\n * `configure()` — which `index.ts` always calls before this. A second\n * message would be noise, and this function runs even when the customer\n * never asked for DB capture.\n */\nexport function maybeEnableDbCaptureFromEnv(): void {}\n","/**\n * Background batched HTTP transport.\n *\n * Mirrors the Python SDK's `transport.py` semantics:\n *\n * - Async batching: 2-second flush interval (configurable), 100-event\n * max batch size (configurable). Whichever fires first triggers a\n * flush.\n * - Network failures are silently dropped with a debug log — never\n * thrown to the caller.\n * - The caller's thread / event loop is never blocked: `track()`\n * enqueues and returns immediately.\n *\n * The transport speaks two payload shapes (matching Python):\n *\n * - V2 trace events: `{ traces: [{ trace, spans }, ...] }`\n * - V1 legacy runs: `{ runs: [{ ... }, ...] }`\n *\n * Wire-format routing checks for a `traces` key on each enqueued event.\n *\n * Browser vs Node:\n * - Both runtimes ship `fetch` (Node 18+ / all evergreens).\n * - In Node we can run a `setInterval` and call it a day.\n * - In the browser the same approach works; we don't currently flush\n * on `pagehide` / `beforeunload` — Phase 6c spec doesn't require it.\n * A future change can add `navigator.sendBeacon` for last-gasp delivery.\n */\n\nimport { report } from \"./sentry-reporter\";\n\n// Injected at build time by tsup from package.json — see tsup.config.ts.\n// The version is declared in exactly one place (package.json); a copy here\n// would drift the moment a release bumped one and not the other, and the\n// stale value rides out on the User-Agent and on every span's `sdk_version`.\n// `__SDK_VERSION__` is undefined when running straight from source (vitest,\n// ts-node), hence the fallback.\ndeclare const __SDK_VERSION__: string | undefined;\n\nexport const SDK_VERSION = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\nexport interface BatchTransportOptions {\n endpoint: string;\n apiKey: string;\n /** Max wait between flushes, in seconds. Default: 2.0. */\n flushIntervalSeconds?: number;\n /** Max events per batch. Default: 100. */\n maxBatchSize?: number;\n /** Max queued events before drop. Default: 10_000. */\n maxQueueSize?: number;\n /** Timeout for the underlying fetch, in ms. Default: 5_000. */\n fetchTimeoutMs?: number;\n /**\n * Pluggable fetch implementation. Defaults to `globalThis.fetch`.\n * Useful for tests.\n */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Wire shape for a single log record. Mirrors the Python SDK payload at\n * `apps/sdk/python/src/morse/client.py:191-211` and the backend\n * `SimpleLogsIngestController.ingest` contract\n * (`apps/api/apps/logs/controllers.py:66-134`).\n */\nexport interface LogRecordPayload {\n timestamp: string;\n severity: string;\n service_name: string;\n body: string;\n attributes: Record<string, unknown>;\n trace_id: string | null;\n span_id: string | null;\n}\n\nexport type TelemetryEvent =\n | { traces: unknown[] }\n | { logs: LogRecordPayload[] }\n | Record<string, unknown>;\n\n/**\n * Quota / rate-limit state parsed from a 429 batch-POST response.\n *\n * Mirrors Python's `transport.py::BatchTransport._record_quota_state` /\n * `quota_state()` (TQE-04 / MHQ-676) field-for-field, camelCased per this\n * codebase's convention (`received_at` -> `receivedAt`, etc.). Two known\n * body shapes:\n *\n * - `event_limit_exceeded` (traces controllers) — flat body with\n * `current_count` / `limit_count` / `plan_id` / `period_start` /\n * `upgrade_url` / `sample_rate_url`.\n * - `RATE_LIMIT_INGEST` (ingest rate-limit middleware) — nested\n * `{ error: { code, message, details: { limit, plan } } }`.\n *\n * Anything else is recorded as `kind: \"unknown\"` so the state surface\n * still reflects that ingest is throttled even when the body doesn't\n * parse into a known shape.\n */\nexport interface QuotaState {\n kind: \"event_limit\" | \"rate_limit\" | \"unknown\";\n status: number;\n /** Epoch seconds, matching Python's `time.time()`. */\n receivedAt: number;\n message?: string;\n currentCount?: number;\n limitCount?: number;\n planId?: string;\n periodStart?: string;\n upgradeUrl?: string;\n sampleRateUrl?: string;\n quotaLimit?: number;\n quotaRemaining?: number;\n quotaReset?: number;\n retryAfterSeconds?: number;\n}\n\n// `Retry-After` values above this are clamped, mirroring Python's\n// `_RETRY_AFTER_CAP_SECONDS` — a background flush loop must never commit\n// to reporting minutes-long waits from one batch.\nconst RETRY_AFTER_CAP_SECONDS = 30.0;\n\n/**\n * Parse a `Retry-After` header (seconds form only), capped.\n *\n * Mirrors Python's `transport.py::_parse_retry_after`. HTTP-date form\n * isn't worth supporting — Morse's own 429s always use the seconds form.\n * Unparseable / negative values return `null`.\n */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number(value);\n if (!Number.isFinite(seconds) || seconds < 0) return null;\n return Math.min(seconds, RETRY_AFTER_CAP_SECONDS);\n}\n\n// Module-level, matching the `lastTrace` test-introspection pattern in\n// `_internal/tracing.ts` — the latest quota/rate-limit state observed on\n// a 429 response, exposed via `quotaState()` / `morse.quotaState()`. Not\n// cleared on a subsequent successful POST (mirrors Python: it persists\n// until overwritten by a newer 429, so a host app can see \"quota was\n// exceeded recently\" even after the queue drains — see\n// `test_429_recovers_when_retry_succeeds` in the Python test suite,\n// which only asserts no drop-warning, never that state was cleared).\nlet quotaState: QuotaState | null = null;\n\n/**\n * Latest quota/rate-limit state observed on a 429, or `null` if none has\n * been observed yet. Returns a shallow copy — callers can't mutate\n * transport-internal state. Mirrors Python's `BatchTransport.quota_state()`.\n *\n * @internal\n */\nexport function getQuotaState(): QuotaState | null {\n return quotaState ? { ...quotaState } : null;\n}\n\n/**\n * Reset the module-level quota state between test files/cases.\n *\n * @internal\n */\nexport function _resetQuotaStateForTesting(): void {\n quotaState = null;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction numberFromHeader(raw: string | null): number | undefined {\n if (raw === null) return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Parse a 429 response into the module-level quota state. Never throws —\n * defensively parses two known body shapes (see `QuotaState`), falling\n * back to `kind: \"unknown\"` for anything else. Mirrors Python's\n * `_record_quota_state`.\n */\nasync function recordQuotaState(resp: Response): Promise<void> {\n try {\n const state: QuotaState = {\n kind: \"unknown\",\n status: 429,\n receivedAt: Date.now() / 1000,\n };\n\n let data: unknown = null;\n try {\n data = await resp.clone().json();\n } catch {\n data = null;\n }\n\n if (data !== null && typeof data === \"object\") {\n const body = data as Record<string, unknown>;\n const err = body.error;\n if (err === \"event_limit_exceeded\") {\n state.kind = \"event_limit\";\n state.message = asString(body.message);\n state.currentCount = asNumber(body.current_count);\n state.limitCount = asNumber(body.limit_count);\n state.planId = asString(body.plan_id);\n state.periodStart = asString(body.period_start);\n state.upgradeUrl = asString(body.upgrade_url);\n state.sampleRateUrl = asString(body.sample_rate_url);\n } else if (\n err !== null &&\n typeof err === \"object\" &&\n (err as Record<string, unknown>).code === \"RATE_LIMIT_INGEST\"\n ) {\n const errObj = err as Record<string, unknown>;\n const details =\n errObj.details !== null && typeof errObj.details === \"object\"\n ? (errObj.details as Record<string, unknown>)\n : {};\n state.kind = \"rate_limit\";\n state.message = asString(errObj.message);\n state.limitCount = asNumber(details.limit);\n state.planId = asString(details.plan);\n }\n }\n\n // TQE-05 headers, emitted by the server on every ingest response.\n const quotaLimit = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Limit\"));\n if (quotaLimit !== undefined) state.quotaLimit = quotaLimit;\n const quotaRemaining = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Remaining\"));\n if (quotaRemaining !== undefined) state.quotaRemaining = quotaRemaining;\n const quotaReset = numberFromHeader(resp.headers.get(\"X-Morse-Quota-Reset\"));\n if (quotaReset !== undefined) state.quotaReset = quotaReset;\n const retryAfter = parseRetryAfter(resp.headers.get(\"Retry-After\"));\n if (retryAfter !== null) state.retryAfterSeconds = retryAfter;\n\n quotaState = state;\n } catch {\n // Never let state capture break the send path.\n }\n}\n\nexport class BatchTransport {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly flushIntervalSeconds: number;\n private readonly maxBatchSize: number;\n private readonly maxQueueSize: number;\n private readonly fetchTimeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n private queue: TelemetryEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private closed = false;\n private inflight: Promise<void> | null = null;\n\n constructor(opts: BatchTransportOptions) {\n this.endpoint = opts.endpoint;\n this.apiKey = opts.apiKey;\n this.flushIntervalSeconds = opts.flushIntervalSeconds ?? 2.0;\n this.maxBatchSize = opts.maxBatchSize ?? 100;\n this.maxQueueSize = opts.maxQueueSize ?? 10_000;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 5_000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);\n\n // setInterval keeps the Node event loop alive. We mark it `unref`\n // when available so a process-exit isn't blocked by a quiet flusher.\n const intervalMs = Math.max(50, Math.round(this.flushIntervalSeconds * 1000));\n this.timer = setInterval(() => {\n void this.tick();\n }, intervalMs);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const t = this.timer as any;\n if (typeof t?.unref === \"function\") {\n t.unref();\n }\n }\n\n /** Enqueue an event. Non-blocking. Returns false if the queue is full. */\n track(event: TelemetryEvent): boolean {\n if (this.closed) return false;\n if (this.queue.length >= this.maxQueueSize) return false;\n this.queue.push(event);\n if (this.queue.length >= this.maxBatchSize) {\n void this.tick();\n }\n return true;\n }\n\n /** Block until all currently-queued events have been sent. */\n async flush(): Promise<void> {\n // Drain in one-batch chunks until empty, then await the in-flight send.\n while (this.queue.length > 0) {\n await this.tick();\n }\n if (this.inflight) {\n try {\n await this.inflight;\n } catch {\n // Swallow — caller observes nothing.\n }\n }\n }\n\n /** Stop the background timer. After this call, `track()` is a no-op. */\n async shutdown(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private async tick(): Promise<void> {\n if (this.flushing) return;\n if (this.queue.length === 0) return;\n this.flushing = true;\n try {\n const batch = this.queue.splice(0, this.maxBatchSize);\n this.inflight = this.sendBatch(batch);\n await this.inflight;\n this.inflight = null;\n } finally {\n this.flushing = false;\n }\n }\n\n private async sendBatch(batch: TelemetryEvent[]): Promise<void> {\n const traceEntries: unknown[] = [];\n const runEntries: Record<string, unknown>[] = [];\n const logEntries: LogRecordPayload[] = [];\n for (const event of batch) {\n if (typeof event === \"object\" && event !== null && \"traces\" in event) {\n const e = event as { traces: unknown[] };\n if (Array.isArray(e.traces)) {\n traceEntries.push(...e.traces);\n }\n } else if (typeof event === \"object\" && event !== null && \"logs\" in event) {\n const e = event as { logs: LogRecordPayload[] };\n if (Array.isArray(e.logs)) {\n logEntries.push(...e.logs);\n }\n } else {\n runEntries.push(event as Record<string, unknown>);\n }\n }\n if (traceEntries.length > 0) {\n await this.post(this.endpoint, { traces: traceEntries });\n }\n if (runEntries.length > 0) {\n await this.post(this.endpoint, { runs: runEntries });\n }\n if (logEntries.length > 0) {\n // Phase 12.5 Tier 1.2 — logs go to a derived `/api/v1/logs/ingest`\n // endpoint on the same host as the traces batch URL. Mirrors the\n // Python SDK's URL derivation in\n // `apps/sdk/python/src/morse/client.py:187-190`.\n await this.post(deriveLogsEndpoint(this.endpoint), { logs: logEntries });\n }\n }\n\n private async post(url: string, payload: Record<string, unknown>): Promise<void> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.fetchTimeoutMs);\n try {\n const resp = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `morse-typescript/${SDK_VERSION}`,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n if (resp.status === 429) {\n // TQE-04 (MHQ-676) parity: surface quota/rate-limit state instead\n // of silently dropping telemetry. `resp.clone()` inside so the\n // body-snippet read below still works off the original response.\n await recordQuotaState(resp);\n }\n if (!resp.ok) {\n // Drain a few hundred chars of the body for diagnostics.\n let bodySnippet = \"\";\n try {\n const text = await resp.text();\n bodySnippet = text.slice(0, 200);\n } catch {\n // ignore\n }\n report(new Error(`morse: batch POST returned ${resp.status} ${bodySnippet}`), {\n fingerprint: \"morse.transport.post.non_2xx\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n }\n } catch (err) {\n // Network errors / aborts / timeouts — silent drop, out-of-band report.\n report(err, {\n fingerprint: \"morse.transport.post.network\",\n extra: {\n endpoint_host: hostnameOf(url),\n sdk_version: SDK_VERSION,\n },\n });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n\nfunction hostnameOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return \"<invalid>\";\n }\n}\n\n/**\n * Derive the logs ingest URL from the traces batch endpoint. Mirrors the\n * Python SDK helper: replace the path with `/api/v1/logs/ingest` and drop\n * any query/fragment.\n *\n * Exported for tests. Customer code should not call this directly.\n *\n * @internal\n */\nexport function deriveLogsEndpoint(tracesEndpoint: string): string {\n try {\n const u = new URL(tracesEndpoint);\n u.pathname = \"/api/v1/logs/ingest\";\n u.search = \"\";\n u.hash = \"\";\n return u.toString();\n } catch {\n // Fall back to a string replace on the most common shape.\n return tracesEndpoint.replace(/\\/api\\/v1\\/.*$/, \"/api/v1/logs/ingest\");\n }\n}\n"]}