Reference

Plugins

Twelve plugin features, all hook plugins, with no dependency between any of them.

In one line
Twelve plugin-* features; each one implements CucaPlugin and 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.

FeatureEntry typeHooks that act
plugin-mcpMcpPluginon_stream_chunk
plugin-sandboxSandboxPluginon_stream_chunk
plugin-memoryMemoryPluginon_request
plugin-guardrailsJsonGuardrailPluginon_stream_chunk
plugin-subagentSubagentPluginon_stream_chunk
plugin-hitlHitlPluginon_stream_chunk
plugin-web-searchWebSearchPluginon_stream_chunk
plugin-skillsSkillsPluginon_request, on_stream_chunk
plugin-telemetryOpenTelemetryPluginon_request, on_stream_chunk, on_response_complete
plugin-session-logSessionLogPluginon_request, on_stream_chunk, on_response_complete
plugin-costCostPluginon_request, on_response_complete
plugin-redactionRedactionPluginon_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:

  1. Feature and entry type
  2. Hooks
  3. Configuration
  4. Caps and at-cap policy
  5. Errors
  6. Example
  7. 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.

HookFiresOn the first error
on_requestOnce per turn, before dispatch; may mutate the request.The turn fails before dispatch; later plugins' hooks do not run.
execute_local_toolPer ToolCall block; the first plugin to return Some wins.The stream yields the error in place of the block.
on_stream_chunkOnce 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_completeOnce, when the stream ends.Not propagated; the next plugin's hook still runs.
  • A value returned from execute_local_tool must be a ToolResult whose tool_call_id equals the incoming ToolCall id. A mismatch is PluginError::Validation with schema local tool result.
  • A plugin-guardrails build logs a failed on_response_complete through tracing under target cuca::client; other builds drop it.
  • A prompt cache hit under service-prompt-cache replays the stored blocks, skips execute_local_tool and on_stream_chunk, and still runs every on_response_complete.
  • Recoverable conditions belong in the block itself: guardrails, MCP, sandbox and the other tool plugins replace a ToolCall with a ToolResult instead 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.

PluginBoundAt the boundUsage gauge
plugin-guardrailsMAX_TRACKED_CALLS, 4096Evict the oldest, in insertion order.tracked_calls()
plugin-subagentDEFAULT_MAX_PENDING, 1024Refuse the spawn.pending_len()
plugin-subagentMAX_SPAWN_LOG, 4096Evict the oldest.spawns() length
plugin-hitlDEFAULT_MAX_AUDIT_ENTRIES, 65536Refuse the gated call.audit_len()
plugin-session-logInMemoryBackend::DEFAULT_MAX_RECORDS, 65536Refuse the append or fork.len() against max_records()
plugin-costCostConfig::max_tracked_models, default 64Fold into one overflow bucket; totals stay exact.usage(), breakdown()
plugin-memoryMessage list: the active BudgetRun the strategies pipeline.ContextUsage readings, count_tokens()
plugin-memoryWorking graph: noneThe caller owns the bound.snapshot(), MemoryGraph::len()
plugin-redactionSee the list belowReject at construction, or refuse the request.rule_count(), match_cap()
plugin-sandboxSee the list belowTrap that call.last_diagnostic()
plugin-mcp, plugin-skills, plugin-telemetry, plugin-web-searchNone: nothing grows with trafficNot applicableNone

plugin-redaction bounds:

  • RedactionConfig::MAX_RULES, 256 rules per policy.
  • MAX_PATTERN_BYTES, 512 per literal or prefix; MAX_KIND_BYTES, 32 per kind.
  • max_matches_per_text, default 1024, at most MAX_MATCHES_PER_TEXT (4096). A string over it fails with PluginError::HookFailure instead of being truncated.
  • MAX_JSON_DEPTH, 64 levels of ToolCall arguments, also refused with HookFailure.
  • 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-extraction and service-vector-store require plugin-memory, and service-replay requires plugin-session-log. A plugin never depends on, or names, a service.
  • Services never implement CucaPlugin; they are explicit-call, and registering one with register_plugin is a compile error.
  • The CI job plugin_layering greps every file under src/plugins/ for service-, for crate::services, and for plugins:: paths to prompt_cache, entity_extraction, replay and rate_limit. A cfg(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.

CrateEnabled by
reqwest with rustlsThe seven HTTP provider-* features, provider-local and plugin-web-search
tokioprovider-local, plugin-mcp, plugin-subagent, plugin-hitl, plugin-web-search, service-speculative, service-rate-limit
tokio-streamThe seven HTTP provider-* features and service-speculative
tracingplugin-guardrails, plugin-telemetry, plugin-redaction
base64provider-anthropic, plugin-sandbox
sha2provider-anthropic, provider-local, service-prompt-cache
postcardplugin-session-log, service-prompt-cache
tiktoken-rsplugin-memory, plugin-cost

plugin-web-search enables reqwest/rustls itself, so it runs HTTPS searches in a build where no provider feature contributes TLS.

Per-plugin pages

Plugins

MCP

The Model Context Protocol client plugin: transports, the stateless 2026-07-28 protocol, and tool call routing.

Plugins

Sandbox

The WebAssembly code execution plugin: the guest ABI, resource limits, and the run_code and sandbox_exec tools.

Plugins

Memory

The context compaction and working memory graph plugin: triggers, the compaction pipeline, and graph context injection.

Plugins

Guardrails

The JSON Schema output guardrail plugin: schema keys, the retry protocol, and the tracked-call cap.

Plugins

Subagent

The child subagent delegation plugin: spawn and collect tools, Git worktree isolation, and the pending-child cap.

Plugins

Human approval

The human-in-the-loop approval plugin: risk classification, the failure-closed default, and the audit log cap.

Plugins

Web search

The live web search plugin: the three provider backends, the web_search and web_extract tools, and their request shapes.

Plugins

Skills

The reusable agent skills plugin: SKILL.md loading, the catalog injection, and the skill, skill_read, and skill_search tools.

Plugins

Telemetry

The OpenTelemetry observability plugin: its three instruments, the meter name, and the tracing log events.

Plugins

Session log

The append-only session trajectory plugin: the SessionEvent model, the two storage backends, and forking.

Plugins

Cost accounting

The token and currency ledger plugin: budget caps, per-model breakdown, and the tiktoken estimate it prices from.

Plugins

Redaction

Outbound PII and secret scrubbing over every text-bearing request field: the rule types, the scrubbed-field map, and why on_stream_chunk stays a no-op.

↑↓ to navigate ↵ to open esc to close