#18911: feat(plugins): Add registerStreamFnWrapper and updatePluginConfig APIs
gateway
extensions: lobster
agents
size: S
Cluster:
Security Enhancements and Guardrails
## Summary
Adds two new methods to the plugin API:
1. **`api.registerStreamFnWrapper()`** — lets plugins wrap the agent's `streamFn` to intercept, modify, or proxy LLM API calls
2. **`api.updatePluginConfig()`** — lets plugins write to their own config section
**Why:** Memory/proxy plugins need to route LLM calls through external services and let users configure them via CLI commands. Today's plugin hooks can observe but not modify LLM calls, and plugins have no way to persist their own configuration.
---
## 1. registerStreamFnWrapper
### Problem
Plugins that need to proxy LLM calls (memory services, observability, cost tracking) have no way to intercept the actual HTTP request. The existing hooks are:
- `llm_input` / `llm_output` — read-only observation, can't modify anything
- `before_agent_start` — can prepend context, but can't change the endpoint URL or add HTTP headers
Internally, OpenClaw already wraps `streamFn` for cache tracing, extra params, and payload logging — but this mechanism isn't exposed to plugins.
### API
```typescript
api.registerStreamFnWrapper((next: StreamFn) => {
return (model, context, options) => {
const proxiedModel = { ...model, baseUrl: "https://my-proxy.example.com" };
const proxiedOptions = {
...options,
headers: { ...options?.headers, "X-Custom-Header": "value" },
};
return next(proxiedModel, context, proxiedOptions);
};
});
```
### Use Cases
1. **Memory proxy** — Route through MemoryRouter/Supermemory to inject recalled context and capture conversations
2. **Observability** — Log/trace all LLM calls to an external service (Langfuse, Helicone, etc.)
3. **Cost tracking** — Monitor token usage per-request
4. **Custom routing** — Route specific models through different endpoints
5. **Header injection** — Add auth tokens, correlation IDs, or metadata
---
## 2. updatePluginConfig
### Problem
Plugins can read their config (`api.pluginConfig`) but have no way to write it. This means plugins that need user-facing CLI setup commands (like `openclaw mr <key>` to set an API key or `openclaw mr off` to disable) can't persist changes — users have to manually edit `~/.openclaw/openclaw.json`.
### API
```typescript
// Write to the plugin's own config section only
await api.updatePluginConfig({ key: "mk_xxx" });
// Enable/disable the plugin itself
await api.updatePluginEnabled(true);
```
### Scope & Security
- **Scoped to own config only.** A plugin can only write to `plugins.entries.<its-own-id>.config`. It cannot modify other plugins' config or core OpenClaw settings.
- **Fresh read before write.** Config is read at write time to avoid overwriting concurrent changes.
---
## Changes
4 files changed, ~104 lines added:
| File | Change |
|------|--------|
| `src/plugins/types.ts` | Add `StreamFnWrapperFn` type, add `registerStreamFnWrapper` and `updatePluginConfig`/`updatePluginEnabled` to `OpenClawPluginApi` |
| `src/plugins/registry.ts` | Add `PluginStreamFnWrapperRegistration` type, `streamFnWrappers` array, registration functions, config write implementation |
| `src/agents/pi-embedded-runner/run/attempt.ts` | Apply registered wrappers after existing internal wrappers |
| `src/plugin-sdk/index.ts` | Export `StreamFnWrapperFn` type for plugin authors |
## Breaking Changes
None. New additive API only.
## Local Validation
- ✅ `pnpm build` — clean
- ✅ Proof-of-concept plugin tested end-to-end (MemoryRouter)
- Existing tests unaffected (no wrappers registered = no behavior change)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds two new plugin API methods to enable plugins to intercept LLM calls and persist their own configuration.
**Key changes:**
- `registerStreamFnWrapper()` allows plugins to wrap the agent's `streamFn` and intercept/modify/proxy LLM API calls
- `updatePluginConfig()` and `updatePluginEnabled()` let plugins write to their own config section
**Implementation details:**
- Wrappers are applied after internal wrappers (cache trace, payload logger) in the correct composition order
- Config updates are scoped to `plugins.entries.<pluginId>.config` only - plugins cannot modify other plugins or core settings
- Dynamic imports used for config/io to avoid test side effects
- All test mocks updated to include new API methods
- Types exported through plugin-sdk for external plugin authors
**Use cases enabled:**
- Memory proxy plugins (route calls through MemoryRouter/Supermemory)
- Observability (log/trace all LLM calls to external services)
- Cost tracking (monitor token usage per-request)
- Custom routing (route specific models through different endpoints)
- CLI setup commands (persist API keys and enable/disable state)
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with no critical issues found
- The implementation is well-architected with proper security scoping, type safety, test coverage, and follows established patterns. Config updates are scoped to plugin-only sections, wrappers compose correctly with existing internal wrappers, dynamic imports prevent test side effects, and all test mocks are comprehensively updated.
- No files require special attention
<sub>Last reviewed commit: 37cde57</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
75.4%
#22624: feat(plugins): add before_context_send hook and model routing via b...
by davidrudduck · 2026-02-21
73.2%
#20499: test(plugins): add bundled+config duplicate discovery regression
by dcol91863 · 2026-02-19
73.0%
#10680: docs: clarify api.on() vs api.registerHook() for plugin hooks
by yassinebkr · 2026-02-06
72.9%
#20184: feat: memory plugin compaction control
by solstead · 2026-02-18
72.9%
#11124: feat(plugins): add before_llm_request hook for custom LLM headers
by johnlanni · 2026-02-07
72.7%
#20424: Fix plugin extension path traversal in discovery/install
by markmusson · 2026-02-18
72.6%
#23574: security: P0 critical remediation — plugin sandbox, password hashin...
by lumeleopard001 · 2026-02-22
72.6%
#9006: fix: streaming UI, session locks, routing performance, plugin sandb...
by facundollamas2007 · 2026-02-04
72.3%
#12596: fix(status): show third-party memory plugins as active instead of u...
by nhadaututtheky · 2026-02-09
72.3%