Add inline comment explaining that compileSafeRegex rejects patterns
with nested repetition (ReDoS risk) and returns null. Rejected patterns
are silently skipped; the plugin will not match via that pattern but
other patterns and prefixes still apply.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Replace raw `new RegExp(patternSource, "u")` in
`resolveModelSupportMatchKind` with the existing
`compileSafeRegex()` guard from `src/security/safe-regex.ts`.
A malicious or careless plugin manifest pattern like `(a+)+$`
causes catastrophic backtracking (ReDoS) against non-matching model
IDs. `compileSafeRegex` detects nested repetition and returns null,
which the caller now treats as a non-match (equivalent to the
previous catch-continue for invalid regex).
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Normalize Google Gemini 3.1 Flash Lite routing to the GA model id and keep the retired preview spelling as a compatibility alias. Align default alias docs, FAQ guidance, and deprecated-model manifest recommendations with the GA id.
Fixes#86151.
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Summary:
- The PR routes local GGUF memory embeddings through a bundled worker sidecar, adds structured degradation and fallback handling, updates memory tests/build output, and keeps the local config contract unchanged.
- PR surface: Source +831, Tests +503, Docs +1, Other +2. Total +1337 across 23 files.
- Reproducibility: Do we have a high-confidence way to reproduce the issue? Source and report evidence are str ... cludes native crash logs; the exact Metal teardown abort was not reproduced in this review or the PR proof.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(memory): keep local embedding config unchanged
- PR branch already contained follow-up commit before automerge: fix(memory): type local embedding degradation
- PR branch already contained follow-up commit before automerge: fix(memory): refresh keywords after embedding fallback
- PR branch already contained follow-up commit before automerge: fix(memory): keep worker errors internal
- PR branch already contained follow-up commit before automerge: test: satisfy memory provider lifecycle harnesses
- PR branch already contained follow-up commit before automerge: fix: harden local embedding worker fallback
Validation:
- ClawSweeper review passed for head 1d1fe41c4e.
- Required merge gates passed before the squash merge.
Prepared head SHA: 1d1fe41c4e
Review: https://github.com/openclaw/openclaw/pull/85348#issuecomment-4518516047
Co-authored-by: Onur Solmaz <onur@Onurs-MacBook-Pro.local>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
* perf(plugins): thread metadata snapshot and discovery through hot paths
With the snapshot memo now actually hitting, route the snapshot's
manifestRegistry and discovery through the helper chains that already
had fast paths for them. Eliminates redundant per-call rebuilds at
two big amplifiers.
- Provider resolve paths (resolvePluginProviders /
isPluginProvidersLoadInFlight / resolveOwningPluginIdsForProvider /
resolveExternalAuthProfilesWithPlugins) self-service a snapshot once
at the public entry, then thread it as a separate required arg
through resolvePluginProviderLoadBase,
resolveExplicitProviderOwnerPluginIds, and the setup/runtime load
state helpers. Inner reads change from
'params.pluginMetadataSnapshot?.x' to 'snapshot.x', no more
enrichedParams clone. loadPluginManifestRegistryForInstalledIndex
fires drop ~685 -> ~10 per cold start.
- Bundled-channel / auto-enable chain accepts an optional
PluginDiscoveryResult. discoverOpenClawPlugins is fired once during
snapshot building (resolveInstalledPluginIndexRegistry already
produced it internally; now bubbled up through
loadInstalledPluginIndexWithDiscovery, PluginRegistrySnapshotResult,
and onto PluginMetadataSnapshot.discovery). load-context reads
metadataSnapshot.discovery and passes it through
applyPluginAutoEnable, so the bundled-channel cascade
(collectConfiguredChannelIds, listBundledChannelIdsWith*,
listPotentialConfiguredChannelPresenceSignals) short-circuits
instead of each leaf re-firing discovery. Persisted-cache path is
unchanged: no discovery on the snapshot, downstream chain handles
its own fallback (pre-PR behavior on that path).
* test(plugins): isolate snapshot memo across tests that mock manifest registry
The snapshot memo is now process-scoped and effective (~98% hit rate).
Three test files were depending on cache misses (because the broken
cache returned them) — each test would set up its own
loadPluginManifestRegistry mock and expect a fresh derive. With the
cache fixed, an earlier test's mocked registry now leaks into later
tests in the same file.
- io.write-config.test.ts: afterEach now clears the snapshot memo so
the 'demo' plugin mocked in the first test does not survive into
'keeps shipped plugin install config records when index migration
fails', which expects an empty registry to surface the 'plugin not
found: demo' warning.
- gateway/model-pricing-cache.ts: resetGatewayModelPricingCacheForTest
also clears the memo. Tests in model-pricing-cache.test.ts assert
loadPluginManifestRegistryForInstalledIndex was called; the memo
hit otherwise skips the call.
- providers.test.ts: vi.doMock loadPluginMetadataSnapshot to wrap the
existing loadPluginManifestRegistryMock fixture. The plumbing
commit added an auto-fetch fall-through in
resolveOwningPluginIdsForProvider; without the mock, providers
tests hit real disk reads and return empty registries (which is
what surfaced as 9 unrelated-looking failures in the prior CI
run).
* fix(plugins): preserve setup.cliBackends owner matching in provider scan
resolveOwningPluginIdsForProvider now also checks plugin.setup?.cliBackends.
The pre-PR no-registry fallback used resolvePluginContributionOwners which
includes both top-level cliBackends and setup.cliBackends; the PR's manifest
scan replacement was missing the setup case.
* fix(plugins): inherit active registry workspaceDir before loading metadata snapshot
isPluginProvidersLoadInFlight and resolvePluginProviders now resolve
env and workspaceDir once at the entry point (falling back to
getActivePluginRegistryWorkspaceDir) and pass them into both
loadPluginMetadataSnapshot and resolvePluginProviderLoadBase. Pre-fix
the snapshot used params.workspaceDir raw while the load base inherited
the active workspace, so workspace-scoped provider plugins could be
absent from the snapshot manifest registry even though owner resolution
expected them.
Regression test asserts the snapshot mock receives the active
workspaceDir when the caller omits it.
* perf(gateway): thread discovery into applyPluginAutoEnable call sites
Every gateway applyPluginAutoEnable call now passes the snapshot's
PluginDiscoveryResult so the bundled-channel cascade (collectConfiguredChannelIds
→ listBundledChannelIdsWith* → listPotentialConfiguredChannelPresenceSignals)
short-circuits instead of each leaf re-firing discovery.
Startup-time sites pull discovery from the snapshot/lookup-table they already
hold:
- server-plugin-bootstrap.ts (pluginLookUpTable)
- server-startup-plugins.ts (pluginMetadataSnapshot)
- server-startup-config.ts (pluginMetadataSnapshot)
- server-plugins.ts (pluginLookUpTable, both call sites)
Per-RPC sites (server.impl getRuntimeConfig callback, server-methods/channels
status + start handlers, server-methods/send) source discovery via
getCurrentPluginMetadataSnapshot using the runtime config to validate
compatibility. Falls through to the original slow path when the snapshot is
absent or incompatible.
Expose a path-free estimated context budget status on session entries and gateway session rows, render it in status when fresh provider usage is unavailable, and clear stale estimates across reset, refresh, compaction, and session-rotation boundaries.
Verification: focused local Vitest covered session persistence, status rendering, gateway rows, model resets, compaction, and session rotation; GitHub CI passed on clean head cad199e43d.
Refs #80594, #54996, #77992, #84490, #83177, #43009, #83526, #8635.
Defer Gateway channel startup until after readiness, remove startup model prewarm, and move model catalog data onto manifest/static paths so startup no longer loads broad provider runtimes.
Verification:
- focused gateway/catalog/auth/QA Vitest runs
- autoreview clean
- Blacksmith Testbox-through-Crabbox tbx_01ksahn65rsrsqz3q1qyxwf929: pnpm check:changed, exit 0
- PR CI green on ee2b631c72
normalizeAllowedModelRef() and the resolved override ref interpolated
${provider}/${model} after normalizeModelRef(), so a provider-qualified
model id like openrouter/gpt-5.4-mini surfaced as
openrouter/openrouter/gpt-5.4-mini in the allowlist set and policy
denial message, masking the actionable model ref.
Route both sites through modelKey() (src/agents/model-ref-shared.ts)
so the provider segment is collapsed when the model id already starts
with it. Add regression tests covering allowlist hit and denial paths
for the OpenRouter shape.
Fixes#84887