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.js CHANGED
@@ -1856,6 +1856,101 @@ function isAnthropicInstrumented() {
1856
1856
  // src/control-agent.ts
1857
1857
  var import_crypto6 = require("crypto");
1858
1858
  var import_ws = __toESM(require("ws"));
1859
+
1860
+ // src/logging.ts
1861
+ var LOG_LEVEL_PRIORITY = {
1862
+ debug: 0,
1863
+ info: 1,
1864
+ warn: 2,
1865
+ error: 3,
1866
+ silent: 4
1867
+ };
1868
+ var currentConfig = {
1869
+ level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1870
+ metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1871
+ handler: console
1872
+ };
1873
+ function parseLogLevel(level) {
1874
+ if (!level) return void 0;
1875
+ const normalized = level.toLowerCase();
1876
+ if (normalized in LOG_LEVEL_PRIORITY) {
1877
+ return normalized;
1878
+ }
1879
+ return void 0;
1880
+ }
1881
+ function shouldLog(level, configLevel) {
1882
+ return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[configLevel];
1883
+ }
1884
+ function configureLogging(config) {
1885
+ if (config.level !== void 0) {
1886
+ currentConfig.level = config.level;
1887
+ if (config.metricsLevel === void 0) {
1888
+ currentConfig.metricsLevel = config.level;
1889
+ }
1890
+ }
1891
+ if (config.metricsLevel !== void 0) {
1892
+ currentConfig.metricsLevel = config.metricsLevel;
1893
+ }
1894
+ if (config.handler !== void 0) {
1895
+ currentConfig.handler = config.handler;
1896
+ }
1897
+ }
1898
+ function getLoggingConfig() {
1899
+ return { ...currentConfig };
1900
+ }
1901
+ function resetLoggingConfig() {
1902
+ currentConfig = {
1903
+ level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1904
+ metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
1905
+ handler: console
1906
+ };
1907
+ }
1908
+ var logger = {
1909
+ debug(message, ...args) {
1910
+ if (shouldLog("debug", currentConfig.level)) {
1911
+ currentConfig.handler.debug(`[aden] ${message}`, ...args);
1912
+ }
1913
+ },
1914
+ info(message, ...args) {
1915
+ if (shouldLog("info", currentConfig.level)) {
1916
+ currentConfig.handler.info(`[aden] ${message}`, ...args);
1917
+ }
1918
+ },
1919
+ warn(message, ...args) {
1920
+ if (shouldLog("warn", currentConfig.level)) {
1921
+ currentConfig.handler.warn(`[aden] ${message}`, ...args);
1922
+ }
1923
+ },
1924
+ error(message, ...args) {
1925
+ if (shouldLog("error", currentConfig.level)) {
1926
+ currentConfig.handler.error(`[aden] ${message}`, ...args);
1927
+ }
1928
+ }
1929
+ };
1930
+ var metricsLogger = {
1931
+ debug(message, ...args) {
1932
+ if (shouldLog("debug", currentConfig.metricsLevel)) {
1933
+ currentConfig.handler.debug(`[aden.metrics] ${message}`, ...args);
1934
+ }
1935
+ },
1936
+ info(message, ...args) {
1937
+ if (shouldLog("info", currentConfig.metricsLevel)) {
1938
+ currentConfig.handler.info(`[aden.metrics] ${message}`, ...args);
1939
+ }
1940
+ },
1941
+ warn(message, ...args) {
1942
+ if (shouldLog("warn", currentConfig.metricsLevel)) {
1943
+ currentConfig.handler.warn(`[aden.metrics] ${message}`, ...args);
1944
+ }
1945
+ },
1946
+ error(message, ...args) {
1947
+ if (shouldLog("error", currentConfig.metricsLevel)) {
1948
+ currentConfig.handler.error(`[aden.metrics] ${message}`, ...args);
1949
+ }
1950
+ }
1951
+ };
1952
+
1953
+ // src/control-agent.ts
1859
1954
  var SDK_VERSION = "0.1.0";
1860
1955
  var ControlAgent = class {
1861
1956
  constructor(options) {
@@ -1886,12 +1981,12 @@ var ControlAgent = class {
1886
1981
  instanceId: options.instanceId ?? (0, import_crypto6.randomUUID)(),
1887
1982
  onAlert: options.onAlert ?? (() => {
1888
1983
  }),
1889
- // Hybrid enforcement options
1890
- enableHybridEnforcement: options.enableHybridEnforcement ?? false,
1891
- serverValidationThreshold: options.serverValidationThreshold ?? 80,
1984
+ // Hybrid enforcement options (defaults match Python SDK)
1985
+ enableHybridEnforcement: options.enableHybridEnforcement ?? true,
1986
+ serverValidationThreshold: options.serverValidationThreshold ?? 5,
1892
1987
  serverValidationTimeoutMs: options.serverValidationTimeoutMs ?? 2e3,
1893
1988
  adaptiveThresholdEnabled: options.adaptiveThresholdEnabled ?? true,
1894
- adaptiveMinRemainingUsd: options.adaptiveMinRemainingUsd ?? 1,
1989
+ adaptiveMinRemainingUsd: options.adaptiveMinRemainingUsd ?? 5,
1895
1990
  samplingEnabled: options.samplingEnabled ?? true,
1896
1991
  samplingBaseRate: options.samplingBaseRate ?? 0.1,
1897
1992
  samplingFullValidationPercent: options.samplingFullValidationPercent ?? 95,
@@ -1903,9 +1998,11 @@ var ControlAgent = class {
1903
1998
  */
1904
1999
  async connect() {
1905
2000
  const url = this.options.serverUrl;
2001
+ logger.debug(`Connecting to control server: ${url}`);
1906
2002
  if (url.startsWith("wss://") || url.startsWith("ws://")) {
1907
2003
  await this.connectWebSocket();
1908
2004
  } else {
2005
+ logger.debug("Using HTTP polling mode (no WebSocket URL)");
1909
2006
  this.startPolling();
1910
2007
  }
1911
2008
  this.startHeartbeat();
@@ -1921,6 +2018,7 @@ var ControlAgent = class {
1921
2018
  };
1922
2019
  try {
1923
2020
  const wsUrl = `${this.options.serverUrl}/v1/control/ws`;
2021
+ logger.debug(`Attempting WebSocket connection to: ${wsUrl}`);
1924
2022
  this.ws = new import_ws.default(wsUrl, {
1925
2023
  headers: {
1926
2024
  Authorization: `Bearer ${this.options.apiKey}`,
@@ -1930,7 +2028,7 @@ var ControlAgent = class {
1930
2028
  this.ws.on("open", () => {
1931
2029
  this.connected = true;
1932
2030
  this.reconnectAttempts = 0;
1933
- console.log("[aden] WebSocket connected to control server");
2031
+ logger.info("WebSocket connected to control server");
1934
2032
  this.flushEventQueue();
1935
2033
  resolve();
1936
2034
  });
@@ -1939,12 +2037,12 @@ var ControlAgent = class {
1939
2037
  });
1940
2038
  this.ws.on("close", () => {
1941
2039
  this.connected = false;
1942
- console.log("[aden] WebSocket disconnected, falling back to polling");
2040
+ logger.info("WebSocket disconnected, falling back to polling");
1943
2041
  this.scheduleReconnect();
1944
2042
  this.startPolling();
1945
2043
  });
1946
2044
  this.ws.on("error", (error) => {
1947
- console.warn("[aden] WebSocket error:", error.message);
2045
+ logger.warn("WebSocket error:", error.message);
1948
2046
  this.errorsSinceLastHeartbeat++;
1949
2047
  if (!this.connected) {
1950
2048
  fallbackToPolling();
@@ -1952,12 +2050,12 @@ var ControlAgent = class {
1952
2050
  });
1953
2051
  setTimeout(() => {
1954
2052
  if (!this.connected) {
1955
- console.warn("[aden] WebSocket connection timeout, using polling");
2053
+ logger.warn("WebSocket connection timeout, using polling");
1956
2054
  fallbackToPolling();
1957
2055
  }
1958
2056
  }, this.options.timeoutMs);
1959
2057
  } catch (error) {
1960
- console.warn("[aden] WebSocket setup failed:", error);
2058
+ logger.warn("WebSocket setup failed:", error);
1961
2059
  fallbackToPolling();
1962
2060
  }
1963
2061
  });
@@ -1968,7 +2066,7 @@ var ControlAgent = class {
1968
2066
  scheduleReconnect() {
1969
2067
  if (this.reconnectTimer) return;
1970
2068
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
1971
- console.warn("[aden] Max reconnect attempts reached, using polling only");
2069
+ logger.warn("Max reconnect attempts reached, using polling only");
1972
2070
  return;
1973
2071
  }
1974
2072
  const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
@@ -1989,12 +2087,12 @@ var ControlAgent = class {
1989
2087
  if (message.type === "policy") {
1990
2088
  this.cachedPolicy = message.policy;
1991
2089
  this.lastPolicyFetch = Date.now();
1992
- console.log("[aden] Policy updated:", this.cachedPolicy.version);
2090
+ logger.info("Policy updated:", this.cachedPolicy.version);
1993
2091
  } else if (message.type === "command") {
1994
- console.log("[aden] Command received:", message);
2092
+ logger.info("Command received:", message);
1995
2093
  }
1996
2094
  } catch (error) {
1997
- console.warn("[aden] Failed to parse message:", error);
2095
+ logger.warn("Failed to parse message:", error);
1998
2096
  }
1999
2097
  }
2000
2098
  /**
@@ -2003,6 +2101,7 @@ var ControlAgent = class {
2003
2101
  */
2004
2102
  async startPolling() {
2005
2103
  if (this.pollingTimer) return;
2104
+ logger.debug(`Starting HTTP polling (interval: ${this.options.pollingIntervalMs}ms)`);
2006
2105
  await this.fetchPolicy();
2007
2106
  this.pollingTimer = setInterval(() => {
2008
2107
  if (!this.connected) {
@@ -2023,15 +2122,19 @@ var ControlAgent = class {
2023
2122
  * Fetch policy via HTTP
2024
2123
  */
2025
2124
  async fetchPolicy() {
2125
+ logger.debug("Fetching policy from server...");
2026
2126
  try {
2027
2127
  const response = await this.httpRequest("/v1/control/policy", "GET");
2028
2128
  if (response.ok) {
2029
2129
  const policy = await response.json();
2030
2130
  this.cachedPolicy = policy;
2031
2131
  this.lastPolicyFetch = Date.now();
2132
+ logger.debug(`Policy fetched successfully (version: ${policy.version}, budgets: ${policy.budgets?.length ?? 0})`);
2133
+ } else {
2134
+ logger.debug(`Policy fetch returned status ${response.status}`);
2032
2135
  }
2033
2136
  } catch (error) {
2034
- console.warn("[aden] Failed to fetch policy:", error);
2137
+ logger.warn("Failed to fetch policy:", error);
2035
2138
  }
2036
2139
  }
2037
2140
  /**
@@ -2039,6 +2142,7 @@ var ControlAgent = class {
2039
2142
  */
2040
2143
  startHeartbeat() {
2041
2144
  if (this.heartbeatTimer) return;
2145
+ logger.debug(`Starting heartbeat (interval: ${this.options.heartbeatIntervalMs}ms)`);
2042
2146
  this.heartbeatTimer = setInterval(() => {
2043
2147
  this.sendHeartbeat();
2044
2148
  }, this.options.heartbeatIntervalMs);
@@ -2075,6 +2179,7 @@ var ControlAgent = class {
2075
2179
  * Disconnect from the control server
2076
2180
  */
2077
2181
  async disconnect() {
2182
+ logger.debug("Disconnecting from control server...");
2078
2183
  this.stopPolling();
2079
2184
  this.stopHeartbeat();
2080
2185
  if (this.reconnectTimer) {
@@ -2086,6 +2191,7 @@ var ControlAgent = class {
2086
2191
  this.ws = null;
2087
2192
  }
2088
2193
  this.connected = false;
2194
+ logger.debug("Disconnected from control server");
2089
2195
  }
2090
2196
  /**
2091
2197
  * Get a control decision for a request
@@ -2139,66 +2245,53 @@ var ControlAgent = class {
2139
2245
  }
2140
2246
  if (policy.budgets) {
2141
2247
  const applicableBudgets = this.findApplicableBudgets(policy.budgets, request);
2142
- if (this.options.enableHybridEnforcement) {
2248
+ if (applicableBudgets.length > 0) {
2249
+ let mostRestrictiveDecision = null;
2250
+ let mostRestrictivePriority = -1;
2143
2251
  for (const budget of applicableBudgets) {
2144
- const decision = await this.evaluateBudgetWithHybridEnforcement(
2145
- request,
2146
- budget,
2147
- throttleInfo
2148
- );
2149
- if (decision) {
2150
- return decision;
2252
+ let decision = null;
2253
+ if (this.options.enableHybridEnforcement) {
2254
+ decision = await this.evaluateBudgetWithHybridEnforcement(
2255
+ request,
2256
+ budget,
2257
+ throttleInfo
2258
+ );
2259
+ } else {
2260
+ decision = this.evaluateBudgetLocally(request, budget, throttleInfo);
2151
2261
  }
2152
- if (policy.degradations) {
2262
+ if (!decision && policy.degradations) {
2153
2263
  for (const degrade of policy.degradations) {
2154
2264
  if (degrade.from_model === request.model && degrade.trigger === "budget_threshold" && degrade.threshold_percent) {
2155
2265
  const usagePercent = budget.spent / budget.limit * 100;
2156
2266
  if (usagePercent >= degrade.threshold_percent) {
2157
- return {
2267
+ decision = {
2158
2268
  action: "degrade",
2159
2269
  reason: `Budget "${budget.name}" at ${usagePercent.toFixed(1)}% (threshold: ${degrade.threshold_percent}%)`,
2160
2270
  degradeToModel: degrade.to_model,
2161
2271
  ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2162
2272
  };
2273
+ break;
2163
2274
  }
2164
2275
  }
2165
2276
  }
2166
2277
  }
2167
- }
2168
- } else {
2169
- for (const budget of applicableBudgets) {
2170
- const projectedSpend = budget.spent + (request.estimated_cost ?? 0);
2171
- if (projectedSpend > budget.limit) {
2172
- if (budget.limitAction === "degrade" && budget.degradeToModel) {
2173
- return {
2174
- action: "degrade",
2175
- reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`,
2176
- degradeToModel: budget.degradeToModel,
2177
- ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2178
- };
2278
+ if (decision) {
2279
+ const priority = this.getActionPriority(decision.action);
2280
+ if (priority > mostRestrictivePriority) {
2281
+ mostRestrictiveDecision = decision;
2282
+ mostRestrictivePriority = priority;
2179
2283
  }
2180
- const action = budget.limitAction === "kill" ? "block" : budget.limitAction;
2181
- return {
2182
- action,
2183
- reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`
2184
- };
2185
- }
2186
- if (policy.degradations) {
2187
- for (const degrade of policy.degradations) {
2188
- if (degrade.from_model === request.model && degrade.trigger === "budget_threshold" && degrade.threshold_percent) {
2189
- const usagePercent = budget.spent / budget.limit * 100;
2190
- if (usagePercent >= degrade.threshold_percent) {
2191
- return {
2192
- action: "degrade",
2193
- reason: `Budget "${budget.name}" at ${usagePercent.toFixed(1)}% (threshold: ${degrade.threshold_percent}%)`,
2194
- degradeToModel: degrade.to_model,
2195
- ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2196
- };
2197
- }
2198
- }
2284
+ if (decision.action === "block") {
2285
+ break;
2199
2286
  }
2200
2287
  }
2201
2288
  }
2289
+ if (mostRestrictiveDecision) {
2290
+ if (throttleInfo && mostRestrictiveDecision.action !== "block" && !mostRestrictiveDecision.throttleDelayMs) {
2291
+ mostRestrictiveDecision.throttleDelayMs = throttleInfo.delayMs;
2292
+ }
2293
+ return mostRestrictiveDecision;
2294
+ }
2202
2295
  }
2203
2296
  }
2204
2297
  if (policy.degradations) {
@@ -2228,7 +2321,7 @@ var ControlAgent = class {
2228
2321
  timestamp: /* @__PURE__ */ new Date()
2229
2322
  };
2230
2323
  Promise.resolve(this.options.onAlert(alertEvent)).catch((err) => {
2231
- console.warn("[aden] Alert callback error:", err);
2324
+ logger.warn("Alert callback error:", err);
2232
2325
  });
2233
2326
  return {
2234
2327
  action: "alert",
@@ -2291,36 +2384,96 @@ var ControlAgent = class {
2291
2384
  return true;
2292
2385
  }
2293
2386
  /**
2294
- * Find budgets that apply to the given request based on budget type
2387
+ * Get action priority for finding most restrictive decision.
2388
+ * Higher priority = more restrictive.
2389
+ */
2390
+ getActionPriority(action) {
2391
+ const priority = {
2392
+ allow: 0,
2393
+ alert: 1,
2394
+ throttle: 2,
2395
+ degrade: 3,
2396
+ block: 4
2397
+ };
2398
+ return priority[action] ?? 0;
2399
+ }
2400
+ /**
2401
+ * Evaluate a single budget using local-only enforcement.
2402
+ * Returns a decision if the budget triggers an action, null otherwise.
2403
+ */
2404
+ evaluateBudgetLocally(request, budget, throttleInfo) {
2405
+ const projectedSpend = budget.spent + (request.estimated_cost ?? 0);
2406
+ if (projectedSpend > budget.limit) {
2407
+ if (budget.limitAction === "degrade" && budget.degradeToModel) {
2408
+ return {
2409
+ action: "degrade",
2410
+ reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`,
2411
+ degradeToModel: budget.degradeToModel,
2412
+ ...throttleInfo && { throttleDelayMs: throttleInfo.delayMs }
2413
+ };
2414
+ }
2415
+ const action = budget.limitAction === "kill" ? "block" : budget.limitAction;
2416
+ return {
2417
+ action,
2418
+ reason: `Budget "${budget.name}" exceeded: $${projectedSpend.toFixed(4)} > $${budget.limit}`
2419
+ };
2420
+ }
2421
+ return null;
2422
+ }
2423
+ /**
2424
+ * Find budgets that apply to the given request based on budget type.
2425
+ *
2426
+ * Matching logic by budget type:
2427
+ * - global: Matches ALL requests
2428
+ * - agent: Matches if request.metadata.agent == budget.name or budget.id
2429
+ * - tenant: Matches if request.metadata.tenant_id == budget.name or budget.id
2430
+ * - customer: Matches if request.metadata.customer_id == budget.name or budget.id
2431
+ * - feature: Matches if request.metadata.feature == budget.name or budget.id
2432
+ * - tag: Matches if any request.metadata.tags intersect with budget.tags
2433
+ * - legacy (context_id): Matches if request.context_id == budget.context_id
2295
2434
  */
2296
2435
  findApplicableBudgets(budgets, request) {
2297
2436
  const result = [];
2298
2437
  const metadata = request.metadata || {};
2299
2438
  for (const budget of budgets) {
2439
+ if (budget.context_id && request.context_id) {
2440
+ if (budget.context_id === request.context_id) {
2441
+ result.push(budget);
2442
+ continue;
2443
+ }
2444
+ }
2300
2445
  switch (budget.type) {
2301
2446
  case "global":
2302
2447
  result.push(budget);
2303
2448
  break;
2304
- case "agent":
2305
- if (metadata.agent_id && budget.id.includes(String(metadata.agent_id))) {
2449
+ case "agent": {
2450
+ const agent = metadata.agent;
2451
+ if (agent && (agent === budget.name || agent === budget.id)) {
2306
2452
  result.push(budget);
2307
2453
  }
2308
2454
  break;
2309
- case "tenant":
2310
- if (metadata.tenant_id && budget.id.includes(String(metadata.tenant_id))) {
2455
+ }
2456
+ case "tenant": {
2457
+ const tenantId = metadata.tenant_id;
2458
+ if (tenantId && (tenantId === budget.name || tenantId === budget.id)) {
2311
2459
  result.push(budget);
2312
2460
  }
2313
2461
  break;
2314
- case "customer":
2315
- if (metadata.customer_id && budget.id.includes(String(metadata.customer_id)) || request.context_id && budget.id.includes(request.context_id)) {
2462
+ }
2463
+ case "customer": {
2464
+ const customerId = metadata.customer_id;
2465
+ if (customerId && (customerId === budget.name || customerId === budget.id) || request.context_id && (request.context_id === budget.name || request.context_id === budget.id)) {
2316
2466
  result.push(budget);
2317
2467
  }
2318
2468
  break;
2319
- case "feature":
2320
- if (metadata.feature && budget.id.includes(String(metadata.feature))) {
2469
+ }
2470
+ case "feature": {
2471
+ const feature = metadata.feature;
2472
+ if (feature && (feature === budget.name || feature === budget.id)) {
2321
2473
  result.push(budget);
2322
2474
  }
2323
2475
  break;
2476
+ }
2324
2477
  case "tag":
2325
2478
  if (budget.tags && Array.isArray(metadata.tags)) {
2326
2479
  const requestTags = metadata.tags;
@@ -2370,13 +2523,48 @@ var ControlAgent = class {
2370
2523
  if (this.cachedPolicy?.budgets && event.total_tokens > 0) {
2371
2524
  const estimatedCost = this.estimateCost(event);
2372
2525
  const contextId2 = this.options.getContextId?.();
2526
+ const metadata = event.metadata || {};
2373
2527
  for (const budget of this.cachedPolicy.budgets) {
2374
- if (budget.type === "global") {
2375
- budget.spent += estimatedCost;
2376
- } else if (budget.type === "customer" && contextId2) {
2377
- if (budget.id === contextId2 || budget.id.includes(contextId2)) {
2378
- budget.spent += estimatedCost;
2528
+ let shouldUpdate = false;
2529
+ switch (budget.type) {
2530
+ case "global":
2531
+ shouldUpdate = true;
2532
+ break;
2533
+ case "agent": {
2534
+ const agent = metadata.agent;
2535
+ shouldUpdate = Boolean(agent && (agent === budget.name || agent === budget.id));
2536
+ break;
2379
2537
  }
2538
+ case "tenant": {
2539
+ const tenantId = metadata.tenant_id;
2540
+ shouldUpdate = Boolean(tenantId && (tenantId === budget.name || tenantId === budget.id));
2541
+ break;
2542
+ }
2543
+ case "customer": {
2544
+ const customerId = metadata.customer_id;
2545
+ shouldUpdate = Boolean(
2546
+ customerId && (customerId === budget.name || customerId === budget.id) || contextId2 && (contextId2 === budget.name || contextId2 === budget.id)
2547
+ );
2548
+ break;
2549
+ }
2550
+ case "feature": {
2551
+ const feature = metadata.feature;
2552
+ shouldUpdate = Boolean(feature && (feature === budget.name || feature === budget.id));
2553
+ break;
2554
+ }
2555
+ case "tag": {
2556
+ if (budget.tags && Array.isArray(metadata.tags)) {
2557
+ const requestTags = metadata.tags;
2558
+ shouldUpdate = budget.tags.some((tag) => requestTags.includes(tag));
2559
+ }
2560
+ break;
2561
+ }
2562
+ }
2563
+ if (!shouldUpdate && budget.context_id && contextId2) {
2564
+ shouldUpdate = budget.context_id === contextId2;
2565
+ }
2566
+ if (shouldUpdate) {
2567
+ budget.spent += estimatedCost;
2380
2568
  }
2381
2569
  }
2382
2570
  }
@@ -2440,8 +2628,8 @@ var ControlAgent = class {
2440
2628
  }
2441
2629
  if (this.options.adaptiveThresholdEnabled) {
2442
2630
  if (remainingBudgetUsd <= this.options.adaptiveMinRemainingUsd) {
2443
- console.debug(
2444
- `[aden] Remaining budget $${remainingBudgetUsd.toFixed(2)} <= $${this.options.adaptiveMinRemainingUsd.toFixed(2)}, forcing validation`
2631
+ logger.debug(
2632
+ `Remaining budget $${remainingBudgetUsd.toFixed(4)} <= $${this.options.adaptiveMinRemainingUsd.toFixed(2)}, forcing validation`
2445
2633
  );
2446
2634
  return true;
2447
2635
  }
@@ -2450,13 +2638,13 @@ var ControlAgent = class {
2450
2638
  const samplingRate = this.calculateSamplingRate(budgetUsagePercent);
2451
2639
  const shouldSample = Math.random() < samplingRate;
2452
2640
  if (!shouldSample) {
2453
- console.debug(
2454
- `[aden] Skipping validation (sampling rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2641
+ logger.debug(
2642
+ `Skipping validation (sampling rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2455
2643
  );
2456
2644
  return false;
2457
2645
  }
2458
- console.debug(
2459
- `[aden] Sampled for validation (rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2646
+ logger.debug(
2647
+ `Sampled for validation (rate: ${(samplingRate * 100).toFixed(1)}%, usage: ${budgetUsagePercent.toFixed(1)}%)`
2460
2648
  );
2461
2649
  return true;
2462
2650
  }
@@ -2514,8 +2702,8 @@ var ControlAgent = class {
2514
2702
  });
2515
2703
  if (response.ok) {
2516
2704
  const data = await response.json();
2517
- console.debug(
2518
- `[aden] Server validation response: allowed=${data.allowed}, action=${data.action}, reason=${data.reason}`
2705
+ logger.debug(
2706
+ `Server validation response: allowed=${data.allowed}, action=${data.action}, reason=${data.reason}`
2519
2707
  );
2520
2708
  return {
2521
2709
  allowed: data.allowed ?? true,
@@ -2530,14 +2718,14 @@ var ControlAgent = class {
2530
2718
  degradeToModel: data.degrade_to_model
2531
2719
  };
2532
2720
  } else {
2533
- console.warn(`[aden] Server validation returned status ${response.status}`);
2721
+ logger.warn(`Server validation returned status ${response.status}`);
2534
2722
  return null;
2535
2723
  }
2536
2724
  } finally {
2537
2725
  clearTimeout(timeoutId);
2538
2726
  }
2539
2727
  } catch (error) {
2540
- console.warn(`[aden] Server validation failed: ${error}`);
2728
+ logger.warn(`Server validation failed: ${error}`);
2541
2729
  return null;
2542
2730
  }
2543
2731
  }
@@ -2577,8 +2765,8 @@ var ControlAgent = class {
2577
2765
  }
2578
2766
  }
2579
2767
  if (this.shouldValidateWithServer(usagePercent, remaining, limit)) {
2580
- console.debug(
2581
- `[aden] Budget '${budget.id}' at ${usagePercent.toFixed(1)}% ($${currentSpend.toFixed(2)}/$${limit.toFixed(2)}), validating with server`
2768
+ logger.debug(
2769
+ `Budget '${budget.name}' at ${usagePercent.toFixed(1)}% ($${currentSpend.toFixed(6)}/$${limit.toFixed(6)}), validating with server`
2582
2770
  );
2583
2771
  const validation = await this.validateBudgetWithServer(
2584
2772
  budget.id,
@@ -2588,8 +2776,8 @@ var ControlAgent = class {
2588
2776
  if (validation) {
2589
2777
  return this.applyServerValidationResult(validation, budget.id);
2590
2778
  } else {
2591
- console.warn(
2592
- `[aden] Server validation failed for budget '${budget.id}', using local enforcement`
2779
+ logger.warn(
2780
+ `Server validation failed for budget '${budget.id}', using local enforcement`
2593
2781
  );
2594
2782
  if (!this.options.failOpen) {
2595
2783
  return {
@@ -2651,12 +2839,14 @@ var ControlAgent = class {
2651
2839
  if (this.connected && this.ws?.readyState === import_ws.default.OPEN) {
2652
2840
  try {
2653
2841
  this.ws.send(JSON.stringify(event));
2842
+ logger.debug(`Event sent via WebSocket: ${event.event_type}`);
2654
2843
  return;
2655
2844
  } catch (error) {
2656
- console.warn("[aden] WebSocket send failed, queuing event");
2845
+ logger.warn("WebSocket send failed, queuing event");
2657
2846
  }
2658
2847
  }
2659
2848
  this.queueEvent(event);
2849
+ logger.debug(`Event queued: ${event.event_type} (queue size: ${this.eventQueue.length})`);
2660
2850
  if (!this.connected) {
2661
2851
  await this.flushEventQueue();
2662
2852
  }
@@ -2675,7 +2865,9 @@ var ControlAgent = class {
2675
2865
  */
2676
2866
  async flushEventQueue() {
2677
2867
  if (this.eventQueue.length === 0) return;
2868
+ const eventCount = this.eventQueue.length;
2678
2869
  if (this.connected && this.ws?.readyState === import_ws.default.OPEN) {
2870
+ logger.debug(`Flushing ${eventCount} events via WebSocket`);
2679
2871
  const events = [...this.eventQueue];
2680
2872
  this.eventQueue = [];
2681
2873
  for (const event of events) {
@@ -2687,12 +2879,14 @@ var ControlAgent = class {
2687
2879
  }
2688
2880
  return;
2689
2881
  }
2882
+ logger.debug(`Flushing ${eventCount} events via HTTP`);
2690
2883
  try {
2691
2884
  const events = [...this.eventQueue];
2692
2885
  this.eventQueue = [];
2693
2886
  await this.httpRequest("/v1/control/events", "POST", { events });
2887
+ logger.debug(`Successfully sent ${eventCount} events via HTTP`);
2694
2888
  } catch (error) {
2695
- console.warn("[aden] Failed to flush event queue:", error);
2889
+ logger.warn("Failed to flush event queue:", error);
2696
2890
  }
2697
2891
  }
2698
2892
  /**
@@ -6564,99 +6758,6 @@ function createReportEmitter(builder) {
6564
6758
  builder.recordEvent(event);
6565
6759
  };
6566
6760
  }
6567
-
6568
- // src/logging.ts
6569
- var LOG_LEVEL_PRIORITY = {
6570
- debug: 0,
6571
- info: 1,
6572
- warn: 2,
6573
- error: 3,
6574
- silent: 4
6575
- };
6576
- var currentConfig = {
6577
- level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6578
- metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6579
- handler: console
6580
- };
6581
- function parseLogLevel(level) {
6582
- if (!level) return void 0;
6583
- const normalized = level.toLowerCase();
6584
- if (normalized in LOG_LEVEL_PRIORITY) {
6585
- return normalized;
6586
- }
6587
- return void 0;
6588
- }
6589
- function shouldLog(level, configLevel) {
6590
- return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[configLevel];
6591
- }
6592
- function configureLogging(config) {
6593
- if (config.level !== void 0) {
6594
- currentConfig.level = config.level;
6595
- if (config.metricsLevel === void 0) {
6596
- currentConfig.metricsLevel = config.level;
6597
- }
6598
- }
6599
- if (config.metricsLevel !== void 0) {
6600
- currentConfig.metricsLevel = config.metricsLevel;
6601
- }
6602
- if (config.handler !== void 0) {
6603
- currentConfig.handler = config.handler;
6604
- }
6605
- }
6606
- function getLoggingConfig() {
6607
- return { ...currentConfig };
6608
- }
6609
- function resetLoggingConfig() {
6610
- currentConfig = {
6611
- level: parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6612
- metricsLevel: parseLogLevel(process.env.ADEN_METRICS_LOG_LEVEL) ?? parseLogLevel(process.env.ADEN_LOG_LEVEL) ?? "info",
6613
- handler: console
6614
- };
6615
- }
6616
- var logger = {
6617
- debug(message, ...args) {
6618
- if (shouldLog("debug", currentConfig.level)) {
6619
- currentConfig.handler.debug(`[aden] ${message}`, ...args);
6620
- }
6621
- },
6622
- info(message, ...args) {
6623
- if (shouldLog("info", currentConfig.level)) {
6624
- currentConfig.handler.info(`[aden] ${message}`, ...args);
6625
- }
6626
- },
6627
- warn(message, ...args) {
6628
- if (shouldLog("warn", currentConfig.level)) {
6629
- currentConfig.handler.warn(`[aden] ${message}`, ...args);
6630
- }
6631
- },
6632
- error(message, ...args) {
6633
- if (shouldLog("error", currentConfig.level)) {
6634
- currentConfig.handler.error(`[aden] ${message}`, ...args);
6635
- }
6636
- }
6637
- };
6638
- var metricsLogger = {
6639
- debug(message, ...args) {
6640
- if (shouldLog("debug", currentConfig.metricsLevel)) {
6641
- currentConfig.handler.debug(`[aden.metrics] ${message}`, ...args);
6642
- }
6643
- },
6644
- info(message, ...args) {
6645
- if (shouldLog("info", currentConfig.metricsLevel)) {
6646
- currentConfig.handler.info(`[aden.metrics] ${message}`, ...args);
6647
- }
6648
- },
6649
- warn(message, ...args) {
6650
- if (shouldLog("warn", currentConfig.metricsLevel)) {
6651
- currentConfig.handler.warn(`[aden.metrics] ${message}`, ...args);
6652
- }
6653
- },
6654
- error(message, ...args) {
6655
- if (shouldLog("error", currentConfig.metricsLevel)) {
6656
- currentConfig.handler.error(`[aden.metrics] ${message}`, ...args);
6657
- }
6658
- }
6659
- };
6660
6761
  // Annotate the CommonJS export names for ESM import in node:
6661
6762
  0 && (module.exports = {
6662
6763
  AnalyticsEngine,