# How Python-Backed Skills Expose Async Callables to the Prime Agent Kernel

> Learn how Python-backed skills expose async callables to the Prime Agent kernel. Discover the export of async def run for automatic detection and awaiting.

- Repository: [Prime Intellect/prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
- Tags: how-to-guide
- Published: 2026-09-06

---

**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.

```python

# 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`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/prime-agent-runtime/src/rlm/skill.py) executes skills through a seven-step pipeline:

1. **Resolve module name** — Strip the extension from the console-script name and normalize dashes to underscores (`cli()`)
2. **Import the module** — `__import__(prog)` loads the skill; import errors become `RuntimeError` with context (`cli()`)
3. **Retrieve `run`** — `getattr(module, "run", None)` fetches the callable; non-callable values raise `RuntimeError` (`cli()`)
4. **Parse CLI arguments** — `tyro.cli(skill_fn, args=argv)` maps command-line arguments to function parameters (`run_cli()`)
5. **Detect awaitable** — `inspect.isawaitable(result)` checks if the call returned a coroutine (`run_cli()`)
6. **Await if necessary** — `await result` runs the coroutine to completion (`run_cli()`)
7. **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:

```python

# 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:

```python
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`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/prime-agent-runtime/src/rlm/skill.py)** — Central runtime that implements `cli()` and `run_cli()` for skill execution with automatic async detection
- **[`packages/coding-agent/test/fixtures/skills/python-skill/src/python_skill/__init__.py`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/test/fixtures/skills/python-skill/src/python_skill/__init__.py)** — Minimal async skill fixture used in testing
- **[`packages/coding-agent/skills/websearch/src/websearch/websearch.py`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/skills/websearch/src/websearch/websearch.py)** — Production skill demonstrating async HTTP requests

## Summary

- Skills export a single **`run`** callable—sync or async—at module level
- The kernel uses **`inspect.isawaitable`** to detect coroutines and **`await`** to resolve them
- **`asyncio.run`** wraps execution so skill authors need no event-loop boilerplate
- **`tyro.cli`** bridges CLI arguments to function parameters automatically
- The pattern in [`prime-agent-runtime/src/rlm/skill.py`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/prime-agent-runtime/src/rlm/skill.py) makes 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`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/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.