@defai.digital/automatosx 6.2.2 → 6.2.6
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/CHANGELOG.md +66 -0
- package/README.md +1 -1
- package/dist/index.js +111 -31
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [6.2.6] - 2025-10-31
|
|
6
|
+
|
|
7
|
+
### 🔧 Fixes
|
|
8
|
+
|
|
9
|
+
**Cache Prediction Division by Zero (Bug #33)**
|
|
10
|
+
|
|
11
|
+
Through ultra-deep systematic analysis of all division operations in the codebase:
|
|
12
|
+
|
|
13
|
+
- **Bug #33 (MEDIUM)**: Division by zero in adaptive cache prediction (`src/core/adaptive-cache.ts:244`)
|
|
14
|
+
- **Problem**: `intervals.reduce(...) / intervals.length` can divide by zero when timestamps array contains only undefined values
|
|
15
|
+
- **Impact**: NaN values in predictive cache scoring, corrupting cache access predictions
|
|
16
|
+
- **Root Cause**: Even with `timestamps.length >= 2` check, the intervals array could be empty if all timestamps were undefined
|
|
17
|
+
- **Fix**: Added guard to check `intervals.length === 0` after building intervals array, skip prediction for insufficient data
|
|
18
|
+
- **Pattern**: Consistent with defensive division guards from Bugs #16, #19, #31
|
|
19
|
+
|
|
20
|
+
### 🔍 Analysis Methodology
|
|
21
|
+
|
|
22
|
+
- **Ultra-deep grep analysis**: Systematically found and verified all 9 division operations in `src/core/`
|
|
23
|
+
- **Verified safe operations**: provider-metrics-tracker.ts (4 divisions), AnalyticsAggregator.ts (3 divisions)
|
|
24
|
+
- **Fixed vulnerable operation**: adaptive-cache.ts (1 division)
|
|
25
|
+
|
|
26
|
+
### 📊 Impact
|
|
27
|
+
|
|
28
|
+
- **Users affected**: Users with adaptive caching enabled (performance feature)
|
|
29
|
+
- **Breaking changes**: None
|
|
30
|
+
- **Migration**: None required - automatic graceful skip for invalid prediction data
|
|
31
|
+
|
|
32
|
+
### ✅ Testing
|
|
33
|
+
|
|
34
|
+
- All 2,281 unit tests passing
|
|
35
|
+
- TypeScript compilation successful
|
|
36
|
+
- Build successful
|
|
37
|
+
|
|
38
|
+
## [6.2.5] - 2025-10-31
|
|
39
|
+
|
|
40
|
+
### 🔧 Fixes
|
|
41
|
+
|
|
42
|
+
**Routing and Cost Estimation Bugs (Bugs #31, #32)**
|
|
43
|
+
|
|
44
|
+
Through heavy-thinking analysis and ax agent collaboration, identified and fixed 2 critical bugs in routing systems:
|
|
45
|
+
|
|
46
|
+
- **Bug #31 (MEDIUM)**: Division by zero in routing statistics (`src/core/routing-strategy.ts:317`)
|
|
47
|
+
- **Problem**: Empty scores array caused `0/0 = NaN` in avgScores calculation
|
|
48
|
+
- **Impact**: Corrupted routing statistics with NaN values
|
|
49
|
+
- **Fix**: Added guard to check `scores.length === 0` before division, return 0 for empty arrays
|
|
50
|
+
- **Pattern**: Same defensive approach as Bugs #16, #19
|
|
51
|
+
|
|
52
|
+
- **Bug #32 (HIGH)**: Unsafe metadata access crashes cost estimation (`src/core/router.ts:878-880`)
|
|
53
|
+
- **Problem**: Accessed `metadata.costPerToken.input/output` without validation
|
|
54
|
+
- **Impact**: TypeError crash if provider metadata missing `costPerToken` field
|
|
55
|
+
- **Fix**: Added comprehensive validation of metadata structure before access
|
|
56
|
+
- **Fallback**: Uses fallback pricing ($2.50/$10.00 per 1M tokens) if metadata invalid
|
|
57
|
+
- **Pattern**: Defensive programming to prevent production crashes
|
|
58
|
+
|
|
59
|
+
### 📊 Impact
|
|
60
|
+
|
|
61
|
+
- **Users affected**: All users using multi-factor routing and cost tracking
|
|
62
|
+
- **Breaking changes**: None
|
|
63
|
+
- **Migration**: None required - automatic graceful fallback for missing metadata
|
|
64
|
+
|
|
65
|
+
### ✅ Testing
|
|
66
|
+
|
|
67
|
+
- All 2,281 unit tests passing
|
|
68
|
+
- TypeScript compilation successful
|
|
69
|
+
- Build successful
|
|
70
|
+
|
|
5
71
|
## [6.0.7] - 2025-10-30
|
|
6
72
|
|
|
7
73
|
### 🔧 Fixes
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ AutomatosX is the only AI CLI that combines declarative workflow specs, policy-d
|
|
|
7
7
|
[](https://www.npmjs.com/package/@defai.digital/automatosx)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
|
-
[](#)
|
|
11
11
|
[](https://www.apple.com/macos)
|
|
12
12
|
[](https://www.microsoft.com/windows)
|
|
13
13
|
[](https://ubuntu.com)
|
package/dist/index.js
CHANGED
|
@@ -11797,6 +11797,30 @@ var ProviderMetricsTracker = class extends EventEmitter {
|
|
|
11797
11797
|
* Record a request for metrics tracking
|
|
11798
11798
|
*/
|
|
11799
11799
|
async recordRequest(provider, latencyMs, success, finishReason, tokenUsage, costUsd, model) {
|
|
11800
|
+
if (!provider || provider.trim().length === 0) {
|
|
11801
|
+
throw new Error("Provider name cannot be empty");
|
|
11802
|
+
}
|
|
11803
|
+
if (!Number.isFinite(latencyMs)) {
|
|
11804
|
+
throw new Error(`Invalid latencyMs value: ${latencyMs}. Must be a finite number.`);
|
|
11805
|
+
}
|
|
11806
|
+
if (latencyMs < 0) {
|
|
11807
|
+
throw new Error(`Invalid latencyMs value: ${latencyMs}. Cannot be negative.`);
|
|
11808
|
+
}
|
|
11809
|
+
if (!Number.isFinite(costUsd)) {
|
|
11810
|
+
throw new Error(`Invalid costUsd value: ${costUsd}. Must be a finite number.`);
|
|
11811
|
+
}
|
|
11812
|
+
if (costUsd < 0) {
|
|
11813
|
+
throw new Error(`Invalid costUsd value: ${costUsd}. Cannot be negative.`);
|
|
11814
|
+
}
|
|
11815
|
+
if (!Number.isFinite(tokenUsage.prompt) || tokenUsage.prompt < 0) {
|
|
11816
|
+
throw new Error(`Invalid prompt tokens: ${tokenUsage.prompt}. Must be a non-negative finite number.`);
|
|
11817
|
+
}
|
|
11818
|
+
if (!Number.isFinite(tokenUsage.completion) || tokenUsage.completion < 0) {
|
|
11819
|
+
throw new Error(`Invalid completion tokens: ${tokenUsage.completion}. Must be a non-negative finite number.`);
|
|
11820
|
+
}
|
|
11821
|
+
if (!Number.isFinite(tokenUsage.total) || tokenUsage.total < 0) {
|
|
11822
|
+
throw new Error(`Invalid total tokens: ${tokenUsage.total}. Must be a non-negative finite number.`);
|
|
11823
|
+
}
|
|
11800
11824
|
if (!this.metrics.has(provider)) {
|
|
11801
11825
|
this.metrics.set(provider, []);
|
|
11802
11826
|
}
|
|
@@ -12276,6 +12300,10 @@ var RoutingStrategyManager = class extends EventEmitter {
|
|
|
12276
12300
|
}
|
|
12277
12301
|
}
|
|
12278
12302
|
for (const [provider, scores] of Object.entries(scoresByProvider)) {
|
|
12303
|
+
if (scores.length === 0) {
|
|
12304
|
+
stats.avgScores[provider] = 0;
|
|
12305
|
+
continue;
|
|
12306
|
+
}
|
|
12279
12307
|
const avg = scores.reduce((sum, s) => sum + s, 0) / scores.length;
|
|
12280
12308
|
stats.avgScores[provider] = avg;
|
|
12281
12309
|
}
|
|
@@ -13081,49 +13109,91 @@ var CostTracker = class extends EventEmitter {
|
|
|
13081
13109
|
}
|
|
13082
13110
|
/**
|
|
13083
13111
|
* Initialize database
|
|
13112
|
+
* v6.2.4: Bug fix #24 - Wrap in try-catch to prevent memory leaks on error
|
|
13084
13113
|
*/
|
|
13085
13114
|
async initialize() {
|
|
13086
13115
|
if (this.initialized) {
|
|
13087
13116
|
return;
|
|
13088
13117
|
}
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13118
|
+
try {
|
|
13119
|
+
const dir = dirname(this.dbPath);
|
|
13120
|
+
if (!existsSync(dir)) {
|
|
13121
|
+
mkdirSync(dir, { recursive: true });
|
|
13122
|
+
}
|
|
13123
|
+
this.db = new Database3(this.dbPath);
|
|
13124
|
+
this.db.pragma("journal_mode = WAL");
|
|
13125
|
+
this.db.exec(`
|
|
13126
|
+
CREATE TABLE IF NOT EXISTS cost_entries (
|
|
13127
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
13128
|
+
timestamp INTEGER NOT NULL,
|
|
13129
|
+
provider TEXT NOT NULL,
|
|
13130
|
+
model TEXT NOT NULL,
|
|
13131
|
+
session_id TEXT,
|
|
13132
|
+
agent TEXT,
|
|
13133
|
+
prompt_tokens INTEGER NOT NULL,
|
|
13134
|
+
completion_tokens INTEGER NOT NULL,
|
|
13135
|
+
total_tokens INTEGER NOT NULL,
|
|
13136
|
+
estimated_cost_usd REAL NOT NULL,
|
|
13137
|
+
request_id TEXT
|
|
13138
|
+
);
|
|
13109
13139
|
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
|
|
13140
|
+
CREATE INDEX IF NOT EXISTS idx_cost_timestamp ON cost_entries(timestamp);
|
|
13141
|
+
CREATE INDEX IF NOT EXISTS idx_cost_provider ON cost_entries(provider);
|
|
13142
|
+
CREATE INDEX IF NOT EXISTS idx_cost_session ON cost_entries(session_id);
|
|
13143
|
+
CREATE INDEX IF NOT EXISTS idx_cost_agent ON cost_entries(agent);
|
|
13144
|
+
`);
|
|
13145
|
+
this.initialized = true;
|
|
13146
|
+
logger.info("CostTracker initialized", {
|
|
13147
|
+
dbPath: this.dbPath
|
|
13148
|
+
});
|
|
13149
|
+
} catch (error) {
|
|
13150
|
+
if (this.db) {
|
|
13151
|
+
try {
|
|
13152
|
+
this.db.close();
|
|
13153
|
+
} catch (closeError) {
|
|
13154
|
+
}
|
|
13155
|
+
this.db = null;
|
|
13156
|
+
}
|
|
13157
|
+
this.initialized = false;
|
|
13158
|
+
logger.error("Failed to initialize CostTracker", {
|
|
13159
|
+
error: error instanceof Error ? error.message : String(error),
|
|
13160
|
+
dbPath: this.dbPath
|
|
13161
|
+
});
|
|
13162
|
+
throw error;
|
|
13163
|
+
}
|
|
13119
13164
|
}
|
|
13120
13165
|
/**
|
|
13121
13166
|
* Record cost for a request
|
|
13167
|
+
* v6.2.4: Bug fix #25 - Added input validation
|
|
13122
13168
|
*/
|
|
13123
13169
|
async recordCost(entry) {
|
|
13124
13170
|
if (!this.initialized) {
|
|
13125
13171
|
await this.initialize();
|
|
13126
13172
|
}
|
|
13173
|
+
if (!entry.provider || entry.provider.trim().length === 0) {
|
|
13174
|
+
throw new Error("Provider name cannot be empty");
|
|
13175
|
+
}
|
|
13176
|
+
if (!entry.model || entry.model.trim().length === 0) {
|
|
13177
|
+
throw new Error("Model name cannot be empty");
|
|
13178
|
+
}
|
|
13179
|
+
if (!Number.isFinite(entry.estimatedCostUsd)) {
|
|
13180
|
+
throw new Error(`Invalid estimatedCostUsd value: ${entry.estimatedCostUsd}. Must be a finite number.`);
|
|
13181
|
+
}
|
|
13182
|
+
if (entry.estimatedCostUsd < 0) {
|
|
13183
|
+
throw new Error(`Invalid estimatedCostUsd value: ${entry.estimatedCostUsd}. Cannot be negative.`);
|
|
13184
|
+
}
|
|
13185
|
+
if (!Number.isFinite(entry.promptTokens) || entry.promptTokens < 0) {
|
|
13186
|
+
throw new Error(`Invalid promptTokens: ${entry.promptTokens}. Must be a non-negative finite number.`);
|
|
13187
|
+
}
|
|
13188
|
+
if (!Number.isFinite(entry.completionTokens) || entry.completionTokens < 0) {
|
|
13189
|
+
throw new Error(`Invalid completionTokens: ${entry.completionTokens}. Must be a non-negative finite number.`);
|
|
13190
|
+
}
|
|
13191
|
+
if (!Number.isFinite(entry.totalTokens) || entry.totalTokens < 0) {
|
|
13192
|
+
throw new Error(`Invalid totalTokens: ${entry.totalTokens}. Must be a non-negative finite number.`);
|
|
13193
|
+
}
|
|
13194
|
+
if (!Number.isFinite(entry.timestamp) || entry.timestamp <= 0) {
|
|
13195
|
+
throw new Error(`Invalid timestamp: ${entry.timestamp}. Must be a positive finite number.`);
|
|
13196
|
+
}
|
|
13127
13197
|
const stmt = this.db.prepare(`
|
|
13128
13198
|
INSERT INTO cost_entries (
|
|
13129
13199
|
timestamp, provider, model, session_id, agent,
|
|
@@ -14281,8 +14351,8 @@ var Router = class {
|
|
|
14281
14351
|
*/
|
|
14282
14352
|
estimateCost(providerName, tokensUsed) {
|
|
14283
14353
|
const metadata = PROVIDER_METADATA[providerName];
|
|
14284
|
-
if (!metadata) {
|
|
14285
|
-
logger.warn(`Provider metadata not found for
|
|
14354
|
+
if (!metadata || !metadata.costPerToken || typeof metadata.costPerToken.input !== "number" || typeof metadata.costPerToken.output !== "number") {
|
|
14355
|
+
logger.warn(`Provider metadata or cost data not found for: ${providerName}, using fallback costs`);
|
|
14286
14356
|
const inputCostPer1M = 2.5;
|
|
14287
14357
|
const outputCostPer1M = 10;
|
|
14288
14358
|
const inputCost2 = tokensUsed.prompt / 1e6 * inputCostPer1M;
|
|
@@ -14378,6 +14448,7 @@ var MemoryManager = class _MemoryManager {
|
|
|
14378
14448
|
* Initialize database and load FTS5 extension
|
|
14379
14449
|
*
|
|
14380
14450
|
* v4.11.0: Added FTS5 full-text search support
|
|
14451
|
+
* v6.2.4: Bug fix #30 - Wrap in try-catch to prevent memory leaks on error
|
|
14381
14452
|
*/
|
|
14382
14453
|
async initialize() {
|
|
14383
14454
|
if (this.initialized) return;
|
|
@@ -14475,6 +14546,15 @@ var MemoryManager = class _MemoryManager {
|
|
|
14475
14546
|
entryCount: this.entryCount
|
|
14476
14547
|
});
|
|
14477
14548
|
} catch (error) {
|
|
14549
|
+
if (this.db) {
|
|
14550
|
+
try {
|
|
14551
|
+
this.db.close();
|
|
14552
|
+
} catch (closeError) {
|
|
14553
|
+
}
|
|
14554
|
+
this.initialized = false;
|
|
14555
|
+
this.entryCount = 0;
|
|
14556
|
+
this.statements = {};
|
|
14557
|
+
}
|
|
14478
14558
|
logger.error("Failed to initialize MemoryManager", { error: error.message });
|
|
14479
14559
|
throw new MemoryError(
|
|
14480
14560
|
`Failed to initialize memory system: ${error.message}`,
|