Requires OS 0.7.0 or newer.
Declare, then call
Sub-skills are declared exactly like robot state — annotate the attribute with the skill’s class:- Blocks until the sub-skill finishes — no callbacks or polling.
- Raises
SkillFailedif the sub-skill fails, so a failing step stops the routine unless you catch it. - Returns a
SkillOutput—.message,.data, and.ok. - Shows up as its own step in the app timeline, so a composed routine is legible while it runs.
Learned policies call the same way
A physical skill — a trained policy or recorded demonstration — has no class of its own, so the catalog generates a typed reference for it inside its own folder. Import and declare it exactly like a code skill:Reading a sub-skill’s output
Skills that return structured data expose it on.data:
Handling failure
A sub-skill that fails raisesSkillFailed. Catch it to recover, or let it propagate to
fail your skill too — which is often what you want for a step that must succeed:
A worked example
Talk, emote, shuffle, turn, and attempt a learned pick — six skills chained into one:self.storage, speaks with self.say(..., wait=True), and returns a plain success message.
Choosing a skill at run time
When you don’t know which skill to call until the routine is running, dispatch by id:self.skills.run() returns a failed SkillOutput rather than raising, so you check .ok yourself. Prefer a declaration whenever the skill is known ahead of time — it’s typed, your editor catches renames, and the dependency is verified before the run starts.
When to compose vs. write from scratch
Composing is also how a one-off demo becomes a reusable capability: name the routine, give it
guidelines(), and the agent can trigger the whole chain with a single skill call.
