aden-ts 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1739,6 +1739,101 @@ function isAnthropicInstrumented() {
1739
1739
  // src/control-agent.ts
1740
1740
  import { randomUUID as randomUUID6 } from "crypto";
1741
1741
  import WebSocket from "ws";
1742
+
1743
+ // src/logging.ts
1744
+ var LOG_LEVEL_PRIORITY = {
1745
+ debug: 0,
1746
+ info: 1,
1747
+ warn: 2,
1748
+ error: 3,
1749
+ silent: 4
1750
+ };
1751
+ var currentConfig = {
1752
+ level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1753
+ metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1754
+ handler: console
1755
+ };
1756
+ function parseLogLevel(level) {
1757
+ if (!level) return void 0;
1758
+ const normalized = level.toLowerCase();
1759
+ if (normalized in LOG_LEVEL_PRIORITY) {
1760
+ return normalized;
1761
+ }
1762
+ return void 0;
1763
+ }
1764
+ function shouldLog(level, configLevel) {
1765
+ return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[configLevel];
1766
+ }
1767
+ function configureLogging(config) {
1768
+ if (config.level !== void 0) {
1769
+ currentConfig.level = config.level;
1770
+ if (config.metricsLevel === void 0) {
1771
+ currentConfig.metricsLevel = config.level;
1772
+ }
1773
+ }
1774
+ if (config.metricsLevel !== void 0) {
1775
+ currentConfig.metricsLevel = config.metricsLevel;
1776
+ }
1777
+ if (config.handler !== void 0) {
1778
+ currentConfig.handler = config.handler;
1779
+ }
1780
+ }
1781
+ function getLoggingConfig() {
1782
+ return { ...currentConfig };
1783
+ }
1784
+ function resetLoggingConfig() {
1785
+ currentConfig = {
1786
+ level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1787
+ metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1788
+ handler: console
1789
+ };
1790
+ }
1791
+ var logger = {
1792
+ debug(message, ...args) {
1793
+ if (shouldLog("debug", currentConfig.level)) {
1794
+ currentConfig.handler.debug(`[aden] ${message}`, ...args);
1795
+ }
1796
+ },
1797
+ info(message, ...args) {
1798
+ if (shouldLog("info", currentConfig.level)) {
1799
+ currentConfig.handler.info(`[aden] ${message}`, ...args);
1800
+ }
1801
+ },
1802
+ warn(message, ...args) {
1803
+ if (shouldLog("warn", currentConfig.level)) {
1804
+ currentConfig.handler.warn(`[aden] ${message}`, ...args);
1805
+ }
1806
+ },
1807
+ error(message, ...args) {
1808
+ if (shouldLog("error", currentConfig.level)) {
1809
+ currentConfig.handler.error(`[aden] ${message}`, ...args);
1810
+ }
1811
+ }
1812
+ };
1813
+ var metricsLogger = {
1814
+ debug(message, ...args) {
1815
+ if (shouldLog("debug", currentConfig.metricsLevel)) {
1816
+ currentConfig.handler.debug(`[aden.metrics] ${message}`, ...args);
1817
+ }
1818
+ },
1819
+ info(message, ...args) {
1820
+ if (shouldLog("info", currentConfig.metricsLevel)) {
1821
+ currentConfig.handler.info(`[aden.metrics] ${message}`, ...args);
1822
+ }
1823
+ },
1824
+ warn(message, ...args) {
1825
+ if (shouldLog("warn", currentConfig.metricsLevel)) {
1826
+ currentConfig.handler.warn(`[aden.metrics] ${message}`, ...args);
1827
+ }
1828
+ },
1829
+ error(message, ...args) {
1830
+ if (shouldLog("error", currentConfig.metricsLevel)) {
1831
+ currentConfig.handler.error(`[aden.metrics] ${message}`, ...args);
1832
+ }
1833
+ }
1834
+ };
1835
+
1836
+ // src/control-agent.ts
1742
1837
  var SDK_VERSION = "0.1.0";
1743
1838
  var ControlAgent = class {
1744
1839
  constructor(options) {
@@ -1769,12 +1864,12 @@ var ControlAgent = class {
1769
1864
  instanceId: options.instanceId ?? randomUUID6(),
1770
1865
  onAlert: options.onAlert ?? (() => {
1771
1866
  }),
1772
- // Hybrid enforcement options
1773
- enableHybridEnforcement: options.enableHybridEnforcement ?? false,
1774
- serverValidationThreshold: options.serverValidationThreshold ?? 80,
1867
+ // Hybrid enforcement options (defaults match Python SDK)
1868
+ enableHybridEnforcement: options.enableHybridEnforcement ?? true,
1869
+ serverValidationThreshold: options.serverValidationThreshold ?? 5,
1775
1870
  serverValidationTimeoutMs: options.serverValidationTimeoutMs ?? 2e3,
1776
1871
  adaptiveThresholdEnabled: options.adaptiveThresholdEnabled ?? true,
1777
- adaptiveMinRemainingUsd: options.adaptiveMinRemainingUsd ?? 1,
1872
+ adaptiveMinRemainingUsd: options.adaptiveMinRemainingUsd ?? 5,
1778
1873
  samplingEnabled: options.samplingEnabled ?? true,
1779
1874
  samplingBaseRate: options.samplingBaseRate ?? 0.1,
1780
1875
  samplingFullValidationPercent: options.samplingFullValidationPercent ?? 95,
@@ -1786,9 +1881,11 @@ var ControlAgent = class {
1786
1881
  */
1787
1882
  async connect() {
1788
1883
  const url = this.options.serverUrl;
1884
+ logger.debug(`Connecting to control server: ${url}`);
1789
1885
  if (url.startsWith("wss://") || url.startsWith("ws://")) {
1790
1886
  await this.connectWebSocket();
1791
1887
  } else {
1888
+ logger.debug("Using HTTP polling mode (no WebSocket URL)");
1792
1889
  this.startPolling();
1793
1890
  }
1794
1891
  this.startHeartbeat();
@@ -1804,6 +1901,7 @@ var ControlAgent = class {
1804
1901
  };
1805
1902
  try {
1806
1903
  const wsUrl = `${this.options.serverUrl}/v1/control/ws`;
1904
+ logger.debug(`Attempting WebSocket connection to: ${wsUrl}`);
1807
1905
  this.ws = new WebSocket(wsUrl, {
1808
1906
  headers: {
1809
1907
  Authorization: `Bearer ${this.options.apiKey}`,
@@ -1813,7 +1911,7 @@ var ControlAgent = class {
1813
1911
  this.ws.on("open", () => {
1814
1912
  this.connected = true;
1815
1913
  this.reconnectAttempts = 0;
1816
- console.log("[aden] WebSocket connected to control server");
1914
+ logger.info("WebSocket connected to control server");
1817
1915
  this.flushEventQueue();
1818
1916
  resolve();
1819
1917
  });
@@ -1822,12 +1920,12 @@ var ControlAgent = class {
1822
1920
  });
1823
1921
  this.ws.on("close", () => {
1824
1922
  this.connected = false;
1825
- console.log("[aden] WebSocket disconnected, falling back to polling");
1923
+ logger.info("WebSocket disconnected, falling back to polling");
1826
1924
  this.scheduleReconnect();
1827
1925
  this.startPolling();
1828
1926
  });
1829
1927
  this.ws.on("error", (error) => {
1830
- console.warn("[aden] WebSocket error:", error.message);
1928
+ logger.warn("WebSocket error:", error.message);
1831
1929
  this.errorsSinceLastHeartbeat++;
1832
1930
  if (!this.connected) {
1833
1931
  fallbackToPolling();
@@ -1835,12 +1933,12 @@ var ControlAgent = class {
1835
1933
  });
1836
1934
  setTimeout(() => {
1837
1935
  if (!this.connected) {
1838
- console.warn("[aden] WebSocket connection timeout, using polling");
1936
+ logger.warn("WebSocket connection timeout, using polling");
1839
1937
  fallbackToPolling();
1840
1938
  }
1841
1939
  }, this.options.timeoutMs);
1842
1940
  } catch (error) {
1843
- console.warn("[aden] WebSocket setup failed:", error);
1941
+ logger.warn("WebSocket setup failed:", error);
1844
1942
  fallbackToPolling();
1845
1943
  }
1846
1944
  });
@@ -1851,7 +1949,7 @@ var ControlAgent = class {
1851
1949
  scheduleReconnect() {
1852
1950
  if (this.reconnectTimer) return;
1853
1951
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
1854
- console.warn("[aden] Max reconnect attempts reached, using polling only");
1952
+ logger.warn("Max reconnect attempts reached, using polling only");
1855
1953
  return;
1856
1954
  }
1857
1955
  const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
@@ -1872,12 +1970,12 @@ var ControlAgent = class {
1872
1970
  if (message.type === "policy") {
1873
1971
  this.cachedPolicy = message.policy;
1874
1972
  this.lastPolicyFetch = Date.now();
1875
- console.log("[aden] Policy updated:", this.cachedPolicy.version);
1973
+ logger.info("Policy updated:", this.cachedPolicy.version);
1876
1974
  } else if (message.type === "command") {
1877
- console.log("[aden] Command received:", message);
1975
+ logger.info("Command received:", message);
1878
1976
  }
1879
1977
  } catch (error) {
1880
- console.warn("[aden] Failed to parse message:", error);
1978
+ logger.warn("Failed to parse message:", error);
1881
1979
  }
1882
1980
  }
1883
1981
  /**
@@ -1886,6 +1984,7 @@ var ControlAgent = class {
1886
1984
  */
1887
1985
  async startPolling() {
1888
1986
  if (this.pollingTimer) return;
1987
+ logger.debug(`Starting HTTP polling (interval: ${this.options.pollingIntervalMs}ms)`);
1889
1988
  await this.fetchPolicy();
1890
1989
  this.pollingTimer = setInterval(() => {
1891
1990
  if (!this.connected) {
@@ -1906,15 +2005,19 @@ var ControlAgent = class {
1906
2005
  * Fetch policy via HTTP
1907
2006
  */
1908
2007
  async fetchPolicy() {
2008
+ logger.debug("Fetching policy from server...");
1909
2009
  try {
1910
2010
  const response = await this.httpRequest("/v1/control/policy", "GET");
1911
2011
  if (response.ok) {
1912
2012
  const policy = await response.json();
1913
2013
  this.cachedPolicy = policy;
1914
2014
  this.lastPolicyFetch = Date.now();
2015
+ logger.debug(`Policy fetched successfully (version: ${policy.version}, budgets: ${policy.budgets?.length ?? 0})`);
2016
+ } else {
2017
+ logger.debug(`Policy fetch returned status ${response.status}`);
1915
2018
  }
1916
2019
  } catch (error) {
1917
- console.warn("[aden] Failed to fetch policy:", error);
2020
+ logger.warn("Failed to fetch policy:", error);
1918
2021
  }
1919
2022
  }
1920
2023
  /**
@@ -1922,6 +2025,7 @@ var ControlAgent = class {
1922
2025
  */
1923
2026
  startHeartbeat() {
1924
2027
  if (this.heartbeatTimer) return;
2028
+ logger.debug(`Starting heartbeat (interval: ${this.options.heartbeatIntervalMs}ms)`);
1925
2029
  this.heartbeatTimer = setInterval(() => {
1926
2030
  this.sendHeartbeat();
1927
2031
  }, this.options.heartbeatIntervalMs);
@@ -1958,6 +2062,7 @@ var ControlAgent = class {
1958
2062
  * Disconnect from the control server
1959
2063
  */
1960
2064
  async disconnect() {
2065
+ logger.debug("Disconnecting from control server...");
1961
2066
  this.stopPolling();
1962
2067
  this.stopHeartbeat();
1963
2068
  if (this.reconnectTimer) {
@@ -1969,6 +2074,7 @@ var ControlAgent = class {
1969
2074
  this.ws = null;
1970
2075
  }
1971
2076
  this.connected = false;
2077
+ logger.debug("Disconnected from control server");
1972
2078
  }
1973
2079
  /**
1974
2080
  * Get a control decision for a request
@@ -2022,66 +2128,53 @@ var ControlAgent = class {
2022
2128
  }
2023
2129
  if (policy.budgets) {
2024
2130
  const applicableBudgets = this.findApplicableBudgets(policy.budgets, request);
2025
- if (this.options.enableHybridEnforcement) {
2131
+ if (applicableBudgets.length > 0) {
2132
+ let mostRestrictiveDecision = null;
2133
+ let mostRestrictivePriority = -1;
2026
2134
  for (const budget of applicableBudgets) {
2027
- const decision = await this.evaluateBudgetWithHybridEnforcement(
2028
- request,
2029
- budget,
2030
- throttleInfo
2031
- );
2032
- if (decision) {
2033
- return decision;
2135
+ let decision = null;
2136
+ if (this.options.enableHybridEnforcement) {
2137
+ decision = await this.evaluateBudgetWithHybridEnforcement(
2138
+ request,
2139
+ budget,
2140
+ throttleInfo
2141
+ );
2142
+ } else {
2143
+ decision = this.evaluateBudgetLocally(request, budget, throttleInfo);
2034
2144
  }
2035
- if (policy.degradations) {
2145
+ if (!decision && policy.degradations) {
2036
2146
  for (const degrade of policy.degradations) {
2037
2147
  if (degrade.from_model === request.model && degrade.trigger === "budget_threshold" && degrade.threshold_percent) {
2038
2148
  const usagePercent = budget.spent / budget.limit * 100;
2039
2149
  if (usagePercent >= degrade.threshold_percent) {
2040
- return {
2150
+ decision = {
2041
2151
  action: "degrade",
2042
2152
  reason: `Budget "${budget.name}" at ${usagePercent.toFixed(1)}% (threshold: ${degrade.threshold_percent}%)`,
2043
2153
  degradeToModel: degrade.to_model,
2044
2154
  ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2045
2155
  };
2156
+ break;
2046
2157
  }
2047
2158
  }
2048
2159
  }
2049
2160
  }
2050
- }
2051
- } else {
2052
- for (const budget of applicableBudgets) {
2053
- const projectedSpend = budget.spent + (request.estimated_cost ?? 0);
2054
- if (projectedSpend > budget.limit) {
2055
- if (budget.limitAction === "degrade" && budget.degradeToModel) {
2056
- return {
2057
- action: "degrade",
2058
- reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`,
2059
- degradeToModel: budget.degradeToModel,
2060
- ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2061
- };
2161
+ if (decision) {
2162
+ const priority = this.getActionPriority(decision.action);
2163
+ if (priority > mostRestrictivePriority) {
2164
+ mostRestrictiveDecision = decision;
2165
+ mostRestrictivePriority = priority;
2062
2166
  }
2063
- const action = budget.limitAction === "kill" ? "block" : budget.limitAction;
2064
- return {
2065
- action,
2066
- reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`
2067
- };
2068
- }
2069
- if (policy.degradations) {
2070
- for (const degrade of policy.degradations) {
2071
- if (degrade.from_model === request.model && degrade.trigger === "budget_threshold" && degrade.threshold_percent) {
2072
- const usagePercent = budget.spent / budget.limit * 100;
2073
- if (usagePercent >= degrade.threshold_percent) {
2074
- return {
2075
- action: "degrade",
2076
- reason: `Budget "${budget.name}" at ${usagePercent.toFixed(1)}% (threshold: ${degrade.threshold_percent}%)`,
2077
- degradeToModel: degrade.to_model,
2078
- ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2079
- };
2080
- }
2081
- }
2167
+ if (decision.action === "block") {
2168
+ break;
2082
2169
  }
2083
2170
  }
2084
2171
  }
2172
+ if (mostRestrictiveDecision) {
2173
+ if (throttleInfo && mostRestrictiveDecision.action !== "block" && !mostRestrictiveDecision.throttleDelayMs) {
2174
+ mostRestrictiveDecision.throttleDelayMs = throttleInfo.delayMs;
2175
+ }
2176
+ return mostRestrictiveDecision;
2177
+ }
2085
2178
  }
2086
2179
  }
2087
2180
  if (policy.degradations) {
@@ -2111,7 +2204,7 @@ var ControlAgent = class {
2111
2204
  timestamp: /* @__PURE__ */ new Date()
2112
2205
  };
2113
2206
  Promise.resolve(this.options.onAlert(alertEvent)).catch((err) => {
2114
- console.warn("[aden] Alert callback error:", err);
2207
+ logger.warn("Alert callback error:", err);
2115
2208
  });
2116
2209
  return {
2117
2210
  action: "alert",
@@ -2174,36 +2267,96 @@ var ControlAgent = class {
2174
2267
  return true;
2175
2268
  }
2176
2269
  /**
2177
- * Find budgets that apply to the given request based on budget type
2270
+ * Get action priority for finding most restrictive decision.
2271
+ * Higher priority = more restrictive.
2272
+ */
2273
+ getActionPriority(action) {
2274
+ const priority = {
2275
+ allow: 0,
2276
+ alert: 1,
2277
+ throttle: 2,
2278
+ degrade: 3,
2279
+ block: 4
2280
+ };
2281
+ return priority[action] ?? 0;
2282
+ }
2283
+ /**
2284
+ * Evaluate a single budget using local-only enforcement.
2285
+ * Returns a decision if the budget triggers an action, null otherwise.
2286
+ */
2287
+ evaluateBudgetLocally(request, budget, throttleInfo) {
2288
+ const projectedSpend = budget.spent + (request.estimated_cost ?? 0);
2289
+ if (projectedSpend > budget.limit) {
2290
+ if (budget.limitAction === "degrade" && budget.degradeToModel) {
2291
+ return {
2292
+ action: "degrade",
2293
+ reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`,
2294
+ degradeToModel: budget.degradeToModel,
2295
+ ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2296
+ };
2297
+ }
2298
+ const action = budget.limitAction === "kill" ? "block" : budget.limitAction;
2299
+ return {
2300
+ action,
2301
+ reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`
2302
+ };
2303
+ }
2304
+ return null;
2305
+ }
2306
+ /**
2307
+ * Find budgets that apply to the given request based on budget type.
2308
+ *
2309
+ * Matching logic by budget type:
2310
+ * - global: Matches ALL requests
2311
+ * - agent: Matches if request.metadata.agent == budget.name or budget.id
2312
+ * - tenant: Matches if request.metadata.tenant_id == budget.name or budget.id
2313
+ * - customer: Matches if request.metadata.customer_id == budget.name or budget.id
2314
+ * - feature: Matches if request.metadata.feature == budget.name or budget.id
2315
+ * - tag: Matches if any request.metadata.tags intersect with budget.tags
2316
+ * - legacy (context_id): Matches if request.context_id == budget.context_id
2178
2317
  */
2179
2318
  findApplicableBudgets(budgets, request) {
2180
2319
  const result = [];
2181
2320
  const metadata = request.metadata || {};
2182
2321
  for (const budget of budgets) {
2322
+ if (budget.context_id && request.context_id) {
2323
+ if (budget.context_id === request.context_id) {
2324
+ result.push(budget);
2325
+ continue;
2326
+ }
2327
+ }
2183
2328
  switch (budget.type) {
2184
2329
  case "global":
2185
2330
  result.push(budget);
2186
2331
  break;
2187
- case "agent":
2188
- if (metadata.agent_id && budget.id.includes(String(metadata.agent_id))) {
2332
+ case "agent": {
2333
+ const agent = metadata.agent;
2334
+ if (agent && (agent === budget.name || agent === budget.id)) {
2189
2335
  result.push(budget);
2190
2336
  }
2191
2337
  break;
2192
- case "tenant":
2193
- if (metadata.tenant_id && budget.id.includes(String(metadata.tenant_id))) {
2338
+ }
2339
+ case "tenant": {
2340
+ const tenantId = metadata.tenant_id;
2341
+ if (tenantId && (tenantId === budget.name || tenantId === budget.id)) {
2194
2342
  result.push(budget);
2195
2343
  }
2196
2344
  break;
2197
- case "customer":
2198
- if (metadata.customer_id && budget.id.includes(String(metadata.customer_id)) || request.context_id && budget.id.includes(request.context_id)) {
2345
+ }
2346
+ case "customer": {
2347
+ const customerId = metadata.customer_id;
2348
+ if (customerId && (customerId === budget.name || customerId === budget.id) || request.context_id && (request.context_id === budget.name || request.context_id === budget.id)) {
2199
2349
  result.push(budget);
2200
2350
  }
2201
2351
  break;
2202
- case "feature":
2203
- if (metadata.feature && budget.id.includes(String(metadata.feature))) {
2352
+ }
2353
+ case "feature": {
2354
+ const feature = metadata.feature;
2355
+ if (feature && (feature === budget.name || feature === budget.id)) {
2204
2356
  result.push(budget);
2205
2357
  }
2206
2358
  break;
2359
+ }
2207
2360
  case "tag":
2208
2361
  if (budget.tags && Array.isArray(metadata.tags)) {
2209
2362
  const requestTags = metadata.tags;
@@ -2253,13 +2406,48 @@ var ControlAgent = class {
2253
2406
  if (this.cachedPolicy?.budgets && event.total_tokens > 0) {
2254
2407
  const estimatedCost = this.estimateCost(event);
2255
2408
  const contextId2 = this.options.getContextId?.();
2409
+ const metadata = event.metadata || {};
2256
2410
  for (const budget of this.cachedPolicy.budgets) {
2257
- if (budget.type === "global") {
2258
- budget.spent += estimatedCost;
2259
- } else if (budget.type === "customer" && contextId2) {
2260
- if (budget.id === contextId2 || budget.id.includes(contextId2)) {
2261
- budget.spent += estimatedCost;
2411
+ let shouldUpdate = false;
2412
+ switch (budget.type) {
2413
+ case "global":
2414
+ shouldUpdate = true;
2415
+ break;
2416
+ case "agent": {
2417
+ const agent = metadata.agent;
2418
+ shouldUpdate = Boolean(agent && (agent === budget.name || agent === budget.id));
2419
+ break;
2262
2420
  }
2421
+ case "tenant": {
2422
+ const tenantId = metadata.tenant_id;
2423
+ shouldUpdate = Boolean(tenantId && (tenantId === budget.name || tenantId === budget.id));
2424
+ break;
2425
+ }
2426
+ case "customer": {
2427
+ const customerId = metadata.customer_id;
2428
+ shouldUpdate = Boolean(
2429
+ customerId && (customerId === budget.name || customerId === budget.id) || contextId2 && (contextId2 === budget.name || contextId2 === budget.id)
2430
+ );
2431
+ break;
2432
+ }
2433
+ case "feature": {
2434
+ const feature = metadata.feature;
2435
+ shouldUpdate = Boolean(feature && (feature === budget.name || feature === budget.id));
2436
+ break;
2437
+ }
2438
+ case "tag": {
2439
+ if (budget.tags && Array.isArray(metadata.tags)) {
2440
+ const requestTags = metadata.tags;
2441
+ shouldUpdate = budget.tags.some((tag) => requestTags.includes(tag));
2442
+ }
2443
+ break;
2444
+ }
2445
+ }
2446
+ if (!shouldUpdate && budget.context_id && contextId2) {
2447
+ shouldUpdate = budget.context_id === contextId2;
2448
+ }
2449
+ if (shouldUpdate) {
2450
+ budget.spent += estimatedCost;
2263
2451
  }
2264
2452
  }
2265
2453
  }
@@ -2323,8 +2511,8 @@ var ControlAgent = class {
2323
2511
  }
2324
2512
  if (this.options.adaptiveThresholdEnabled) {
2325
2513
  if (remainingBudgetUsd <= this.options.adaptiveMinRemainingUsd) {
2326
- console.debug(
2327
- `[aden] Remaining budget $${remainingBudgetUsd.toFixed(2)} <= $${this.options.adaptiveMinRemainingUsd.toFixed(2)}, forcing validation`
2514
+ logger.debug(
2515
+ `Remaining budget $${remainingBudgetUsd.toFixed(4)} <= $${this.options.adaptiveMinRemainingUsd.toFixed(2)}, forcing validation`
2328
2516
  );
2329
2517
  return true;
2330
2518
  }
@@ -2333,13 +2521,13 @@ var ControlAgent = class {
2333
2521
  const samplingRate = this.calculateSamplingRate(budgetUsagePercent);
2334
2522
  const shouldSample = Math.random() < samplingRate;
2335
2523
  if (!shouldSample) {
2336
- console.debug(
2337
- `[aden] Skipping validation (sampling rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2524
+ logger.debug(
2525
+ `Skipping validation (sampling rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2338
2526
  );
2339
2527
  return false;
2340
2528
  }
2341
- console.debug(
2342
- `[aden] Sampled for validation (rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2529
+ logger.debug(
2530
+ `Sampled for validation (rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2343
2531
  );
2344
2532
  return true;
2345
2533
  }
@@ -2397,8 +2585,8 @@ var ControlAgent = class {
2397
2585
  });
2398
2586
  if (response.ok) {
2399
2587
  const data = await response.json();
2400
- console.debug(
2401
- `[aden] Server validation response: allowed=${data.allowed}, action=${data.action}, reason=${data.reason}`
2588
+ logger.debug(
2589
+ `Server validation response: allowed=${data.allowed}, action=${data.action}, reason=${data.reason}`
2402
2590
  );
2403
2591
  return {
2404
2592
  allowed: data.allowed ?? true,
@@ -2413,14 +2601,14 @@ var ControlAgent = class {
2413
2601
  degradeToModel: data.degrade_to_model
2414
2602
  };
2415
2603
  } else {
2416
- console.warn(`[aden] Server validation returned status ${response.status}`);
2604
+ logger.warn(`Server validation returned status ${response.status}`);
2417
2605
  return null;
2418
2606
  }
2419
2607
  } finally {
2420
2608
  clearTimeout(timeoutId);
2421
2609
  }
2422
2610
  } catch (error) {
2423
- console.warn(`[aden] Server validation failed: ${error}`);
2611
+ logger.warn(`Server validation failed: ${error}`);
2424
2612
  return null;
2425
2613
  }
2426
2614
  }
@@ -2460,8 +2648,8 @@ var ControlAgent = class {
2460
2648
  }
2461
2649
  }
2462
2650
  if (this.shouldValidateWithServer(usagePercent, remaining, limit)) {
2463
- console.debug(
2464
- `[aden] Budget '${budget.id}' at ${usagePercent.toFixed(1)}% ($${currentSpend.toFixed(2)}/$${limit.toFixed(2)}), validating with server`
2651
+ logger.debug(
2652
+ `Budget '${budget.name}' at ${usagePercent.toFixed(1)}% ($${currentSpend.toFixed(6)}/$${limit.toFixed(6)}), validating with server`
2465
2653
  );
2466
2654
  const validation = await this.validateBudgetWithServer(
2467
2655
  budget.id,
@@ -2471,8 +2659,8 @@ var ControlAgent = class {
2471
2659
  if (validation) {
2472
2660
  return this.applyServerValidationResult(validation, budget.id);
2473
2661
  } else {
2474
- console.warn(
2475
- `[aden] Server validation failed for budget '${budget.id}', using local enforcement`
2662
+ logger.warn(
2663
+ `Server validation failed for budget '${budget.id}', using local enforcement`
2476
2664
  );
2477
2665
  if (!this.options.failOpen) {
2478
2666
  return {
@@ -2534,12 +2722,14 @@ var ControlAgent = class {
2534
2722
  if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
2535
2723
  try {
2536
2724
  this.ws.send(JSON.stringify(event));
2725
+ logger.debug(`Event sent via WebSocket: ${event.event_type}`);
2537
2726
  return;
2538
2727
  } catch (error) {
2539
- console.warn("[aden] WebSocket send failed, queuing event");
2728
+ logger.warn("WebSocket send failed, queuing event");
2540
2729
  }
2541
2730
  }
2542
2731
  this.queueEvent(event);
2732
+ logger.debug(`Event queued: ${event.event_type} (queue size: ${this.eventQueue.length})`);
2543
2733
  if (!this.connected) {
2544
2734
  await this.flushEventQueue();
2545
2735
  }
@@ -2558,7 +2748,9 @@ var ControlAgent = class {
2558
2748
  */
2559
2749
  async flushEventQueue() {
2560
2750
  if (this.eventQueue.length === 0) return;
2751
+ const eventCount = this.eventQueue.length;
2561
2752
  if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
2753
+ logger.debug(`Flushing ${eventCount} events via WebSocket`);
2562
2754
  const events = [...this.eventQueue];
2563
2755
  this.eventQueue = [];
2564
2756
  for (const event of events) {
@@ -2570,12 +2762,14 @@ var ControlAgent = class {
2570
2762
  }
2571
2763
  return;
2572
2764
  }
2765
+ logger.debug(`Flushing ${eventCount} events via HTTP`);
2573
2766
  try {
2574
2767
  const events = [...this.eventQueue];
2575
2768
  this.eventQueue = [];
2576
2769
  await this.httpRequest("/v1/control/events", "POST", { events });
2770
+ logger.debug(`Successfully sent ${eventCount} events via HTTP`);
2577
2771
  } catch (error) {
2578
- console.warn("[aden] Failed to flush event queue:", error);
2772
+ logger.warn("Failed to flush event queue:", error);
2579
2773
  }
2580
2774
  }
2581
2775
  /**
@@ -6447,99 +6641,6 @@ function createReportEmitter(builder) {
6447
6641
  builder.recordEvent(event);
6448
6642
  };
6449
6643
  }
6450
-
6451
- // src/logging.ts
6452
- var LOG_LEVEL_PRIORITY = {
6453
- debug: 0,
6454
- info: 1,
6455
- warn: 2,
6456
- error: 3,
6457
- silent: 4
6458
- };
6459
- var currentConfig = {
6460
- level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6461
- metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6462
- handler: console
6463
- };
6464
- function parseLogLevel(level) {
6465
- if (!level) return void 0;
6466
- const normalized = level.toLowerCase();
6467
- if (normalized in LOG_LEVEL_PRIORITY) {
6468
- return normalized;
6469
- }
6470
- return void 0;
6471
- }
6472
- function shouldLog(level, configLevel) {
6473
- return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[configLevel];
6474
- }
6475
- function configureLogging(config) {
6476
- if (config.level !== void 0) {
6477
- currentConfig.level = config.level;
6478
- if (config.metricsLevel === void 0) {
6479
- currentConfig.metricsLevel = config.level;
6480
- }
6481
- }
6482
- if (config.metricsLevel !== void 0) {
6483
- currentConfig.metricsLevel = config.metricsLevel;
6484
- }
6485
- if (config.handler !== void 0) {
6486
- currentConfig.handler = config.handler;
6487
- }
6488
- }
6489
- function getLoggingConfig() {
6490
- return { ...currentConfig };
6491
- }
6492
- function resetLoggingConfig() {
6493
- currentConfig = {
6494
- level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6495
- metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6496
- handler: console
6497
- };
6498
- }
6499
- var logger = {
6500
- debug(message, ...args) {
6501
- if (shouldLog("debug", currentConfig.level)) {
6502
- currentConfig.handler.debug(`[aden] ${message}`, ...args);
6503
- }
6504
- },
6505
- info(message, ...args) {
6506
- if (shouldLog("info", currentConfig.level)) {
6507
- currentConfig.handler.info(`[aden] ${message}`, ...args);
6508
- }
6509
- },
6510
- warn(message, ...args) {
6511
- if (shouldLog("warn", currentConfig.level)) {
6512
- currentConfig.handler.warn(`[aden] ${message}`, ...args);
6513
- }
6514
- },
6515
- error(message, ...args) {
6516
- if (shouldLog("error", currentConfig.level)) {
6517
- currentConfig.handler.error(`[aden] ${message}`, ...args);
6518
- }
6519
- }
6520
- };
6521
- var metricsLogger = {
6522
- debug(message, ...args) {
6523
- if (shouldLog("debug", currentConfig.metricsLevel)) {
6524
- currentConfig.handler.debug(`[aden.metrics] ${message}`, ...args);
6525
- }
6526
- },
6527
- info(message, ...args) {
6528
- if (shouldLog("info", currentConfig.metricsLevel)) {
6529
- currentConfig.handler.info(`[aden.metrics] ${message}`, ...args);
6530
- }
6531
- },
6532
- warn(message, ...args) {
6533
- if (shouldLog("warn", currentConfig.metricsLevel)) {
6534
- currentConfig.handler.warn(`[aden.metrics] ${message}`, ...args);
6535
- }
6536
- },
6537
- error(message, ...args) {
6538
- if (shouldLog("error", currentConfig.metricsLevel)) {
6539
- currentConfig.handler.error(`[aden.metrics] ${message}`, ...args);
6540
- }
6541
- }
6542
- };
6543
6644
  export {
6544
6645
  AnalyticsEngine,
6545
6646
  BudgetExceededError,