@defai.digital/automatosx 6.2.1 → 6.2.5
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 +33 -0
- package/README.md +1 -1
- package/dist/index.js +206 -31
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
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.5] - 2025-10-31
|
|
6
|
+
|
|
7
|
+
### 🔧 Fixes
|
|
8
|
+
|
|
9
|
+
**Routing and Cost Estimation Bugs (Bugs #31, #32)**
|
|
10
|
+
|
|
11
|
+
Through heavy-thinking analysis and ax agent collaboration, identified and fixed 2 critical bugs in routing systems:
|
|
12
|
+
|
|
13
|
+
- **Bug #31 (MEDIUM)**: Division by zero in routing statistics (`src/core/routing-strategy.ts:317`)
|
|
14
|
+
- **Problem**: Empty scores array caused `0/0 = NaN` in avgScores calculation
|
|
15
|
+
- **Impact**: Corrupted routing statistics with NaN values
|
|
16
|
+
- **Fix**: Added guard to check `scores.length === 0` before division, return 0 for empty arrays
|
|
17
|
+
- **Pattern**: Same defensive approach as Bugs #16, #19
|
|
18
|
+
|
|
19
|
+
- **Bug #32 (HIGH)**: Unsafe metadata access crashes cost estimation (`src/core/router.ts:878-880`)
|
|
20
|
+
- **Problem**: Accessed `metadata.costPerToken.input/output` without validation
|
|
21
|
+
- **Impact**: TypeError crash if provider metadata missing `costPerToken` field
|
|
22
|
+
- **Fix**: Added comprehensive validation of metadata structure before access
|
|
23
|
+
- **Fallback**: Uses fallback pricing ($2.50/$10.00 per 1M tokens) if metadata invalid
|
|
24
|
+
- **Pattern**: Defensive programming to prevent production crashes
|
|
25
|
+
|
|
26
|
+
### 📊 Impact
|
|
27
|
+
|
|
28
|
+
- **Users affected**: All users using multi-factor routing and cost tracking
|
|
29
|
+
- **Breaking changes**: None
|
|
30
|
+
- **Migration**: None required - automatic graceful fallback for missing metadata
|
|
31
|
+
|
|
32
|
+
### ✅ Testing
|
|
33
|
+
|
|
34
|
+
- All 2,281 unit tests passing
|
|
35
|
+
- TypeScript compilation successful
|
|
36
|
+
- Build successful
|
|
37
|
+
|
|
5
38
|
## [6.0.7] - 2025-10-30
|
|
6
39
|
|
|
7
40
|
### 🔧 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
|
@@ -6364,12 +6364,16 @@ var init_openai_sdk_provider = __esm({
|
|
|
6364
6364
|
init_provider_connection_pool();
|
|
6365
6365
|
init_provider_limit_manager();
|
|
6366
6366
|
init_streaming_feedback();
|
|
6367
|
+
init_timeout_estimator();
|
|
6367
6368
|
OpenAISDKProvider = class extends BaseProvider {
|
|
6368
6369
|
sdkConfig;
|
|
6369
6370
|
connectionPool = getProviderConnectionPool();
|
|
6370
6371
|
initialized = false;
|
|
6371
6372
|
// v6.0.7: Streaming feedback
|
|
6372
6373
|
currentStreamingFeedback = null;
|
|
6374
|
+
// v6.2.2: Progress tracking for SDK streaming (bugfix #5, #6)
|
|
6375
|
+
currentProgressTracker = null;
|
|
6376
|
+
progressInterval = null;
|
|
6373
6377
|
constructor(config, sdkConfig = {}) {
|
|
6374
6378
|
super(config);
|
|
6375
6379
|
this.sdkConfig = sdkConfig;
|
|
@@ -6437,10 +6441,25 @@ var init_openai_sdk_provider = __esm({
|
|
|
6437
6441
|
*/
|
|
6438
6442
|
async executeRequest(request) {
|
|
6439
6443
|
const startTime = Date.now();
|
|
6444
|
+
const fullPrompt = `${request.systemPrompt || ""}
|
|
6445
|
+
${request.prompt}`.trim();
|
|
6446
|
+
const timeoutEstimate = estimateTimeout({
|
|
6447
|
+
prompt: fullPrompt,
|
|
6448
|
+
systemPrompt: request.systemPrompt,
|
|
6449
|
+
model: typeof request.model === "string" ? request.model : void 0,
|
|
6450
|
+
maxTokens: request.maxTokens
|
|
6451
|
+
});
|
|
6452
|
+
if (process.env.AUTOMATOSX_QUIET !== "true") {
|
|
6453
|
+
logger.info(formatTimeoutEstimate(timeoutEstimate));
|
|
6454
|
+
}
|
|
6455
|
+
if (timeoutEstimate.estimatedDurationMs > 1e4) {
|
|
6456
|
+
this.startProgressTracking(timeoutEstimate.estimatedDurationMs);
|
|
6457
|
+
}
|
|
6440
6458
|
const useMock = process.env.AUTOMATOSX_MOCK_PROVIDERS === "true" || process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
6441
6459
|
if (useMock) {
|
|
6442
6460
|
const mockPrompt = request.prompt.substring(0, 100);
|
|
6443
6461
|
const latency = Date.now() - startTime;
|
|
6462
|
+
this.stopProgressTracking();
|
|
6444
6463
|
return {
|
|
6445
6464
|
content: `[Mock Response from OpenAI SDK]
|
|
6446
6465
|
|
|
@@ -6477,6 +6496,7 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
|
|
|
6477
6496
|
// Non-streaming for this method
|
|
6478
6497
|
});
|
|
6479
6498
|
await this.connectionPool.release(this.config.name, connection);
|
|
6499
|
+
this.stopProgressTracking();
|
|
6480
6500
|
const content = response.choices[0]?.message?.content || "";
|
|
6481
6501
|
const finishReason = this.mapFinishReason(response.choices[0]?.finish_reason);
|
|
6482
6502
|
const latency = Date.now() - startTime;
|
|
@@ -6500,9 +6520,11 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
|
|
|
6500
6520
|
};
|
|
6501
6521
|
} catch (error) {
|
|
6502
6522
|
await this.connectionPool.release(this.config.name, connection);
|
|
6523
|
+
this.stopProgressTracking();
|
|
6503
6524
|
throw error;
|
|
6504
6525
|
}
|
|
6505
6526
|
} catch (error) {
|
|
6527
|
+
this.stopProgressTracking();
|
|
6506
6528
|
return this.handleSDKError(error, request, startTime);
|
|
6507
6529
|
}
|
|
6508
6530
|
}
|
|
@@ -6511,6 +6533,20 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
|
|
|
6511
6533
|
*/
|
|
6512
6534
|
async executeStreaming(request, options) {
|
|
6513
6535
|
const startTime = Date.now();
|
|
6536
|
+
const fullPrompt = `${request.systemPrompt || ""}
|
|
6537
|
+
${request.prompt}`.trim();
|
|
6538
|
+
const timeoutEstimate = estimateTimeout({
|
|
6539
|
+
prompt: fullPrompt,
|
|
6540
|
+
systemPrompt: request.systemPrompt,
|
|
6541
|
+
model: typeof request.model === "string" ? request.model : void 0,
|
|
6542
|
+
maxTokens: request.maxTokens
|
|
6543
|
+
});
|
|
6544
|
+
if (process.env.AUTOMATOSX_QUIET !== "true") {
|
|
6545
|
+
logger.info(formatTimeoutEstimate(timeoutEstimate));
|
|
6546
|
+
}
|
|
6547
|
+
if (timeoutEstimate.estimatedDurationMs > 1e4) {
|
|
6548
|
+
this.startProgressTracking(timeoutEstimate.estimatedDurationMs);
|
|
6549
|
+
}
|
|
6514
6550
|
const estimatedOutputTokens = request.maxTokens || this.estimateTokens(request.prompt) * 2;
|
|
6515
6551
|
this.currentStreamingFeedback = createStreamingFeedback(estimatedOutputTokens);
|
|
6516
6552
|
const useMock = process.env.AUTOMATOSX_MOCK_PROVIDERS === "true" || process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
@@ -6531,6 +6567,7 @@ This is a placeholder streaming response. Set AUTOMATOSX_MOCK_PROVIDERS=false to
|
|
|
6531
6567
|
this.currentStreamingFeedback.stop(50);
|
|
6532
6568
|
this.currentStreamingFeedback = null;
|
|
6533
6569
|
}
|
|
6570
|
+
this.stopProgressTracking();
|
|
6534
6571
|
return {
|
|
6535
6572
|
content: mockContent,
|
|
6536
6573
|
model: request.model || "gpt-4o",
|
|
@@ -6586,6 +6623,19 @@ This is a placeholder streaming response. Set AUTOMATOSX_MOCK_PROVIDERS=false to
|
|
|
6586
6623
|
});
|
|
6587
6624
|
}
|
|
6588
6625
|
}
|
|
6626
|
+
if (options.onProgress) {
|
|
6627
|
+
try {
|
|
6628
|
+
const currentTokens = this.estimateTokens(fullContent);
|
|
6629
|
+
const expectedTokens = request.maxTokens || 4096;
|
|
6630
|
+
const progress = Math.min(currentTokens / expectedTokens, 0.95);
|
|
6631
|
+
options.onProgress(progress);
|
|
6632
|
+
} catch (error) {
|
|
6633
|
+
logger.warn("Streaming onProgress callback error", {
|
|
6634
|
+
provider: this.config.name,
|
|
6635
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6636
|
+
});
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6589
6639
|
}
|
|
6590
6640
|
if (chunk.choices[0]?.finish_reason) {
|
|
6591
6641
|
finishReason = this.mapFinishReason(chunk.choices[0].finish_reason);
|
|
@@ -6616,6 +6666,17 @@ This is a placeholder streaming response. Set AUTOMATOSX_MOCK_PROVIDERS=false to
|
|
|
6616
6666
|
this.currentStreamingFeedback.stop(completionTokens);
|
|
6617
6667
|
this.currentStreamingFeedback = null;
|
|
6618
6668
|
}
|
|
6669
|
+
if (options.onProgress) {
|
|
6670
|
+
try {
|
|
6671
|
+
options.onProgress(1);
|
|
6672
|
+
} catch (error) {
|
|
6673
|
+
logger.warn("Final streaming onProgress callback error", {
|
|
6674
|
+
provider: this.config.name,
|
|
6675
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6676
|
+
});
|
|
6677
|
+
}
|
|
6678
|
+
}
|
|
6679
|
+
this.stopProgressTracking();
|
|
6619
6680
|
return {
|
|
6620
6681
|
content: fullContent,
|
|
6621
6682
|
model: modelUsed,
|
|
@@ -6633,6 +6694,7 @@ This is a placeholder streaming response. Set AUTOMATOSX_MOCK_PROVIDERS=false to
|
|
|
6633
6694
|
this.currentStreamingFeedback.stop();
|
|
6634
6695
|
this.currentStreamingFeedback = null;
|
|
6635
6696
|
}
|
|
6697
|
+
this.stopProgressTracking();
|
|
6636
6698
|
throw error;
|
|
6637
6699
|
}
|
|
6638
6700
|
} catch (error) {
|
|
@@ -6640,6 +6702,7 @@ This is a placeholder streaming response. Set AUTOMATOSX_MOCK_PROVIDERS=false to
|
|
|
6640
6702
|
this.currentStreamingFeedback.stop();
|
|
6641
6703
|
this.currentStreamingFeedback = null;
|
|
6642
6704
|
}
|
|
6705
|
+
this.stopProgressTracking();
|
|
6643
6706
|
return this.handleSDKError(error, request, startTime);
|
|
6644
6707
|
}
|
|
6645
6708
|
}
|
|
@@ -6808,6 +6871,38 @@ This is a placeholder streaming response. Set AUTOMATOSX_MOCK_PROVIDERS=false to
|
|
|
6808
6871
|
tokensUsed: inputTokens + outputTokens
|
|
6809
6872
|
};
|
|
6810
6873
|
}
|
|
6874
|
+
/**
|
|
6875
|
+
* Start progress tracking for long operations
|
|
6876
|
+
* v6.2.2: Consistency with CLI providers (bugfix #5, #6)
|
|
6877
|
+
* @param estimatedDurationMs - Estimated duration in milliseconds
|
|
6878
|
+
*/
|
|
6879
|
+
startProgressTracking(estimatedDurationMs) {
|
|
6880
|
+
if (process.env.AUTOMATOSX_QUIET === "true") {
|
|
6881
|
+
return;
|
|
6882
|
+
}
|
|
6883
|
+
this.currentProgressTracker = new ProgressTracker(estimatedDurationMs);
|
|
6884
|
+
this.progressInterval = setInterval(() => {
|
|
6885
|
+
if (this.currentProgressTracker && this.currentProgressTracker.shouldUpdate()) {
|
|
6886
|
+
process.stderr.write("\r" + this.currentProgressTracker.formatProgress());
|
|
6887
|
+
}
|
|
6888
|
+
}, 1e3);
|
|
6889
|
+
}
|
|
6890
|
+
/**
|
|
6891
|
+
* Stop progress tracking
|
|
6892
|
+
* v6.2.2: Consistency with CLI providers (bugfix #5, #6)
|
|
6893
|
+
*/
|
|
6894
|
+
stopProgressTracking() {
|
|
6895
|
+
if (this.progressInterval) {
|
|
6896
|
+
clearInterval(this.progressInterval);
|
|
6897
|
+
this.progressInterval = null;
|
|
6898
|
+
}
|
|
6899
|
+
if (this.currentProgressTracker) {
|
|
6900
|
+
if (process.env.AUTOMATOSX_QUIET !== "true") {
|
|
6901
|
+
process.stderr.write("\r" + " ".repeat(80) + "\r");
|
|
6902
|
+
}
|
|
6903
|
+
this.currentProgressTracker = null;
|
|
6904
|
+
}
|
|
6905
|
+
}
|
|
6811
6906
|
};
|
|
6812
6907
|
}
|
|
6813
6908
|
});
|
|
@@ -11702,6 +11797,30 @@ var ProviderMetricsTracker = class extends EventEmitter {
|
|
|
11702
11797
|
* Record a request for metrics tracking
|
|
11703
11798
|
*/
|
|
11704
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
|
+
}
|
|
11705
11824
|
if (!this.metrics.has(provider)) {
|
|
11706
11825
|
this.metrics.set(provider, []);
|
|
11707
11826
|
}
|
|
@@ -12181,6 +12300,10 @@ var RoutingStrategyManager = class extends EventEmitter {
|
|
|
12181
12300
|
}
|
|
12182
12301
|
}
|
|
12183
12302
|
for (const [provider, scores] of Object.entries(scoresByProvider)) {
|
|
12303
|
+
if (scores.length === 0) {
|
|
12304
|
+
stats.avgScores[provider] = 0;
|
|
12305
|
+
continue;
|
|
12306
|
+
}
|
|
12184
12307
|
const avg = scores.reduce((sum, s) => sum + s, 0) / scores.length;
|
|
12185
12308
|
stats.avgScores[provider] = avg;
|
|
12186
12309
|
}
|
|
@@ -12986,49 +13109,91 @@ var CostTracker = class extends EventEmitter {
|
|
|
12986
13109
|
}
|
|
12987
13110
|
/**
|
|
12988
13111
|
* Initialize database
|
|
13112
|
+
* v6.2.4: Bug fix #24 - Wrap in try-catch to prevent memory leaks on error
|
|
12989
13113
|
*/
|
|
12990
13114
|
async initialize() {
|
|
12991
13115
|
if (this.initialized) {
|
|
12992
13116
|
return;
|
|
12993
13117
|
}
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
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
|
+
);
|
|
13014
13139
|
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
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
|
+
}
|
|
13024
13164
|
}
|
|
13025
13165
|
/**
|
|
13026
13166
|
* Record cost for a request
|
|
13167
|
+
* v6.2.4: Bug fix #25 - Added input validation
|
|
13027
13168
|
*/
|
|
13028
13169
|
async recordCost(entry) {
|
|
13029
13170
|
if (!this.initialized) {
|
|
13030
13171
|
await this.initialize();
|
|
13031
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
|
+
}
|
|
13032
13197
|
const stmt = this.db.prepare(`
|
|
13033
13198
|
INSERT INTO cost_entries (
|
|
13034
13199
|
timestamp, provider, model, session_id, agent,
|
|
@@ -14186,8 +14351,8 @@ var Router = class {
|
|
|
14186
14351
|
*/
|
|
14187
14352
|
estimateCost(providerName, tokensUsed) {
|
|
14188
14353
|
const metadata = PROVIDER_METADATA[providerName];
|
|
14189
|
-
if (!metadata) {
|
|
14190
|
-
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`);
|
|
14191
14356
|
const inputCostPer1M = 2.5;
|
|
14192
14357
|
const outputCostPer1M = 10;
|
|
14193
14358
|
const inputCost2 = tokensUsed.prompt / 1e6 * inputCostPer1M;
|
|
@@ -14283,6 +14448,7 @@ var MemoryManager = class _MemoryManager {
|
|
|
14283
14448
|
* Initialize database and load FTS5 extension
|
|
14284
14449
|
*
|
|
14285
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
|
|
14286
14452
|
*/
|
|
14287
14453
|
async initialize() {
|
|
14288
14454
|
if (this.initialized) return;
|
|
@@ -14380,6 +14546,15 @@ var MemoryManager = class _MemoryManager {
|
|
|
14380
14546
|
entryCount: this.entryCount
|
|
14381
14547
|
});
|
|
14382
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
|
+
}
|
|
14383
14558
|
logger.error("Failed to initialize MemoryManager", { error: error.message });
|
|
14384
14559
|
throw new MemoryError(
|
|
14385
14560
|
`Failed to initialize memory system: ${error.message}`,
|