Skill Lifecycle¶
This guide walks through how a shared skill flows from mirai-agent-core's router all the way to a tool result, and where each architectural decision in this catalog plugs in. It's the page to read when you want to understand "what actually happens at runtime when my agent uses AgenticRAGSkill".
End-to-end flow¶
1. Import time
│
▼
import mirai_shared_skills
│
├─► _registry.bootstrap([...]) ADR-0004
│ populates _REGISTRY with 8 descriptors
│
└─► every skill class is now importable
(no instantiation yet)
2. Client construction (downstream code)
│
▼
skill = AgenticRAGSkill(graph=…, vector=…) ADR-0002 (provider DI)
│
▼
gated = SecureSkill(skill, policy={…}) agent-core ADR-0012
│
▼
engine.attach(gated) agent-core API
│
▼
engine accepts a list[BaseSkill]
3. Per-turn — agent-core territory
│
▼
user message
│
▼
GuardrailsManager (regex fast-path) agent-core ADR-0002
│
▼
SemanticRouter selects active skills agent-core ADR-0008
│ output: RouteSelection(active=[gated_rag, gated_browser, …])
│
▼
ephemeral Pydantic AI Agent constructed
│ tools = sum(s.get_tools() for s in active)
│ each get_tools() returns Tool objects
│ built via tool_from_function ADR-0007
│
▼
SecureSkill.get_tools() filters/wraps ADR-0001 + agent-core ADR-0012
│ BLOCKED tools: dropped
│ REQUIRES_HITL tools: wrapped with with_hitl_approval
│ SAFE tools: pass through
│
▼
Agent runs the turn, possibly calling tools
│
▼
tool result returned through AG-UI stream agent-core ADR-0003
Where each ADR fires¶
| Phase | ADR | What happens |
|---|---|---|
| Import | ADR-0004 | bootstrap([...]) registers every descriptor. find-skills is now usable. |
| Import | ADR-0003 | If a backend extra is missing, constructing a provider raises *UnavailableError — but importing the package doesn't. |
| Construction | ADR-0002 | Concrete providers passed to AgenticRAGSkill.__init__. |
| Construction | ADR-0001 | Client decides whether to wrap in SecureSkill. raw skills MUST wrap; standard MAY wrap for defense in depth. |
| Construction | ADR-0007 | Skill's get_tools() returns Tool objects built from plain async methods. |
| Per-turn | ADR-0008 | LLM may call load_skill_instructions to fetch long-form guidance from a references/ file. |
| Per-turn | ADR-0005 | If an HTTP-using skill returns 401/403, agent calls inspect_status_code then request_credential_handoff. |
| Per-turn | ADR-0006 | AgenticRAGSkill enforces token budget on retrieved chunks before returning. |
Where SecureSkill fits¶
SecureSkill is part of mirai-agent-core (agent-core ADR-0012) — not part of this catalog. The two work together:
- The catalog says (via
SkillDescriptor.category) "this skill needs wrapping". SecureSkillsays (via per-toolSecurityLevel) "and here's the policy for each tool".
The catalog cannot ship a SecureSkill directly because the right policy depends on the client's schema, client's threat model, client's user identity, etc. The two-tier design (catalog category × runtime per-tool level) cleanly separates "what this skill does" from "what this client allows".
Common patterns¶
"Wrap everything by default"¶
A defensive client wraps every skill — not just raw ones — with conservative defaults:
def harden(skill: BaseSkill) -> SecureSkill:
# Default-deny: any tool not explicitly listed is BLOCKED.
return SecureSkill(skill, policy=allowlist_for(skill))
"Audit which skills are raw"¶
from mirai_shared_skills import all_descriptors
raw_skills = [d for d in all_descriptors() if d.category == "raw"]
print(f"Raw skills requiring wrapping: {[d.name for d in raw_skills]}")
"Subscribe to credential handoffs"¶
When AuthenticationGatesSkill emits a CredentialHandoff, the AG-UI stream surfaces it to the calling system. The UI renders it as an approval prompt; the user supplies the credential; the agent's turn resumes.
Related¶
- Architecture Overview — the package layout this lifecycle threads through.
- ADR-to-Module Map — which source file owns each step.
- agent-core architecture — the upstream piece of this dance.