- In one line
- Twelve
plugin-*features; each one implementsCucaPluginand no plugin depends on another. - You need
- The plugin features you want, named explicitly; none is enabled by default.
- Read this if
- You need a plugin's feature flag, entry type, hooks, config defaults or caps.
The twelve features
Every plugin attaches with CucaClientBuilder::register_plugin(Arc<dyn CucaPlugin>). The last column lists the hooks where the plugin acts; no bundled plugin overrides execute_local_tool.
| Feature | Entry type | Hooks that act |
|---|---|---|
plugin-mcp | McpPlugin | on_stream_chunk |
plugin-sandbox | SandboxPlugin | on_stream_chunk |
plugin-memory | MemoryPlugin | on_request |
plugin-guardrails | JsonGuardrailPlugin | on_stream_chunk |
plugin-subagent | SubagentPlugin | on_stream_chunk |
plugin-hitl | HitlPlugin | on_stream_chunk |
plugin-web-search | WebSearchPlugin | on_stream_chunk |
plugin-skills | SkillsPlugin | on_request, on_stream_chunk |
plugin-telemetry | OpenTelemetryPlugin | on_request, on_stream_chunk, on_response_complete |
plugin-session-log | SessionLogPlugin | on_request, on_stream_chunk, on_response_complete |
plugin-cost | CostPlugin | on_request, on_response_complete |
plugin-redaction | RedactionPlugin | on_request |
plugin-session-log also implements SessionStorePlugin, which adds append_log, replay_session and fork_session.
Headings on every plugin page
Each plugin page answers the same questions under the same headings, in this order:
- Feature and entry type
- Hooks
- Configuration
- Caps and at-cap policy
- Errors
- Example
- Related pages
Hook order and error handling
Plugins run in registration order at every hook site. A plugin must not require a position relative to another plugin.
| Hook | Fires | On the first error |
|---|---|---|
on_request | Once per turn, before dispatch; may mutate the request. | The turn fails before dispatch; later plugins' hooks do not run. |
execute_local_tool | Per ToolCall block; the first plugin to return Some wins. | The stream yields the error in place of the block. |
on_stream_chunk | Once per block; may mutate the block. | The stream yields the error in place of the block; the block is neither accumulated nor token counted. |
on_response_complete | Once, when the stream ends. | Not propagated; the next plugin's hook still runs. |
- A value returned from
execute_local_toolmust be aToolResultwhosetool_call_idequals the incomingToolCallid. A mismatch isPluginError::Validationwith schemalocal tool result. - A
plugin-guardrailsbuild logs a failedon_response_completethroughtracingunder targetcuca::client; other builds drop it. - A prompt cache hit under
service-prompt-cachereplays the stored blocks, skipsexecute_local_toolandon_stream_chunk, and still runs everyon_response_complete. - Recoverable conditions belong in the block itself: guardrails, MCP, sandbox and the other tool plugins replace a
ToolCallwith aToolResultinstead of returning an error.
Caps and at-cap policies
Every plugin page states its caps as bound, policy at the bound and usage gauge, in that order. The reasoning behind refusing rather than evicting is Memory discipline.
| Plugin | Bound | At the bound | Usage gauge |
|---|---|---|---|
plugin-guardrails | MAX_TRACKED_CALLS, 4096 | Evict the oldest, in insertion order. | tracked_calls() |
plugin-subagent | DEFAULT_MAX_PENDING, 1024 | Refuse the spawn. | pending_len() |
plugin-subagent | MAX_SPAWN_LOG, 4096 | Evict the oldest. | spawns() length |
plugin-hitl | DEFAULT_MAX_AUDIT_ENTRIES, 65536 | Refuse the gated call. | audit_len() |
plugin-session-log | InMemoryBackend::DEFAULT_MAX_RECORDS, 65536 | Refuse the append or fork. | len() against max_records() |
plugin-cost | CostConfig::max_tracked_models, default 64 | Fold into one overflow bucket; totals stay exact. | usage(), breakdown() |
plugin-memory | Message list: the active Budget | Run the strategies pipeline. | ContextUsage readings, count_tokens() |
plugin-memory | Working graph: none | The caller owns the bound. | snapshot(), MemoryGraph::len() |
plugin-redaction | See the list below | Reject at construction, or refuse the request. | rule_count(), match_cap() |
plugin-sandbox | See the list below | Trap that call. | last_diagnostic() |
plugin-mcp, plugin-skills, plugin-telemetry, plugin-web-search | None: nothing grows with traffic | Not applicable | None |
plugin-redaction bounds:
RedactionConfig::MAX_RULES, 256 rules per policy.MAX_PATTERN_BYTES, 512 per literal or prefix;MAX_KIND_BYTES, 32 perkind.max_matches_per_text, default 1024, at mostMAX_MATCHES_PER_TEXT(4096). A string over it fails withPluginError::HookFailureinstead of being truncated.MAX_JSON_DEPTH, 64 levels ofToolCallarguments, also refused withHookFailure.- An empty or over-cap policy is rejected at construction.
plugin-sandbox bounds, per call:
max_memory_bytes, 64 MiB.max_instructions, 1000000.timeout_ms, 5000.- Guest output, 8 MiB, not configurable.
- Nothing accumulates across calls, so no gauge is needed.
No edges between plugins
The plugin tier is flat. Cargo.toml declares no feature edge between plugins, and no plugin references another in code or at runtime.
- Every
plugin-*feature depends on core only and builds alone on any one provider. - A service may depend on a plugin:
service-entity-extractionandservice-vector-storerequireplugin-memory, andservice-replayrequiresplugin-session-log. A plugin never depends on, or names, a service. - Services never implement
CucaPlugin; they are explicit-call, and registering one withregister_pluginis a compile error. - The CI job
plugin_layeringgreps every file undersrc/plugins/forservice-, forcrate::services, and forplugins::paths toprompt_cache,entity_extraction,replayandrate_limit. Acfg(all(...))reverse edge compiles in both the solo and all-features builds, so only that inspection catches it.
Plugins and services explains the layering.
Shared optional dependencies
Eight optional crates are enabled by more than one feature. A shared crate is a shared dependency, not a feature edge between plugins.
| Crate | Enabled by |
|---|---|
reqwest with rustls | The seven HTTP provider-* features, provider-local and plugin-web-search |
tokio | provider-local, plugin-mcp, plugin-subagent, plugin-hitl, plugin-web-search, service-speculative, service-rate-limit |
tokio-stream | The seven HTTP provider-* features and service-speculative |
tracing | plugin-guardrails, plugin-telemetry, plugin-redaction |
base64 | provider-anthropic, plugin-sandbox |
sha2 | provider-anthropic, provider-local, service-prompt-cache |
postcard | plugin-session-log, service-prompt-cache |
tiktoken-rs | plugin-memory, plugin-cost |
plugin-web-search enables reqwest/rustls itself, so it runs HTTPS searches in a build where no provider feature contributes TLS.
Related pages
- The feature matrix: every feature and its dependencies.
- Error types:
PluginErrorandCucaError. - Write a custom plugin: implement
CucaPluginyourself.