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.d.mts +25 -4
- package/dist/index.d.ts +25 -4
- package/dist/index.js +280 -179
- package/dist/index.mjs +280 -179
- package/package.json +1 -1
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 ??
|
|
1891
|
-
serverValidationThreshold: options.serverValidationThreshold ??
|
|
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 ??
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2053
|
+
logger.warn("WebSocket connection timeout, using polling");
|
|
1956
2054
|
fallbackToPolling();
|
|
1957
2055
|
}
|
|
1958
2056
|
}, this.options.timeoutMs);
|
|
1959
2057
|
} catch (error) {
|
|
1960
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2090
|
+
logger.info("Policy updated:", this.cachedPolicy.version);
|
|
1993
2091
|
} else if (message.type === "command") {
|
|
1994
|
-
|
|
2092
|
+
logger.info("Command received:", message);
|
|
1995
2093
|
}
|
|
1996
2094
|
} catch (error) {
|
|
1997
|
-
|
|
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
|
-
|
|
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 (
|
|
2248
|
+
if (applicableBudgets.length > 0) {
|
|
2249
|
+
let mostRestrictiveDecision = null;
|
|
2250
|
+
let mostRestrictivePriority = -1;
|
|
2143
2251
|
for (const budget of applicableBudgets) {
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
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
|
-
|
|
2181
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
2310
|
-
|
|
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
|
-
|
|
2315
|
-
|
|
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
|
-
|
|
2320
|
-
|
|
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
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
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
|
-
|
|
2444
|
-
`
|
|
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
|
-
|
|
2454
|
-
`
|
|
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
|
-
|
|
2459
|
-
`
|
|
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
|
-
|
|
2518
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2581
|
-
`
|
|
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
|
-
|
|
2592
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
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,
|