Contracts¶
A contract is a fixed interface between the kernel and the code it calls. The kernel is the core of mas-runtime. Read this page to learn which boundaries exist before you write a plugin. Other terms are in the glossary.
The problem¶
The kernel must call tools, models, memory, and governance without knowing their implementations. A contract names what the kernel may call. A plugin implements one or more contracts. You swap a plugin without touching the kernel.
Example: a manifest picks a design pattern by name. The kernel calls it through the DesignPatternPlugin contract.
spec:
design_pattern: react
The machine-readable registry is contracts-registry.yaml. It lists ingress and egress events and the Python modules for each registered contract.
Implemented in this release (OSS)¶
These boundaries are wired in the kernel. Tutorials and paper labs exercise them.
| Contract / plugin | Role |
|---|---|
ToolContract |
Tool listing and invocation. See the reference. |
MemoryContract |
Memory read and write at the engine boundary. |
ContextContract / ContextManagerContract |
Prompt and conversation context. |
DesignPatternPlugin |
ReAct, chain-of-thought, and single-pass lifecycles. |
EngineContract |
LLM, tool, and memory I/O. |
LLMProvider |
Chat-completion wire protocol (OpenAI-compatible, routed like tools). Static knobs: spec.models[] sampling, reasoning, extra, and the catalog. Dynamic kwargs: stream, stream_options, tool_choice, extra_body, extra_headers, extra_query. |
CtxAssembler |
Context and prompt assembly. |
ObservabilitySink / EventEmitter |
Trace and JSONL event emission. |
BudgetTracker + budget overlays |
Stateful token and cost governance (lifecycle-control.lab). |
Manifest-selected Protocol categories¶
Library packages declare PluginRegistry categories. A manifest selects a plugin by name. Current categories include tool_provider, governance, hitl_contract, user_io_contract, skill_impl, and the design and context categories.
hitl_contractimplementsHITLContract.request_approval(...). HITL means human in the loop.user_io_contractimplementsUserIOContract.send_progress_update(...).- Both default to
registryinmas-library-standard. - A caller-supplied
InstantiationOptions.hitl_contractor.user_io_contractinstance wins over the manifest. This keeps interactive CLI adapters working.
Governance ingress plugins register under the governance category. Select them in spec.governance[].ingress_plugins with type (or name) and optional chain: stop|continue. They receive the ingress intent snapshot. They cannot access mutable kernel state.
engine_tool_provider and EngineToolContext¶
Orchestration-level tools run another agent instead of calling a function. They register under engine_tool_provider. mas-library-standard ships two variants: delegation (existing peers) and spawn_subagent (new children from pre-authored templates).
A plugin in this category never receives the materialized run or the comm bus. It receives an EngineToolContext. The context exposes three operations and three read-only properties:
| Member | Purpose |
|---|---|
run_turn(agent_id, task, ...) |
Run one turn on an agent of this run. |
spawn_instance(manifest, *, template_id, manifest_dir=None) |
Materialize a child and return its minted id. |
teardown_instance(agent_id) |
Release the child, its working memory, and its ledger depth. |
depth |
Current spawn depth for the session. |
session_id / parent_agent_id |
Identity of the calling scope. |
The budget check lives inside spawn_instance, not in its callers. A third-party plugin therefore meets the same SpawnLedger ceilings as a shipped one. At a ceiling, the facade raises EngineToolBudgetExceeded. It does not return a value a plugin could ignore.
A plugin that holds this object cannot enumerate other agents, read another agent's working memory, or send arbitrary bus messages. The facade does not expose them.
Manifest-facing behaviour is unchanged. spec.behavior.allow_subagent_spawning and spec.behavior.delegation_style still decide whether the tool surface exists. This category changes only how the runtime finds and builds the implementing class.
Taxonomy¶
The runtime groups boundaries into three families.
| Family | Meaning | Examples |
|---|---|---|
| Capability | What the runtime can access or expose. | ToolContract, MemoryContract, RecorderContract |
| Orchestration | How control flows across agents | DelegationContract |
| Governance | What is permitted | BudgetTracker, governance plugins |
For method signatures and implementation detail, see the developer contract reference in the repository. You need a clone to read it.
Contract groups (developer reference)¶
| Group | Topics |
|---|---|
| Models, tools, memory | Tool and model access, prompt assembly, memory backends |
| Context | Prompt context contribution and history trimming |
| Messaging & orchestration | Delegation |
| Control & observability | Execution control, recorder / trace emission |
| Governance | Budget, policy engine, HITL |
| Design patterns | ReAct, chain-of-thought, single-pass lifecycles |
Deep dives on GitHub: model & tools · state & context · messaging & orchestration · execution control · governance · design patterns
Governance (OSS)¶
Governance is the policy layer that decides whether a call may proceed. User reference: governance.md (chain, plugins). Retries, circuit breaker, error_policy, defaults, and logging: reliability.md.
Stateful governance plugins enforce policy across turns. The paper lifecycle-control lab stacks budget caps, guardrails, and HITL through overlays, for example budget-cap on budget_threshold.
Runtime implementation: BudgetTracker in runtime/src/mas/runtime/boundary/gov/budget.py.
Egress and ingress decisions on every tool, LLM, and memory call pass through two layers, evaluated in order:
- Declarative policies.
GovernancePolicyEngine(runtime/src/mas/runtime/boundary/gov/policy_engine.py) matches YAML-authoredPolicyDefinitions against the call. A policy has a trigger condition and anaction:hitl,block,terminate,log,modify,skip,retry, orblacklist. The engine readsspec.governancein the agent or overlay manifest. A matched policy's ownparams.message(orparams.reason) becomes the reported reason. Otherwise the engine builds a reason from the policy name and action. - Parametric profiles.
GovPolicyProfile(egress) andGovIngressProfile(ingress) apply when no declarative policy matches. They are fixed postures such asblock-destructive,hitl-destructive, andretry-on-error. Implementation:resolve_egress_governance,egress_governance_outcome, andingress_governance_outcomeinruntime/src/mas/runtime/boundary/gov/policy.py.
Every decision is emitted as a governance_decision observability event with hook, checkpoint, decision, reason, and policy_name. The Governance lane and per-call badges of the multilevel trajectory plot render these events. See pipeline-processors.md, Multilevel trajectory plot.
Plugin registry (v2)¶
The registry maps contract IDs to Python modules and, where applicable, to Mealy and TLA machines. A Mealy machine is a state machine whose output depends on its state and input. The registered contracts in this release:
| ID | Role |
|---|---|
DesignPatternPlugin |
Reasoning pattern scheduler (ReAct, CoT, single-pass) |
EngineContract |
LLM, tool, and memory I/O |
CtxAssembler |
Prompt / context assembly |
HitlResponder |
Human-in-the-loop approval |
ObservabilitySink |
Trace and event emission |
EnvelopeProduct |
Contract-call envelope (governance + observability wrap) |
EventEmitter |
JSONL and stderr event sinks (mas-library-standard) |
ObservabilityExportPlugin |
Manifest-declared export plugins (native in mas-library-standard, otel in mas-library-telemetry) |
LLMProvider |
Chat-completion wire protocol (OpenAILLMProvider, CacheLLMProvider) |
EventTransform |
Boundary to native or OTel transforms |
See contracts-registry.yaml for aliases, ingress and egress events, and bundled implementations.
Typical call paths¶
In the shipped kernel, budget checks run as governance (BudgetTracker). Every call goes through the envelope (mas.runtime.kernel.envelope): governance authorize, EngineContract I/O, then observability.
Tool execution¶
Runtime or planner
-> ToolContract.list_tools() # required: name, description, parameters
-> BudgetTracker.allow_tool()
-> ToolContract.call_tool(name, arguments) # required two-arg invocation
-> RecorderContract.emit()
Optional advertise keys and call_tool kwargs are documented in
tool-contract.md. Everyday calls use the required two-argument form.
Model execution¶
Runtime
-> BudgetTracker.allow_llm()
-> EngineContract / LLMProvider
-> RecorderContract.emit()
Delegation¶
Runtime
-> DelegationContract.list_tools() and collect_context()
-> DelegationContract.delegate()
See also¶
- Runtime manifest fields: for YAML authors.
- Schema index: validation schemas.
- ToolContract:
call_tool(name, arguments). - ToolServerRegistry: infra connection policy.
- Thin waist: how names resolve to plugins.
- Mealy machines guide
- Glossary: vocabulary.
- Event model: why the
ObservabilitySink/EventEmitterboundary records an announced stream of events instead of exposing a query interface. - Tutorial 14: governance and HITL