Plugin bindings in manifests¶
A plugin binding is how an Agent manifest names a plugin and passes it parameters.
A plugin is a class that a library manifest (library.yaml) registers under a type. The slot name is that type.
Use a binding to pick a non-default plugin for design_pattern, context_manager, assembler, hitl_contract, or user_io_contract. Use it also for the list slots governance, observability, and context_sources.
plugins-reference.md lists the registered plugins.
Minimal working bindings, in shorthand:
spec:
design_pattern: cot
governance:
- gov_no_undeclared_tool
Schema: docs/schemas/runtime/agent.schema.yaml. Code: mas.runtime.spec.plugin_binding.normalize_plugin_binding.
Three forms behave the same at run time: omit the slot, write the plugin name, or write the full object with schema defaults.
mas-ctl compile prints the full object so you can diff it. Compiled agent defaults shows Tutorial 1's minimal manifest, compiled.
Two shapes¶
1. Singleton slot (one plugin)¶
The spec key is the role (design_pattern, context_manager, assembler). The value is the plugin name, optionally with parameters.
String shorthand and object form are equivalent:
spec:
design_pattern: cot # same as {type: cot}
context_manager: summarising
assembler: assembler
Spec keys have no _plugin suffix. Registry type names match the slot. A sub-plugin (today only context_manager.params.summarizer) uses the same shorthand: summarizer: llm is the same as {type: llm}.
params holds constructor and kernel keyword arguments for that plugin.
summarising rejects max_messages; stack rejects summarizer. Assembly
knobs (trimmer, working_memory_messages) are shared. An unknown plugin id
keeps extra keys so a third-party context manager can carry its own kwargs.
design_pattern also accepts config as an alias.
type and ref use the same lookup: a registered name, an alias (full-history, tot, ...), or the full plugin URN (mas.cm.stack). Write one of them, not both.
File paths, module:// and oci:// locators are not resolved in this release. To use your own class, register it in a library manifest (see Libraries) and name it here.
2. List slot (several plugins)¶
The spec key is still the role. Each list item is a plugin name, either bare or as a one-key map of parameters:
spec:
governance:
- sample_governance:
hitl_on_tool: true
hitl_mode: interactive
- no_undeclared_tool
- retry_on_error:
error_recovery_plugin: retry_on_error
error_policy:
transient: retry
fatal: block
observability:
- native
error_recovery_plugin and error_policy may appear on any governance stanza. The parser flattens them and installs the recovery plugin on the ingress chain, not on KernelConfig.
Full chain: governance.md. Retry defaults: reliability.md.
Every list slot defaults to empty (no plugins). The flavour or CLI attaches native observability. Omitting spec.observability does not attach it.
Package defaults and equivalent explicit config¶
Omitted fields come from defaults.yaml. A workspace config.yaml defaults: block can override them. Compile writes the object form below.
design_pattern (default react)¶
Omit the field, write design_pattern: react, or:
spec:
design_pattern:
type: react # aliases: react@v1
params:
max_steps: null # kernel dispatch cap; null/omitted = no cap
max_cot_pass: 1 # extra CoT refinement passes
parallel: true # parallel tool calls
design_pattern: cot has the same shape with type: cot and the same default params.
Other shipped ids: single_pass, introspection, plan_execute, tree_of_thoughts, deterministic_single, deterministic_linear, deterministic_parallel.
Aliases: analyst and planner (to react), linear (to single_pass), reflection (to introspection), tot (to tree_of_thoughts), plan-execute (to plan_execute).
| Param | Maps to | Default | Who uses it |
|---|---|---|---|
max_steps |
KernelConfig.max_auto_steps |
none | every pattern (loop cap; with-hardened sets 512) |
max_cot_pass |
KernelConfig.max_cot_pass |
1 | cot, introspection, tree_of_thoughts (min 2 internally) |
parallel |
KernelConfig.parallel_tool_calls |
true | react and tool-calling patterns |
assembler (default assembler)¶
spec:
assembler:
type: assembler # aliases: context-assembler, ctx-assembler
params:
emit_segments: true
always_reassemble: false
# token_budget: omit = no part-eviction budget
The assembler builds the LLM messages[] from context. One assembler ships (ContextAssemblerPlugin). History policy is not an assembler param. It lives on spec.context_manager.
context_manager (default summarising)¶
The context manager keeps conversation history within the model's context window (the maximum input size). Each manager is a plugin that carries its own strategy code.
spec:
context_manager:
type: summarising
params:
keep_turns: 10
hysteresis_ratio: 0.1
summarizer: llm # sub-plugin; ≡ {type: llm}. Alternative: drop
# summarizer: {type: llm, params: {model: gpt-4o-mini}} # cheaper summary LLM
trimmer:
reserve_tokens: 2000 # effective completion budget
safety_margin_ratio: 0.1
Equivalent forms of the other managers:
sliding-window:{type: sliding-window, params: {keep_turns: 10}}. It drops older turns and uses no summarizer.stack:{type: stack}, with optionalmax_messages.
Full examples: context-assembly.md.
summarizer (sub-plugin of summarising)¶
The summarizer compacts history: it replaces older turns with a shorter summary. It is not a top-level spec key. The summarising context manager composes it, and its registry type is summarizer.
| Plugin | Default? | Behaviour |
|---|---|---|
llm |
yes | Uses the summarizer model slot (Agent, then MAS, then experiment.models.summarizer), else the agent's resolved turn model (summarize_messages). Optional params.model (a spec.models[].id or LiteLLM string) picks another model. Degrades to drop without a live engine. |
drop |
Discards older turns and keeps keep_turns verbatim. |
# default (inside summarising) — summarizer slot else this agent's turn model
params:
summarizer: llm # ≡ {type: llm}
# cheaper / dedicated summarizer
params:
summarizer:
type: llm
params:
model: gpt-4o-mini # or a spec.models[].id such as summarizer
# instructions: | # optional; default is the package system prompt
# Preserve city names and dates.
# explicit drop
params:
summarizer: drop # ≡ {type: drop}
Thresholds, prompt, logs, MCE judge: summarization.md. Example: summarizer-override. Card: summarizer.md.
memory¶
memory is not a plugin binding. The schema accepts memory: semantic (string), a list of names, or the object form memory.types[] (see agent.md). The runtime does not read spec.memory in this release. Memory works through spec.memory_seed and the memory-search tool instead.
List plugins¶
| Slot | Default | Shipped plugins |
|---|---|---|
governance |
[] |
sample / sample_governance, gov_no_undeclared_tool (alias no_undeclared_tool), retry_on_error, backtrack_on_error |
observability |
[] (flavour/CLI may attach native) |
native, otel (from mas-library-telemetry) |
context_sources |
[] |
native, adk, langchain (skill engines from mas-library-skills) |
Tool providers are not an agent slot. The local provider loads spec.tools. Remote MCP endpoints come from infra ToolServerRegistry. Agent and overlay schemas reject a providers key.
Compaction¶
Compaction shortens history so it fits the context window. spec.working_memory.compaction is shorthand for spec.context_manager. Prefer spec.context_manager. If both are set, context_manager wins.
The context manager owns recency. manage_history bounds the LLM view. After each turn commit, the same cap is written back to committed_messages and conversation chunks, and folded prefix data is dropped. Hysteresis on the context-manager instance avoids a summarizer LLM call on every in-turn step.
compaction.model (strategy summarize) is shorthand for summarizer.params.model. See summarization.md.
stack with no max_messages has no recency cap. Set max_messages, or use the default summarising or sliding-window plugin.
Overlay merge¶
An overlay is a YAML patch merged onto a manifest. Singleton plugin slots (design_pattern, context_manager, assembler) merge with plugin_binding_merge:
- A string overlay (
context_manager: stack) replaces the binding. - The same
typeorref(or a params-only patch) deep-mergesparams. Nested maps keep sibling keys: patchingtrimmer.reserve_tokensdoes not droptrimmer.max_tokens. - A changed
typeorrefreplaces the binding. Constructor kwargs from the previous plugin are dropped.
On design_pattern, params wins over the legacy config alias when both are present.
# base
spec:
context_manager:
type: summarising
params: {keep_turns: 10}
# overlay — type change drops keep_turns
spec:
context_manager:
type: stack
# result: {type: stack}
# overlay — same type keeps and patches params
spec:
context_manager:
params: {hysteresis_ratio: 0.3}
# result: {type: summarising, params: {keep_turns: 10, hysteresis_ratio: 0.3}}
# overlay — nested params keep siblings
spec:
context_manager:
params:
trimmer: {reserve_tokens: 4000}
# result trimmer: {max_tokens: 128000, reserve_tokens: 4000} (if base had both)
Malformed slots¶
A slot that is not a plugin name or a {type, ref, params} object is a hard error (PluginBindingError) at mas-ctl compile and mas-ctl validate. This holds even with --no-validate.
The runtime reads an already-compiled manifest leniently (normalize_plugin_binding_lenient). It treats a malformed value as omitted and uses the slot's platform default. This only matters if you build a manifest dict by hand, bypassing mas-ctl.
See also¶
- Compiled agent defaults — Tutorial 1's minimal manifest, fully expanded
- agent.md — every agent spec field
- context-assembly.md — assembler, context managers, summarizers
- summarization.md — thresholds, summarizer/judge model overrides, logs
- runtime/docs/agent-defaults.md
- plugins-reference.md — catalog of registered plugins