Repository navigation
Release/v3.8.7 - #2909
Release/v3.8.7#2909
Conversation
Integrated into release/v3.8.6
Integrated into release/v3.8.6
Integrated into release/v3.8.7
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request introduces a client-facing self-service status endpoint (GET /api/v1/me/status) and corresponding dashboard controls to allow API keys to inspect their own USD usage, token totals, and shared upstream account quotas (with explicit opt-in). It hardens the existing budget endpoints with handler-level management auth, adds a database migration to backfill self-service scopes, and optimizes memory usage across the codebase by implementing strict caps, sweeps, and LRU eviction on various in-memory caches, registries, and statement caches. Review feedback highlighted a critical correctness bug in the UNION ALL analytics query where aggregated rows from daily_usage_summary would be incorrectly counted as single requests. Additionally, the reviewer recommended using ByteLengthQueuingStrategy to ensure the highWaterMark on stream transforms is byte-based rather than chunk-based, and suggested adding defensive optional chaining checks when resolving provider limits to prevent runtime TypeErrors.
|
|
||
| // Unified source CTE: columns aligned to usage_history shape needed by analytics queries. | ||
| // Fields not available in daily_usage_summary default to 0/NULL. | ||
| const unifiedSource = needsAggregated |
There was a problem hiding this comment.
There is a major correctness bug in the UNION ALL design of unifiedSource.
daily_usage_summary is an aggregated table where each row represents multiple requests (stored in total_requests). However, in unifiedSource, each row from daily_usage_summary is selected as a single row with hardcoded values like 1 as success.
When the analytics queries (which are not modified in this diff but are executed on unifiedSource) perform COUNT(*) or COUNT(1) to calculate the total number of requests, or use SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) to calculate successful requests, they will count each aggregated row from daily_usage_summary as exactly one request. This will result in a massive undercount of historical requests (e.g., a day with 10,000 requests will be counted as 1 request).
To fix this, the database queries should be refactored to aggregate both sources separately and then merge the results in JavaScript, or the schema/queries must use a request_count column (e.g., 1 AS request_count for usage_history and total_requests AS request_count for daily_usage_summary) and sum that column instead of using COUNT(*).
| { highWaterMark: 16384 }, | ||
| { highWaterMark: 16384 } |
There was a problem hiding this comment.
The high water mark of 16384 is specified here without a size function or a ByteLengthQueuingStrategy. In the Web Streams API, the default queuing strategy is count-based (where each chunk counts as 1). Therefore, highWaterMark: 16384 allows buffering up to 16,384 individual chunks (e.g., Uint8Array objects) rather than 16,384 bytes.
If the intention was to limit the buffer to 16 KB (as indicated in the unit tests), you should use ByteLengthQueuingStrategy to ensure the limit is byte-based instead of chunk-based. Otherwise, buffering up to 16,384 chunks per stream could lead to significant memory overhead under high concurrency or slow client connections.
| { highWaterMark: 16384 }, | |
| { highWaterMark: 16384 } | |
| new ByteLengthQueuingStrategy({ highWaterMark: 16384 }), | |
| new ByteLengthQueuingStrategy({ highWaterMark: 16384 }) |
| const result = await deps.fetchAndPersistProviderLimits(allowedConnections[0], "manual"); | ||
| const usage = result.usage as JsonRecord; | ||
| const quotas = | ||
| usage.quotas && typeof usage.quotas === "object" && !Array.isArray(usage.quotas) | ||
| ? (usage.quotas as JsonRecord) | ||
| : null; |
There was a problem hiding this comment.
Add defensive checks on result and result.usage before accessing usage.quotas. If fetchAndPersistProviderLimits returns an unexpected structure or undefined, accessing result.usage or usage.quotas will throw a TypeError.
Using optional chaining (result?.usage and usage?.quotas) prevents potential runtime exceptions and handles unexpected API structures gracefully.
| const result = await deps.fetchAndPersistProviderLimits(allowedConnections[0], "manual"); | |
| const usage = result.usage as JsonRecord; | |
| const quotas = | |
| usage.quotas && typeof usage.quotas === "object" && !Array.isArray(usage.quotas) | |
| ? (usage.quotas as JsonRecord) | |
| : null; | |
| const result = await deps.fetchAndPersistProviderLimits(allowedConnections[0], "manual"); | |
| const usage = result?.usage as JsonRecord | undefined; | |
| const quotas = | |
| usage?.quotas && typeof usage.quotas === "object" && !Array.isArray(usage.quotas) | |
| ? (usage.quotas as JsonRecord) | |
| : null; |
CI Coverage Report
Coverage artifact was not available for this run. PR Test PolicyThis PR changes production code in |
Update the main and localized changelogs with the 3.8.7 release notes, including new API self-service status, analytics retention, RAM optimizations, and token accounting fixes. Clarify release skill instructions to ensure new release branches are always created from the latest main branch.
- llm.txt → 3.8.7 (Current version + Key Features header) - CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors - version already 3.8.7 across package.json/open-sse/electron/openapi (from #2909)
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* fix(settings): add missing home page pin keys to updateSettingsSchema
* feat(plugins): add i18n keys to all 42 locales
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(usage): analytics route reads combo_name/requested_model from call_logs only
The 3.8.6 variant of #2904 added SELECTs of combo_name/requested_model
against usage_history, but those columns only exist in call_logs (no
migration adds them to usage_history). This returned HTTP 500 on
/api/usage/analytics. Restore the working query shape from the 3.8.7
variant. Fixes 18 failing usage-analytics-route tests.
* fix(types,test): resolve noImplicitAny in progressiveAging + align semaphore test to #2903 gate pruning
- progressiveAging: type compression results so messages[0].content is
indexable (was TS7053 against {}); restores typecheck:noimplicit:core gate.
- services-branch-hardening: #2903 (perf-ram) prunes idle rate-limit gates
on zero; assert no-running/empty-queue without assuming the entry persists.
* fix(analytics): address merged review regressions
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* chore(release): sync v3.8.7 touchpoints + credit contributors
- llm.txt → 3.8.7 (Current version + Key Features header)
- CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors
- version already 3.8.7 across package.json/open-sse/electron/openapi (from #2909)
* fix(cleanup): restore usage history cutoff boundary
* docs(changelog): rank 3.8.6 contributors in a commits table with their PRs
* fix(dashboard): theme ReactFlow Controls +/- buttons for dark mode
* fix(settings): add missing home page pin keys to updateSettingsSchema
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* fix(analytics): address merged review regressions
* fix(cleanup): restore usage history cutoff boundary
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* feat(plugins): add i18n keys to all 42 locales
* chore(plugins): remove duplicate migration 059_create_plugins.sql
* chore(plugins): remove duplicate migration 059_create_plugins.sql (post-merge)
* fix(sse): guard non-string error.code in proxyFetch + harden model parsing (#2463) (#2923)
Integrated into release/v3.8.7
* fix(docker): add runner-web stage with Playwright Chromium (#2832) (#2846)
Integrated into release/v3.8.7
* docs(changelog): document NVIDIA NIM and error code type-crash fix (#2463)
* test: ignore NVIDIA_BASE_URL and NVIDIA_MODEL in env contract check
---------
Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com>
Co-authored-by: Halil Tezcan KARABULUT <info@hlltzcnkb.com>
Co-authored-by: R.D. <rogerproself@gmail.com>
Hotfixes da release/v3.8.7 (perf RAM cheaper-inference#2903, self-service cheaper-inference#2908, analytics cheaper-inference#2904, bump 3.8.7, docs) na main consolidada com 3.8.6.
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* fix(settings): add missing home page pin keys to updateSettingsSchema
* feat(plugins): add i18n keys to all 42 locales
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(usage): analytics route reads combo_name/requested_model from call_logs only
The 3.8.6 variant of cheaper-inference#2904 added SELECTs of combo_name/requested_model
against usage_history, but those columns only exist in call_logs (no
migration adds them to usage_history). This returned HTTP 500 on
/api/usage/analytics. Restore the working query shape from the 3.8.7
variant. Fixes 18 failing usage-analytics-route tests.
* fix(types,test): resolve noImplicitAny in progressiveAging + align semaphore test to cheaper-inference#2903 gate pruning
- progressiveAging: type compression results so messages[0].content is
indexable (was TS7053 against {}); restores typecheck:noimplicit:core gate.
- services-branch-hardening: cheaper-inference#2903 (perf-ram) prunes idle rate-limit gates
on zero; assert no-running/empty-queue without assuming the entry persists.
* fix(analytics): address merged review regressions
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* chore(release): sync v3.8.7 touchpoints + credit contributors
- llm.txt → 3.8.7 (Current version + Key Features header)
- CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors
- version already 3.8.7 across package.json/open-sse/electron/openapi (from cheaper-inference#2909)
* fix(cleanup): restore usage history cutoff boundary
* docs(changelog): rank 3.8.6 contributors in a commits table with their PRs
* fix(dashboard): theme ReactFlow Controls +/- buttons for dark mode
* fix(settings): add missing home page pin keys to updateSettingsSchema
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* fix(analytics): address merged review regressions
* fix(cleanup): restore usage history cutoff boundary
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* feat(plugins): add i18n keys to all 42 locales
* chore(plugins): remove duplicate migration 059_create_plugins.sql
* chore(plugins): remove duplicate migration 059_create_plugins.sql (post-merge)
* fix(sse): guard non-string error.code in proxyFetch + harden model parsing (cheaper-inference#2463) (cheaper-inference#2923)
Integrated into release/v3.8.7
* fix(docker): add runner-web stage with Playwright Chromium (cheaper-inference#2832) (cheaper-inference#2846)
Integrated into release/v3.8.7
* docs(changelog): document NVIDIA NIM and error code type-crash fix (cheaper-inference#2463)
* test: ignore NVIDIA_BASE_URL and NVIDIA_MODEL in env contract check
---------
Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com>
Co-authored-by: Halil Tezcan KARABULUT <info@hlltzcnkb.com>
Co-authored-by: R.D. <rogerproself@gmail.com>
- llm.txt → 3.8.7 (Current version + Key Features header) - CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors - version already 3.8.7 across package.json/open-sse/electron/openapi (from cheaper-inference#2909)
Hotfixes da release/v3.8.7 (perf RAM cheaper-inference#2903, self-service cheaper-inference#2908, analytics cheaper-inference#2904, bump 3.8.7, docs) na main consolidada com 3.8.6.
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* fix(settings): add missing home page pin keys to updateSettingsSchema
* feat(plugins): add i18n keys to all 42 locales
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(usage): analytics route reads combo_name/requested_model from call_logs only
The 3.8.6 variant of cheaper-inference#2904 added SELECTs of combo_name/requested_model
against usage_history, but those columns only exist in call_logs (no
migration adds them to usage_history). This returned HTTP 500 on
/api/usage/analytics. Restore the working query shape from the 3.8.7
variant. Fixes 18 failing usage-analytics-route tests.
* fix(types,test): resolve noImplicitAny in progressiveAging + align semaphore test to cheaper-inference#2903 gate pruning
- progressiveAging: type compression results so messages[0].content is
indexable (was TS7053 against {}); restores typecheck:noimplicit:core gate.
- services-branch-hardening: cheaper-inference#2903 (perf-ram) prunes idle rate-limit gates
on zero; assert no-running/empty-queue without assuming the entry persists.
* fix(analytics): address merged review regressions
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* chore(release): sync v3.8.7 touchpoints + credit contributors
- llm.txt → 3.8.7 (Current version + Key Features header)
- CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors
- version already 3.8.7 across package.json/open-sse/electron/openapi (from cheaper-inference#2909)
* fix(cleanup): restore usage history cutoff boundary
* docs(changelog): rank 3.8.6 contributors in a commits table with their PRs
* fix(dashboard): theme ReactFlow Controls +/- buttons for dark mode
* fix(settings): add missing home page pin keys to updateSettingsSchema
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* fix(analytics): address merged review regressions
* fix(cleanup): restore usage history cutoff boundary
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* feat(plugins): add i18n keys to all 42 locales
* chore(plugins): remove duplicate migration 059_create_plugins.sql
* chore(plugins): remove duplicate migration 059_create_plugins.sql (post-merge)
* fix(sse): guard non-string error.code in proxyFetch + harden model parsing (cheaper-inference#2463) (cheaper-inference#2923)
Integrated into release/v3.8.7
* fix(docker): add runner-web stage with Playwright Chromium (cheaper-inference#2832) (cheaper-inference#2846)
Integrated into release/v3.8.7
* docs(changelog): document NVIDIA NIM and error code type-crash fix (cheaper-inference#2463)
* test: ignore NVIDIA_BASE_URL and NVIDIA_MODEL in env contract check
---------
Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com>
Co-authored-by: Halil Tezcan KARABULUT <info@hlltzcnkb.com>
Co-authored-by: R.D. <rogerproself@gmail.com>
- llm.txt → 3.8.7 (Current version + Key Features header) - CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors - version already 3.8.7 across package.json/open-sse/electron/openapi (from cheaper-inference#2909)
Hotfixes da release/v3.8.7 (perf RAM cheaper-inference#2903, self-service cheaper-inference#2908, analytics cheaper-inference#2904, bump 3.8.7, docs) na main consolidada com 3.8.6.
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* fix(settings): add missing home page pin keys to updateSettingsSchema
* feat(plugins): add i18n keys to all 42 locales
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(usage): analytics route reads combo_name/requested_model from call_logs only
The 3.8.6 variant of cheaper-inference#2904 added SELECTs of combo_name/requested_model
against usage_history, but those columns only exist in call_logs (no
migration adds them to usage_history). This returned HTTP 500 on
/api/usage/analytics. Restore the working query shape from the 3.8.7
variant. Fixes 18 failing usage-analytics-route tests.
* fix(types,test): resolve noImplicitAny in progressiveAging + align semaphore test to cheaper-inference#2903 gate pruning
- progressiveAging: type compression results so messages[0].content is
indexable (was TS7053 against {}); restores typecheck:noimplicit:core gate.
- services-branch-hardening: cheaper-inference#2903 (perf-ram) prunes idle rate-limit gates
on zero; assert no-running/empty-queue without assuming the entry persists.
* fix(analytics): address merged review regressions
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* chore(release): sync v3.8.7 touchpoints + credit contributors
- llm.txt → 3.8.7 (Current version + Key Features header)
- CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors
- version already 3.8.7 across package.json/open-sse/electron/openapi (from cheaper-inference#2909)
* fix(cleanup): restore usage history cutoff boundary
* docs(changelog): rank 3.8.6 contributors in a commits table with their PRs
* fix(dashboard): theme ReactFlow Controls +/- buttons for dark mode
* fix(settings): add missing home page pin keys to updateSettingsSchema
* fix(settings): add missing security keys to updateSettingsSchema and add tests
* fix(executor): normalize max effort for openai shape providers
* Make zero-latency combo optimizations opt-in
* Address zero-latency combo review feedback
* fix(analytics): address merged review regressions
* fix(cleanup): restore usage history cutoff boundary
* feat(plugins): WordPress-style plugin system backend
* fix(plugins): address code review feedback
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): replace vm with child_process, add auth to all routes
Addresses all remaining code review feedback:
1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
for proper process-level isolation. Complies with Rule 3 (no eval).
Each plugin runs in a separate Node.js process with IPC communication.
2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
API route files (list, install, scan, details, activate, deactivate, config).
3. **Env filtering**: Only safe env vars passed to plugin processes unless
"env" permission is granted.
Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
* fix(plugins): security + ESM fixes for loader and manager
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)
manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)
hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
* feat(discovery): add discovery tool stub service
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
* chore(plugins): slop cleanup — pino logger, remove redundant sorts
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
* test(plugins): add scanner, loader, manager unit tests
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
* feat(plugins): add i18n keys to all 42 locales
* chore(plugins): remove duplicate migration 059_create_plugins.sql
* chore(plugins): remove duplicate migration 059_create_plugins.sql (post-merge)
* fix(sse): guard non-string error.code in proxyFetch + harden model parsing (cheaper-inference#2463) (cheaper-inference#2923)
Integrated into release/v3.8.7
* fix(docker): add runner-web stage with Playwright Chromium (cheaper-inference#2832) (cheaper-inference#2846)
Integrated into release/v3.8.7
* docs(changelog): document NVIDIA NIM and error code type-crash fix (cheaper-inference#2463)
* test: ignore NVIDIA_BASE_URL and NVIDIA_MODEL in env contract check
---------
Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com>
Co-authored-by: Halil Tezcan KARABULUT <info@hlltzcnkb.com>
Co-authored-by: R.D. <rogerproself@gmail.com>
- llm.txt → 3.8.7 (Current version + Key Features header) - CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors - version already 3.8.7 across package.json/open-sse/electron/openapi (from cheaper-inference#2909)
Summary
Related Issues
Validation
npm run lintnpm run test:unitnpm run test:coverage>= 60%for statements, lines, functions, and branchesTests Added Or Updated
Coverage Notes
src/,open-sse/,electron/, orbin/, explain which tests cover the change.Reviewer Notes