How Python-Backed Skills Expose Async Callables to the Prime Agent Kernel
Python-backed skills in Prime Agent expose async callables by exporting an async def run function that the kernel automatically detects and awaits using inspect.isawaitable inside an asyncio.run wrapper.
Prime Agent treats each skill as a small Python package with a single entry point: a callable named run. This design lets skill authors write either synchronous or asynchronous code without worrying about event-loop management. According to the Prime Intellect source code, the kernel handles all the complexity of dispatching and awaiting async skills transparently.
The Skill Contract: Exporting run
Every Python skill must expose exactly one symbol: a callable named run. The kernel imposes no restrictions on whether this callable is synchronous or asynchronous.
# packages/coding-agent/test/fixtures/skills/python-skill/src/python_skill/__init__.py
async def run(value: str = "ok") -> str:
"""Return the provided value."""
return value
The file path matters. The skill name derived from the console-script must match the Python import name exactly—dashes become underscores. For a script named python-skill, the module python_skill is imported.
How the Kernel Detects and Awaits Async Callables
The runtime in prime-agent-runtime/src/rlm/skill.py executes skills through a seven-step pipeline:
- Resolve module name — Strip the extension from the console-script name and normalize dashes to underscores (
cli()) - Import the module —
__import__(prog)loads the skill; import errors becomeRuntimeErrorwith context (cli()) - Retrieve
run—getattr(module, "run", None)fetches the callable; non-callable values raiseRuntimeError(cli()) - Parse CLI arguments —
tyro.cli(skill_fn, args=argv)maps command-line arguments to function parameters (run_cli()) - Detect awaitable —
inspect.isawaitable(result)checks if the call returned a coroutine (run_cli()) - Await if necessary —
await resultruns the coroutine to completion (run_cli()) - Drive event loop —
asyncio.run(run_cli(...))manages the async execution (cli())
Synchronous skills skip steps 5–6 because inspect.isawaitable returns False for regular return values.
Real-World Async Skill Example
The websearch skill demonstrates production-grade async I/O:
# packages/coding-agent/skills/websearch/src/websearch/websearch.py
import aiohttp
import json
from typing import List
async def run(query: str) -> List[str]:
"""Perform a web search and return the top result URLs."""
async with aiohttp.ClientSession() as session:
async with session.get(
"https://api.duckduckgo.com/",
params={"q": query, "format": "json"},
) as resp:
data = await resp.json()
return [result["FirstURL"] for result in data.get("Results", [])][:5]
This skill performs HTTP requests without blocking, yet the kernel consumes it identically to a synchronous skill.
Internal Invocation Flow
The kernel's simplified execution path:
import asyncio
import tyro
import inspect
async def _run_skill(module_name: str, argv):
module = __import__(module_name)
skill_fn = getattr(module, "run")
result = tyro.cli(skill_fn, args=argv) # parses argv → function call
if inspect.isawaitable(result):
result = await result
return result
# Top-level entry point
asyncio.run(_run_skill("websearch", ["--query", "prime agent"]))
The tyro.cli integration provides automatic CLI generation from type hints, making skills self-documenting.
Key Source Files
prime-agent-runtime/src/rlm/skill.py— Central runtime that implementscli()andrun_cli()for skill execution with automatic async detectionpackages/coding-agent/test/fixtures/skills/python-skill/src/python_skill/__init__.py— Minimal async skill fixture used in testingpackages/coding-agent/skills/websearch/src/websearch/websearch.py— Production skill demonstrating async HTTP requests
Summary
- Skills export a single
runcallable—sync or async—at module level - The kernel uses
inspect.isawaitableto detect coroutines andawaitto resolve them asyncio.runwraps execution so skill authors need no event-loop boilerplatetyro.clibridges CLI arguments to function parameters automatically- The pattern in
prime-agent-runtime/src/rlm/skill.pymakes async skills indistinguishable from sync ones at the call site
Frequently Asked Questions
Does the kernel require any special decorators or markers for async skills?
No. The kernel detects async automatically via inspect.isawaitable. You simply write async def run instead of def run—no decorators, registration, or configuration changes are needed.
What happens if a sync skill is called through the async path?
Synchronous skills work unchanged. When inspect.isawaitable(result) returns False, the kernel skips awaiting and returns the value directly. The same asyncio.run wrapper handles both cases.
Can skills use other async libraries like asyncio.gather or trio?
Standard asyncio works natively since the kernel runs asyncio.run. Third-party libraries like trio would need compatibility layers; the runtime as implemented in skill.py does not include alternative event-loop support.
How does argument parsing work for async skills?
Arguments parse identically regardless of sync/async. The kernel calls tyro.cli(skill_fn, args=argv) before awaiting, so type hints and default values on run generate the CLI interface automatically.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →