Tool and Plugin Boundary
Tools and plugins solve different problems. Keep the boundary strict.
In code, “plugin” refers to a type that implements the
AgentBehaviortrait.
Tool Responsibility
Tools implement domain actions:
- Read state through
ToolCallContext(snapshots or references) - Call external systems
- Return structured
ToolResult - Emit state mutations and other
Actions throughToolExecutionEffect
Tools should not orchestrate loop policy globally.
Action Reducer Model
The runtime is not “tool returns JSON and stops there”. Tools and plugins both participate in an action/reducer pipeline.
- A plain tool may return only
ToolResult - A richer tool may return
ToolExecutionEffect ToolExecutionEffectcontains:- a
ToolResult - zero or more
Actions applied duringAfterToolExecute
- a
- State changes are represented as
AnyStateActionand reduced by the runtime into the execution patch - Non-state actions can mutate step-local runtime structures such as queued user messages
Conceptually:
Tool / Plugin -> Action(s) -> Phase validation -> StepContext apply -> Reducer / patch commit
This is why “tool execution” in Tirea can do more than return a payload. It can also update persisted state, inject messages, or alter later runtime behavior through actions.
What Tools Can Change
From a tool, you can:
- read typed state through
ToolCallContext(viasnapshot_of,snapshot_at, or live references) - return a
ToolResult - emit state mutations and other
Actions fromexecute_effect(viaToolExecutionEffect+AnyStateAction)
Direct state writes through
ctx.state::<T>().set_*()are rejected at runtime. All state mutations must go through the action pipeline.
Typical tool-emitted effects include:
AnyStateActionto update reducer-backed state- user-message insertion actions
- other custom
Actionimplementations valid inAfterToolExecute
What Plugins Can Change
Plugins implement cross-cutting policy:
- Inject context (
StepStart,BeforeInference) - Filter/allow/deny tools (
BeforeInference,BeforeToolExecute) - Add reminders or execution metadata (
AfterToolExecute)
Plugins operate at phase boundaries and are the right place for rules that must apply uniformly across many tools.
Concrete Examples
Skill activation
The skill activation tool is a good example of a tool using ToolExecutionEffect instead of returning only ToolResult.
It does three things in one execution:
- Returns a success
ToolResult - Emits a state action to activate the skill in persisted state
- Emits additional actions to:
- append the skill instructions into user-visible message flow
- widen allowed tool permissions for the activated skill
This is implemented in crates/tirea-extension-skills/src/tools.rs.
Permission policy
Permission handling is a plugin concern because it is global execution policy, not domain work.
The permission plugin:
- checks state snapshot before tool execution
- blocks, allows, or suspends the call
- can emit
BeforeInferenceActionto include/exclude tools - can emit
BeforeToolExecuteActionto deny or suspend execution
This is implemented in crates/tirea-extension-permission/src/plugin.rs.
Plugin Responsibility
Plugins should not own domain-side business operations.
Rule of Thumb
- If it is business capability, build a tool.
- If it is execution policy or guardrail, build a plugin.
- If it is a domain tool that needs to return both a result and side effects, use
execute_effect.