Skip to main content
Skills are atomic robot capabilities that the Innate agent chains together to accomplish complex, long-horizon behaviors. Each skill encodes a single capability—moving through the world, manipulating objects, speaking, or reaching an external service like email or an API—that can be combined with others to form coherent action sequences. When the Innate agent receives a request like “check on grandma,” it decomposes this into a skill chain: navigate to bedroom → look around → send picture via email → speak reassurance. Four skills, one coherent behavior.

Two types of skills

Skills are defined in one of two ways.

Code-defined skills

Code-defined skills are Python classes with explicit logic. You declare what the skill consumes with a type annotation and the runtime injects it; the agent reads your execute() signature and the class docstring to call the skill correctly.
Use this style when you want deterministic physical control, API and web-service calls, explicit sequencing, or custom sensor processing.
Policy-defined skills are learned policies trained from demonstrations. For manipulation, the current workflow uses ACT (Action Chunking with Transformers).
Use this style when behavior is easier to learn from data than encode by hand, especially for visuomotor manipulation.

Where to put your skills

Your skills go in ~/innate-os/workspace/custom_skills/ on the robot. That directory is gitignored, so it survives OS updates. Getting this path right is the single most common stumbling block when a skill “doesn’t appear”.
  • Code-defined skill → a Python file: ~/innate-os/workspace/custom_skills/my_skill.py
  • Policy-defined (physical) skill → a directory with its metadata and checkpoint: ~/innate-os/workspace/custom_skills/my_skill/metadata.json
No registration required. Defining a Skill subclass is the registration — the same model as a PyTorch nn.Module. Drop the file in place and it hot-reloads within seconds; the directory is watched while the robot runs, for new files and edits alike. Because skills are found by import rather than by scanning files, ordinary Python works: several skills in one file, a skill split across a subpackage with relative imports, helper modules sitting next to it. A .py that defines no Skill is just a module you can import.
A skill file that fails to import no longer disappears. It shows up in the web app’s skills menu as a disabled row with its load error, and clears the moment you fix it.

Skill packages and IDs

Every directory under workspace/ is a skill package, and skill IDs are namespaced by the package they come from: The skill’s name is its class name, snake_cased: class VictorySpin becomes victory_spin, and in custom_skills/ its full ID is local/victory_spin. Packages import each other by bare name (from innate_skills import arm_utils). To install a pack that lives elsewhere on disk — a team checkout, a mounted volume — symlink it in. It then behaves exactly like a dropped-in folder: discovered at boot, hot-reloaded on edit, IDs namespaced by the link name.
This replaces the 0.6.x extra_skill_dirs / extra_agent_dirs settings and the old ~/skills / ~/agents locations. Upgrading moves those directories into workspace/custom_skills and workspace/custom_agents for you. In the simulator, a symlink target must also be mounted into the container or the pack is skipped.

Referencing a skill from an agent

Prefer the class — your editor catches a rename or a bad import before the robot does:
Full ID strings still work ("local/my_skill", "innate-os/navigate_to_position") and are the escape hatch when the ID is only known at runtime. A bare name without the prefix won’t resolve.